Menu

C++ Sıralama: std::sort, Özel Karşılaştırıcılar ve Kararlı Sıralama

C++'ta vektörleri ve dizileri std::sort ile sıralayın: varsayılan sıra, özel karşılaştırıcılar, struct'ları bir alana göre sıralama ve çökmelere yol açan katı zayıf sıralama tuzağı.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Sıralama Sadece Başka Bir Algoritma

Önceki sayfada, standart kütüphanenin yineleyiciler aracılığıyla herhangi bir aralık üzerinde çalışan hazır algoritmalar sunduğunu gördünüz. Sıralama, en sık başvuracağınız algoritmadır ve kendi sayfasına sahip olmasının nedeni birkaç keskin köşesinin olmasıdır: özel sıralamalar, kararlılık ve bozduğunuzda size yanlış bir cevap yerine tanımsız davranış veren bir kural.

İş atı, <algorithm> içindeki std::sort'tur. Ona bir aralığın başını ve sonunu verirsiniz, o da elemanları yerinde artan sırada yeniden düzenler:

Hiçbir kopya oluşturulmaz; vektörün kendisi yeniden düzenlenir. Arka planda std::sort genellikle bir introsort'tur (heapsort'a geri düşen quicksort) ve ortalamada size O(n log n) verir. Bu, neredeyse her zaman kendi sıralamanızı yazmaktan daha hızlı ve çok daha az hataya açıktır.

Düz C dizilerinde de çalışır; aralığı yalnızca işaretçilerle tanımlarsınız:

Karşılaştırıcı ile Özel Sıra

Varsayılan olarak std::sort, elemanları operator< ile sıralar. Farklı sıralamak için üçüncü bir argüman geçin: iki eleman alan ve birincisinin ikinciden önce gelmesi gerekiyorsa true döndüren bir karşılaştırıcı.

Lambda doğal bir uyum sağlar. Azalan sıra sadece a > b'dir:

Yerleşik tipler üzerinde düz azalan sıra gibi yaygın bir durum için kütüphane, <functional> içinden hazır bir karşılaştırıcı olan greater<T>()'yi bile sunar:

#include <functional>
sort(nums.begin(), nums.end(), greater<int>());  // a > b ile aynı

Karşılaştırıcı, değerin kendisi dışında bir şeye göre sıralama yapmanın da yoludur; örneğin, dizeleri alfabetik yerine uzunluğa göre sıralamak:

Birkaç bayttan büyük her şey için (string gibi) karşılaştırıcı parametrelerini const& ile alın; her karşılaştırmada her elemanı kopyalamak tamamen israftır.

Struct'ları Bir Alana Göre Sıralama

Gerçek programlarda genellikle struct koleksiyonlarını alanlarından birine göre sıralarsınız. Karşılaştırıcı sadece önemsediğiniz alana erişir. Burada insanları yaşa göre, en gençten başlayarak sıralıyoruz:

Linus ile Dennis'in ikisinin de 25 yaşında olduğuna dikkat edin. Burada özgün göreli sıralarında çıktılar, ama std::sort bunu garanti etmez. Eşit elemanların göreli sırası önemliyse, bunu koruyan (küçük bir performans maliyetiyle) std::stable_sort kullanın:

Eşitlikleri kasıtlı olarak çözmek için (örneğin yaşa, sonra ada göre alfabetik sıralamak) ikincil anahtarı yalnızca birincil anahtarlar eşit olduğunda karşılaştırın. std::tie bunu temiz hale getirir:

Katı Zayıf Sıralama Tuzağı

Bu, C++ sıralamasındaki en tehlikeli tek hatadır, çünkü size yanlış bir cevap vermez; çoğu zaman bir çökme veya sınır dışı okuma anlamına gelen tanımsız davranış verir.

