Mengurutkan vektor dalam urutan menurun dalam dua rentang

14

Katakanlah saya memiliki vektor bilangan bulat:

std::vector<int> indices;
for (int i=0; i<15; i++) indices.push_back(i);

Lalu saya mengurutkannya dalam urutan menurun:

sort(indices.begin(), indices.end(), [](int first, int second) -> bool{return indices[first] > indices[second];})
for (int i=0; i<15; i++) printf("%i\n", indices[i]);

Ini menghasilkan yang berikut:

14
13
12
11
10
9
8
7
6
5
4
3
2
1
0

Sekarang saya ingin memiliki angka 3, 4, 5, dan 6 untuk dipindahkan ke akhir, dan menjaga urutan turun untuk mereka (lebih disukai tanpa harus menggunakan sortuntuk yang kedua kalinya). Yaitu, inilah yang saya inginkan:

14
13
12
11
10
9
8
7
2
1
0
6
5
4
3

Bagaimana saya harus memodifikasi fungsi perbandingan std::sortuntuk mencapai itu?

Yury
sumber
4
return indices[first] > indices[second]Bukankah maksud Anda return first < second;?
acraig5075
2
Untuk jenis turun sederhana, std::greaterdari <functional>dapat digunakan sebagai pengganti lambda Anda. Mengenai pertanyaan Anda, menulis komparator yang lebih verbose yang memastikan nilai Anda membandingkan dengan cara yang Anda inginkan mungkin merupakan cara termudah untuk melakukannya.
sweenish
4
@ acraig5075, dalam urutan menurun seharusnya return first > second.
ks1322
1
@ acraig5075 Saya merasa seperti kehilangan sesuatu, atau apakah orang tidak tahu perbedaan antara naik dan turun ?
sweenish
3
Mungkin Anda mencari std :: rotate ?
super

Jawaban:

8

Fungsi perbandingan Anda salah karena nilai yang Anda peroleh firstdan secondmerupakan elemen dari std::vector. Oleh karena itu, tidak perlu menggunakannya sebagai indeks. Jadi, Anda perlu berubah

return indices[first] > indices[second];

untuk

return first > second;

Sekarang, mengenai masalah yang Anda coba selesaikan ...

Anda dapat meninggalkan 3, 4, 5 dan 6 dari perbandingan dengan elemen lain dan masih membandingkan satu sama lain:

std::sort(
    indices.begin(), indices.end(),
    [](int first, int second) -> bool {
        bool first_special = first >= 3 && first <= 6;
        bool second_special = second >= 3 && second <= 6;
        if (first_special != second_special)
            return second_special;
        else
            return first > second;
    }
);

Demo

Alat pemecah buah keras
sumber
@NutCracker Ya, saya setuju lebih baik untuk memiliki kriteria teratas terlebih dahulu.
Heap Overflow
5

Fungsi dari perpustakaan algoritma standar seperti iota, sort, find, rotatedan copyakan membuat hidup Anda lebih mudah. Contoh Anda sampai pada:

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>
#include <iterator>


int main()
{
  std::vector<int> indices(15);
  std::iota(indices.begin(), indices.end(), 0);
  std::sort(indices.begin(), indices.end(), std::greater<>());

  auto a = std::find(indices.begin(), indices.end(), 6);
  auto b = std::find(indices.begin(), indices.end(), 3);
  std::rotate(a, b + 1, indices.end());

  std::copy(indices.begin(), indices.end(), std::ostream_iterator<int>(std::cout, "\n"));
  return 0;
}

Keluaran:

14
13
12
11
10
9
8
7
2
1
0
6
5
4
3


@TedLyngmo dalam komentar membuat poin yang baik bahwa itu bisa / harus ditingkatkan dengan:

auto a = std::lower_bound(indices.begin(), indices.end(), 6, std::greater<int>{});
auto b = a + 4;
acraig5075
sumber
auto b = a + 4;salah (jika Anda ingin tetap konsisten dengan cuplikan sebelumnya). Seharusnya auto b = a + 3;karena std::rotateAnda gunakanb + 1
Biagio Festa
3

Solusi 1

Pendekatan langsung dengan pembanding non-linear .

inline constexpr bool SpecialNumber(const int n) noexcept {
  return n < 7 && 2 < n;
}

void StrangeSortSol1(std::vector<int>* v) {
  std::sort(v->begin(), v->end(), [](const int a, const int b) noexcept {
    const bool aSpecial = SpecialNumber(a);
    const bool bSpecial = SpecialNumber(b);

    if (aSpecial && bSpecial) return b < a;
    if (aSpecial) return false;
    if (bSpecial) return true;
    return b < a;
  });
}

Solusi 2

Menggunakan std::algorithms (partisi)!

inline constexpr bool SpecialNumber(const int n) noexcept {
  return n < 7 && 2 < n;
}

void StrangeSortSol2(std::vector<int>* v) {
  auto pivot = std::partition(v->begin(), v->end(), std::not_fn(SpecialNumber));
  std::sort(v->begin(), pivot, std::greater{});
  std::sort(pivot, v->end(), std::greater{});
}

Pertimbangan Kinerja

Ini mungkin terlihat seperti solusi kedua lebih lambat karena overhead partisi. Mungkin tidak, karena prediksi cache dan miss-branch di prosesor modern.

Tolok ukur

Biagio Festa
sumber
Kompiler mana pun yang baik harus bertransformasi n <= 6 && 3 <= n menjadi apa pun yang bekerja paling baik untuk CPU target sehingga Anda tidak mendapatkan apa-apa dengan memperkenalkan angka 2 dan 7 tetapi berpotensi kebingungan - dan mengapa mengambil pointer ke vektor alih-alih referensi?
Ted Lyngmo
Jangan gunakan `const int number` sebagai fungsi argumen
Antoine Morrier
1
@AntoineMorrier Kenapa?
Heap Overflow
@HeapOverflow Karena itu sama tanpa menggunakan const :).
Antoine Morrier
@AntoineMorrier Saya tidak berpikir itu sama. Tidakkah constmemberi tahu pembaca bahwa fungsinya tidak mengubah nilainya? Dalam kasus satu liner khusus ini mungkin jelas, tetapi secara umum tidak.
Heap Overflow