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?


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


@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;

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


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.