std::sort, karşılaştırıcınızın bir katı zayıf sıralama tanımlamasını gerektirir. Pratik kural: herhangi bir x elemanı için comp(x, x) false olmalıdır. Başka bir deyişle, bir eleman asla kendisinden "önce" değildir. < ve > size tam olarak bunu verir, <= ve >= ise tam olarak bunu bozar:

// HATA: a == b olduğunda true döndürür ve katı zayıf sıralamayı ihlal eder.
sort(v.begin(), v.end(), [](int a, int b) {
    return a <= b;   // tanımsız davranış - bazı girdilerde çökebilir
});

<= ile karşılaştırıcı, 5'in başka bir 5'ten önce geldiğini iddia eder ki bu çelişkilidir. std::sort o zaman bir işaretçiyi aralığın sonunun ötesine yürütebilir. Küçük girdiler bazen çalışıyor gibi görünür ve bu da bu hatayı korkutucu yapar: testlerinizden geçip üretimde çökebilir. Çözüm sadece <:

İkinci klasik tuzak: sıralama, aralığın içine işaret eden her şeyi geçersiz kılar. Sıralamadan önce kaydettiğiniz yineleyiciler, işaretçiler ve indeksler, elemanlar yer değiştirdiği için sonrasında artık aynı mantıksal elemanı göstermez. İhtiyaç duyduğunuz herhangi bir konumu sıralamadan sonra yeniden türetin, asla önce değil.

Bir Aralığın Bir Kısmını Sıralama

Bazen her şeyin sıralanmasına ihtiyacınız yoktur; sadece ilk birkaçını istersiniz. İlk üçü okumak için tüm vektörü sıralamak israftır. std::partial_sort, yalnızca istediğiniz elemanları düzenler ve geri kalanını belirsiz sırada bırakır; bu daha ucuzdur:

Ve yalnızca belirli bir konumda bulunacak tek elemana ihtiyacınız varsa (örneğin medyan), std::nth_element daha da az iş yapar: doğru elemanı o indekse yerleştirir, daha küçük olan her şeyi önüne ve daha büyük olan her şeyi arkasına koyar; tümü ortalamada O(n)'de.

"Tamamen sıralı", problemin gerçekten ihtiyaç duyduğundan fazlaysa bunlara başvurun; büyük verilerde gerçek zaman kazandırırlar.

Sıradaki: Şablonlar

Aynı std::sort'un int, string ve kendi Person struct'ınızı ele aldığını ve greater<int>()'nin aynı kolaylıkla greater<string>() olabileceğini fark ettiniz mi? Bu genellik sihir değildir; tek bir kod parçasının, çağıranın taktığı herhangi bir tip için çalışmasını sağlayan mekanizma olan şablonlardır. Sonraki sayfada kendi şablonlu fonksiyonlarınızı ve sınıflarınızı nasıl yazacağınızı göreceğiz; böylece kodunuz, kullandığınız algoritmalar kadar tipten bağımsız olabilir.

Sıkça Sorulan Sorular

C++'ta bir vektör nasıl sıralanır?

<algorithm> başlığını ekleyin ve sort(v.begin(), v.end()) çağrısını yapın. Bu, elemanları operator< kullanarak yerinde artan sırada sıralar. Bir diziyi sıralamak için arr ve arr + n (veya begin(arr) / end(arr)) geçin.

C++'ta azalan sırada nasıl sıralama yapılır?

a > b döndüren bir karşılaştırıcı geçin: sort(v.begin(), v.end(), [](int a, int b){ return a > b; });. <functional> içinden gelen yerleşik sort(v.begin(), v.end(), greater<int>()); ifadesini de kullanabilirsiniz.

C++'ta karşılaştırıcım neden std::sort'u çökertiyor?

Karşılaştırıcınız bir katı zayıf sıralama olmalıdır: iki argüman eşit olduğunda false döndürmesi gerekir. <= veya >= kullanmak (eşit elemanlar için true döndürürler) bu kuralı bozar ve tanımsız davranıştır; std::sort sınır dışını okuyup çökebilir. Her zaman < veya > ile karşılaştırın.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA