En büyük eleman sıralaması c. Seçime göre sıralama

Bir dizideki değerlerin artan veya azalan şekilde sıralanması (sıralanması) için birçok yöntem geliştirilmiştir [Wirth, Knuth. t 3] Kesinlik açısından X dizisinin ilk n, n=6 elemanlarını dikkate alarak bunlardan üçünü ele alalım.

Sonraki her i'inci adımda, i=2, 3,…,n-1, dizinin (i+1)'inci hücresindeki değer, önceki sayıyla konum değiştirerek hücre indeksini azaltacak şekilde hareket ettirilir. Önceki hücrenin daha küçük bir sayı içerdiği ortaya çıkıncaya kadar hücre.

Yukarıdakilerden, yöntemi uygularken şunu takip eder: doğrudan bağlantı Dış döngünün n-1 kez yürütülmesi gerekir ve karşılaştırmaların ve sayı permütasyonlarının gerçekleştirilmesi gereken gövdedeki iç döngünün mümkün olan maksimum yürütme sayısı 1'den n-1'e artacaktır. Bununla birlikte, iç döngü, koşul oluştuğunda sona erecek veya hiç yürütülmeyecek şekilde düzenlenmelidir: önceki dizi hücresindeki değer, geçerli olandan daha küçüktür.

Örneğimizde:

i=2 olduğunda, X3 hücresindeki 15 sayısı sırasıyla X2 hücresindeki 34 sayısıyla ve ardından X1 hücresindeki 21 sayısıyla yer değiştirir,

i=4 olduğunda X5 hücresindeki 25 sayısı X3 hücresindeki 34 sayısıyla yer değiştirecektir,

Aşağıda, doğrudan dahil etme yöntemini (sıralamayı korurken dahil etme) kullanarak X dizisinin ilk n elemanını artan sırada sıralamak için bir programın bir parçası bulunmaktadır.

    i:=1'den n-1'e kadar

  1. iken (X 0) yap

  2. R:=X[j];

    X[j]:=X;

    X:=R;

Bir dizideki sayıları azalan sırada sıralamak için, her adımda dizinin komşu hücrelerindeki sayıların izin verilmesi koşulunu tersine değiştirmek yeterlidir, yani komşu hücrelerin değerlerinin değişimi öncekinin mevcut olandan daha az olduğu durum.

Doğrudan değişim yöntemi (kabarcık yöntemi).

Bu yöntem, önceki yöntem gibi, dizinin komşu hücrelerinin değerlerinin değiş tokuşuna dayanır, ancak sıralı analizin ilk adımından itibaren, dizinin bir ucundan diğerine geçerken tüm çiftler Dizinin komşu hücreleri dahil olur.

İlk adımda j = n, n-1, ..., 2 için sırasıyla dizideki komşu hücrelerin değerleri karşılaştırılır ve X j koşulu varsa<Х j-1 выполняется их перестановка, в результате чего наименьшее число оказывается в ячейке Х 1 .

Örneğimizde ilk adımı tamamladıktan sonra dizideki veriler şu şekilde konumlandırılacaktır:

Sonraki her adımda kontrol edilecek hücre çifti sayısı 1 azalacaktır. Genel olarak herhangi bir i, i=1, 2, 3, ..., n-1 adımında j'den başlayarak j için işlem yapılacaktır. n'den i+1'e, özellikle i= n-1 için - n'inci ve (n-1)'inci hücreler için yalnızca bir kez.

Yukarıdakilerden, doğrudan değişim yöntemini uygularken, dış döngünün n-1 kez yürütülmesi gerektiği ve gövdede sayıların karşılaştırmaları ve yeniden düzenlenmesinin yapılması gereken iç döngünün yürütme sayısının azalacağı sonucu çıkmaktadır. n-1'den 1'e.

“Kabarcık yöntemi” teriminin kökeni şu şekilde açıklanmaktadır: Dizi hücrelerinin yukarıdan aşağıya doğru artan indekslerle dikey dizilişini hayal ederseniz, bunlardan en küçük olanı sudaki bir kabarcık gibi yükselecektir.

Örneğimizde

i=3 olduğunda permütasyonlar dizinin aşağıdaki durumuna yol açacaktır

Kabarcık yöntemini kullanırken, dizideki sayı çiftlerinin analizinin artan veya azalan indekslere doğru hareket etmesi önemli değildir ve sıralama türü (artan veya azalan) yalnızca sayıların permütasyon koşuluyla belirlenir ( küçük olan büyük olanın arkasına yerleştirilmelidir veya tam tersi).

Değiştirilmiş doğrudan değişim yöntemi (modifiye kabarcık yöntemi).

Yukarıdaki sayısal örnekten görülebileceği gibi, dördüncü adımdan sonra dizinin sıralandığı ortaya çıktı, yani dizinin olduğu bilindiğinde dış döngüyü n-1 kez değil, daha az çalıştırmak mümkün oluyor. zaten sipariş edildi. Bu kontrol aşağıdakilere dayanmaktadır: eğer iç döngünün yürütülmesi sırasında herhangi bir permütasyon yoksa, dizi zaten sıralanmıştır ve dış döngüden çıkabilirsiniz. Boolean tipi bir değişken, bir permütasyonun gerçekleştirilip gerçekleştirilmediğini gösteren bir işaret olarak kullanılır: iç döngüye girmeden önce ona bir değer verilir, örneğin False ve permütasyon gerçekleştirildiğinde başka bir değer verilir, örneğin, Doğru.

Açıkçası, sıralama işlemini hızlandırmada değiştirilmiş kabarcık yönteminin, değiştirilmemiş yönteme kıyasla kullanılmasının etkisi, orijinal sayı dizisinin istenen yönde sıralanmaya yakın olması durumunda gözlemlenecektir. En uç durumda, dizi zaten istenen şekilde sıralandığında, dış döngünün gövdesi yalnızca bir kez yürütülecektir.

Seçim türlerinin ardındaki fikir nedir?

  1. Sıralanmamış bir alt dizide yerel bir maksimum (minimum) aranır.
  2. Bulunan maksimum (minimum), alt dizideki son (ilk) öğeyle yer değiştirir.
  3. Dizide sıralanmamış alt diziler kaldıysa 1. noktaya bakın.
Küçük bir lirik ara söz. Başlangıçta makale dizimde sınıfları kesin bir sıraya göre sıralamayla ilgili materyalleri sırayla sunmayı planladım. Daha sonra diğer ekleme algoritmaları üzerine makaleler planlandı: solitaire sort, Young table sort, eversion sort, vb.

Ancak artık doğrusal olmama eğilimi var, bu nedenle ekleme türleriyle ilgili tüm yayınları yazmadan, bugün seçme türleriyle ilgili paralel bir konu başlatacağım. Daha sonra aynısını diğer algoritmik sınıflar için de yapacağım: birleştirme sıralamaları, dağıtım sıralamaları vb. Genel olarak bu, şu veya bu konuda yayınlar yazmanıza olanak tanır. Böyle bir tematik değişimle daha eğlenceli olacak.

Seçim sıralaması:: Seçim sıralaması


Basit ve iddiasız - maksimum öğeyi bulmak için diziyi inceliyoruz. Bulunan maksimum son elemanla değiştirilir. Dizinin sıralanmamış kısmı bir öğe azaldı (bulunan maksimum değeri taşıdığımız son öğeyi içermiyor). Aynı eylemleri bu sıralanmamış kısma da uyguluyoruz - maksimumu buluyoruz ve onu dizinin sıralanmamış kısmında son sıraya koyuyoruz. Ve dizinin sıralanmamış kısmı tek elemana indirgenene kadar bu şekilde devam ediyoruz.

Def seçimi(veri): for i, e in numaralandırma(veri): mn = min(aralık(i, len(veri))), anahtar=data.__getitem__) veri[i], veri = veri, e dönüş verisi

Basit seçim sıralaması kaba kuvvetle çift aramadır. İyileştirilebilir mi? Birkaç değişikliğe bakalım.

Çift seçimli sıralama: Çift seçimli sıralama


Benzer bir fikir, kabarcık sıralamanın bir çeşidi olan 'da da kullanılır. Dizinin sıralanmamış kısmında dolaşırken maksimuma ek olarak minimumu da buluruz. Minimumu ilk sıraya, maksimumu son sıraya koyuyoruz. Böylece, sıralanmamış kısım her yinelemede iki öğe azaltılır.

İlk bakışta, bu, algoritmayı 2 kat hızlandırıyor gibi görünüyor - her geçişten sonra, sıralanmamış alt dizi bir tarafta değil, her iki tarafta aynı anda azaltılıyor. Ancak aynı zamanda karşılaştırma sayısı iki katına çıkarken takas sayısı değişmedi. Çift seçim, algoritmanın hızını yalnızca biraz artırır ve hatta bazı dillerde bazı nedenlerden dolayı daha yavaş çalışır.

Seçimli sıralama ile eklemeli sıralama arasındaki fark

Seçim sıralaması ve sıralaması aslında aynı şey, yani genel bir algoritma sınıfı gibi görünebilir. Peki, veya ekleme sıralaması - bir tür seçim sıralaması. Veya seçim sıralaması - ekleme sıralamasının özel bir durumu. Her iki durumda da, dizinin sıralanmamış kısmından öğeleri sırayla çıkarırız ve bunları sıralanmış alana yönlendiririz.

Temel fark: eklemeli sıralamada dizinin sıralanmamış kısmından çıkarıyoruz herhangiöğesini seçin ve sıralanan kısımdaki yerine yerleştirin. Seçim sıralamasında bilinçli olarak aradığımız maksimum dizinin sıralanmış kısmını tamamladığımız öğe (veya minimum). Eklemelerde bir sonraki öğeyi nereye yerleştireceğimizi arıyoruz ve seçimde onu nereye koyacağımızı önceden biliyoruz ama aynı zamanda bu yere karşılık gelen bir öğe bulmamız gerekiyor.

Bu, her iki algoritma sınıfını da özünde ve kullanılan yöntemlerde birbirinden tamamen farklı kılar.

Bingo sıralaması:: Bingo sıralaması

Seçimli sıralamanın ilginç bir özelliği, hızın sıralanan verinin doğasına bağlı olmamasıdır.

Örneğin, dizi neredeyse sıralanmışsa, bilindiği gibi ekleme sıralaması onu çok daha hızlı işleyecektir (hızlı sıralamadan bile daha hızlı). Eklemeli sıralama için ters sıralı bir dizi dejenere bir durumdur; mümkün olduğu kadar uzun süre sıralar.

Ve seçim sıralaması için dizinin kısmi veya ters sıralaması önemli değildir - onu yaklaşık olarak normal rastgele ile aynı hızda işleyecektir. Ayrıca, klasik seçim sıralaması için dizinin benzersiz veya yinelenen öğelerden oluşması önemli değildir; bunun hız üzerinde neredeyse hiçbir etkisi yoktur.

Ancak prensip olarak, bazı veri kümeleri için daha hızlı çalışacak şekilde akıllı davranabilir ve algoritmayı değiştirebilirsiniz. Örneğin bingo sıralaması, dizinin yinelenen öğelerden oluşup oluşmadığını dikkate alır.

Buradaki püf noktası, sırasız kısımda sadece maksimum elemanın hatırlanması değil, aynı zamanda bir sonraki yineleme için maksimumun da belirlenmesidir. Bu, maksimumların tekrarlanması durumunda, bunları her seferinde tekrar aramanıza değil, dizide bu maksimumla tekrar karşılaşıldığında bunları hemen yerlerine koymanıza olanak tanır.

Algoritmik karmaşıklık aynı kalır. Ancak dizi yinelenen sayılardan oluşuyorsa, bingo sıralaması normal seçim sıralamasından onlarca kat daha hızlı olacaktır.

# Bingo sort def bingo(veri): # İlk geçiş. max = len(veri) - 1 nextValue = aralıktaki i için veri(max - 1, -1, -1): if data[i] > nextValue: nextValue = data[i] while max ve data == nextValue: max -= 1 # Sonraki geçişler. while max: değer = nextValue nextValue = aralıktaki i için veri(max - 1, -1, -1): if data[i] == değer: data[i], data = data, data[i] max -= 1 elif data[i] > nextValue: nextValue = data[i] while max ve data == nextValue: max -= 1 dönüş verisi

Döngü sıralaması:: Döngü sıralaması

Döngüsel sıralama ilginçtir (ve pratik açıdan değerlidir), çünkü bir dizinin öğeleri arasındaki değişiklikler ancak ve ancak öğenin son yerine yerleştirilmesi durumunda gerçekleşir. Bir diziyi yeniden yazmak çok pahalıysa ve fiziksel belleğe dikkat etmek için dizi öğelerindeki değişiklik sayısını en aza indirmeniz gerekiyorsa bu yararlı olabilir.

Bu şekilde çalışıyor. Dizinin üzerinden geçiyoruz, buradaki sonraki hücreye X diyelim dış döngü. Ve bu hücreden bir sonraki öğeyi dizide hangi yere yerleştirmemiz gerektiğine bakıyoruz. Yapıştırmanız gereken yerde başka bir öğe daha var, onu panoya gönderiyoruz. Tampondaki bu eleman için ayrıca dizideki yerini ararız (ve onu bu yere yerleştiririz ve bu yere gelen elemanı arabelleğe göndeririz). Tampondaki yeni sayı için de aynı işlemleri gerçekleştiriyoruz. Bu süreç ne kadar devam etmeli? Panodaki bir sonraki öğenin X hücresine (algoritmanın ana döngüsündeki dizideki geçerli konum) yerleştirilmesi gereken öğe olduğu ortaya çıkana kadar. Er ya da geç bu an gerçekleşecek ve dış döngüde bir sonraki hücreye geçip aynı işlemi onun için tekrarlayabilirsiniz.

Diğer seçim sıralamalarında, onları son/birinci sıraya koymak için maksimum/minimum değerleri ararız. Döngü sıralamasında, alt dizide ilk sırada yer alan minimumun, diğer birçok öğenin dizinin ortasında bir yerde doğru yerlerine yerleştirilmesi sürecinde olduğu gibi kendisi olduğu ortaya çıktı.

Ve burada algoritmik karmaşıklık da O( n 2). Uygulamada döngüsel sıralama, normal seçimli sıralamadan birkaç kat daha yavaştır çünkü dizide daha fazla çalışmanız ve daha sık karşılaştırma yapmanız gerekir. Bu, mümkün olan minimum yeniden yazma sayısının fiyatıdır.

# Döngüsel sıralama def döngü(veri): # DöngüBaşlangıcı için döngüsel döngüleri aramak için dizide dolaşıyoruz aralık(0, len(veri) - 1): değer = veri # pos = döngüsüBaşlangıç ​​öğesinin nereye ekleneceğine bakıyoruz for i in range(cycleStart + 1, len(data)): if data[i]< value: pos += 1 # Если элемент уже стоит на месте, то сразу # переходим к следующей итерации цикла if pos == cycleStart: continue # В противном случае, помещаем элемент на своё # место или сразу после всех его дубликатов while value == data: pos += 1 data, value = value, data # Циклический круговорот продолжается до тех пор, # пока на текущей позиции не окажется её элемент while pos != cycleStart: # Ищем, куда переместить элемент pos = cycleStart for i in range(cycleStart + 1, len(data)): if data[i] < value: pos += 1 # Помещаем элемент на своё место # или сразу после его дубликатов while value == data: pos += 1 data, value = value, data return data

Gözleme sıralama

Yaşamın her düzeyinde - baştan sona - hakim olan bir algoritma.

En basit versiyonda, dizinin sıralanmamış kısmında maksimum öğeyi ararız. Maksimum bulununca iki keskin dönüş yapıyoruz. İlk olarak, element zincirini maksimumun karşı uçta olacağı şekilde çeviririz. Daha sonra sıralanmamış alt dizinin tamamını ters çevirerek maksimumun yerine oturmasını sağlarız.

Bu tür kordonlar, genel olarak konuşursak, O('nin algoritmik karmaşıklığına yol açar. n 3). Bunlar, tek bir hamlede yuvarlanan eğitimli siliatlardır (bu nedenle performansları O() karmaşıklığına sahiptir. n 2)) ve programlama sırasında bir dizinin bir kısmının ters çevrilmesi ek bir döngüdür.

Gözleme sıralaması matematiksel açıdan çok ilginçtir (en iyi beyinler, sıralama için yeterli olan minimum dönüş sayısını tahmin etmeyi düşünmüştür); problemin daha karmaşık formülasyonları vardır (yanmış taraf olarak adlandırılır). Krep konusu son derece ilginç, belki bu konularda daha detaylı bir monografi yazacağım.

# Pancake sort def pancake(data): if len(data) > 1: for size in range(len(data), 1, -1): # Maksimumun sıralanmamış kısımdaki konumu maxindex = max(range(size) , key = data.__getitem__) if maxindex + 1 != size: # Eğer maksimum kelime değilse, o zaman bunu tersine çevirmeniz gerekir if maxindex != 0: # Maksimum değer soldaki veride olacak şekilde # çevirin[: maxindex+1] = reversed(veri[:maxindex +1]) # Dizinin sıralanmamış bölümünü tersine çevirir, # maksimum yerine düşer data[:size] = ters(veri[:size]) veriyi döndürür

Seçimli sıralama yalnızca dizinin sıralanmamış kısmındaki minimum/maksimum öğenin aranması kadar etkilidir. Günümüzde analiz edilen tüm algoritmalarda arama, çift arama şeklinde gerçekleştirilmektedir. Ve çift arama, nasıl bakarsanız bakın, her zaman O('dan daha iyi olmayan bir algoritmik karmaşıklığa sahip olacaktır. n 2). Bu, tüm seçilim türlerinin kare karmaşıklığına mahkum olduğu anlamına mı geliyor? Arama süreci temelde farklı organize edilmişse hiç de değil. Örneğin, bir veri kümesini bir yığın olarak düşünün ve yığının içinde arama yapın. Ancak yığınlar konusu bir makale bile değil, tam bir destan; yığınlardan mutlaka bahsedeceğiz, ama başka zaman.

Yeni Başlayanlar İçin Algoritmalar ve Veri Yapıları: Sıralama

Nikita Priyatselyuk

Bu bölümde bir dizideki verileri sıralamak için kullanılan beş ana algoritmaya bakacağız. En basitiyle (kabarcık sıralamasıyla) başlayalım ve hızlı sıralamayla bitirelim. (hızlı sıralama).

Her algoritma için, nasıl çalıştığını açıklamanın yanı sıra, en kötü durum, en iyi durum ve ortalama durum hafızasını ve zaman karmaşıklığını da göstereceğiz.

Ayrıca bu serideki diğer materyallere de bakın:, ve.

Takas yöntemi

Kodu basitleştirmek ve okunabilirliği artırmak için dizideki değerleri indekse göre değiştirecek Swap yöntemini tanıtacağız.

Void Swap(T items, int left, int right) ( if (left != right) ( T temp = items; items = items; items = temp; ))

Kabarcık sıralaması

Kabarcık sıralaması en basit sıralama algoritmasıdır. Dizi boyunca birkaç kez döngü yapar ve her adımda sıralanmamış en büyük değeri dizinin sonuna taşır.

Örneğin, bir tam sayı dizimiz var:

Diziden ilk geçtiğimizde 3 ve 7 değerlerini karşılaştırıyoruz. 7, 3'ten büyük olduğu için olduğu gibi bırakıyoruz. Daha sonra 7 ile 4'ü karşılaştırırız. 4, 7'den küçüktür, bu yüzden yediyi dizinin sonuna yaklaştırarak onları değiştiririz. Şimdi şöyle görünüyor:

Bu işlem, yedili neredeyse dizinin sonuna ulaşana kadar tekrarlanır. Sonunda daha büyük olan 8 numaralı elementle karşılaştırılır, bu da herhangi bir değişimin meydana gelmediği anlamına gelir. Diziyi bir kez geçtikten sonra şöyle görünür:

En az bir değer değişimi yapıldığı için dizi üzerinden tekrar geçmemiz gerekiyor. Bu geçiş sonucunda 6 sayısını yerine taşıyoruz.

Ve yine en az bir değişim yapıldı, bu da diziyi tekrar gözden geçireceğimiz anlamına geliyor.

Bir sonraki geçişte herhangi bir değişim yapılmaz, bu da dizimizin sıralandığı ve algoritmanın işini bitirdiği anlamına gelir.

Public void Sort(T items) ( bool swapped; do ( swapped = false; for (int i = 1; i)< items.Length; i++) { if (items.CompareTo(items[i]) >0) ( Swap(items, i - 1, i); swapped = true; ) ) ) while (swapped != false); )

Ekleme sıralaması

Eklemeli sıralama, bir diziyi yineleyerek ve istenen değeri dizinin başına taşıyarak çalışır. Bir sonraki pozisyon işlendikten sonra, ondan önceki tüm pozisyonların sıralandığını ancak sonrasında sıralanmadığını biliyoruz.

Önemli bir nokta: ekleme sıralaması dizi elemanlarını sırayla işler. Algoritma öğeler arasında soldan sağa doğru yineleme yaptığından, geçerli dizinin solundaki her şeyin zaten sıralanmış olduğunu biliyoruz. Bu şekil, dizinin sıralanan kısmının her geçişte nasıl büyüdüğünü gösterir:

Dizinin sıralanan kısmı yavaş yavaş büyür ve sonunda dizi sıralanır.

Belirli bir örneğe bakalım. İşte kullanacağımız sıralanmamış dizimiz:

Algoritma indeks 0 ve değer 3'ten başlar. Bu ilk indeks olduğundan, ona kadar olan ve onu içeren dizi sıralanmış olarak kabul edilir.

Bu aşamada indeksleri 0..1 olan elementler sıralanır ancak indeksleri 2..n olan elementler hakkında hiçbir şey bilinmemektedir.

Daha sonra 4 değeri kontrol edilir. Yediden küçük olduğundan dizinin sıralanan kısmında doğru konuma taşımamız gerekir. Geriye şu soru kalıyor: nasıl tanımlanmalı? Bu FindInsertionIndex yöntemiyle yapılır. Kendisine iletilen değeri (4) eklenecek yer bulana kadar sıralanan kısımdaki her değerle karşılaştırır.

Böylece indeks 1'i bulduk (3 ile 7 değerleri arasında). Insert yöntemi, eklenen değeri diziden kaldırarak ve ekleme dizininden başlayarak tüm değerleri sağa kaydırarak bir ekleme gerçekleştirir. Artık dizi şöyle görünüyor:

Şimdi dizinin sıfır öğesinden başlayıp dizin 2'ye sahip öğeyle biten kısmı sıralanır. Bir sonraki geçiş indeks 3 ve değer 4'ten başlıyor. Algoritma çalıştıkça bu tür eklemeler yapmaya devam ediyoruz.

Daha fazla ekleme kalmadığında dizinin tamamen sıralandığı kabul edilir ve algoritma tamamlanır.

Public void Sort(T items) ( int sortedRangeEndIndex = 1; while (sortedRangeEndIndex< items.Length) { if (items.CompareTo(items) < 0) { int insertIndex = FindInsertionIndex(items, items); Insert(items, insertIndex, sortedRangeEndIndex); } sortedRangeEndIndex++; } } private int FindInsertionIndex(T items, T valueToInsert) { for (int index = 0; index < items.Length; index++) { if (items.CompareTo(valueToInsert) >0) ( dönüş dizini; )) throw new InvalidOperationException("Ekleme dizini bulunamadı"); ) özel void Insert(T itemArray, int indexInsertingAt, int indexInsertingFrom) ( // itemArray = 0 1 2 4 5 6 3 7 // insertingAt = 3 // insertingFrom = 6 // // Eylemler: // 1: Geçerli olanı kaydet temp'deki index // 2: indexInsertingAt'ı indexInsertingFrom ile değiştirin // 3: indexInsertingAt'ı +1 pozisyonundaki indexInsertingFrom ile değiştirin // Elemanları birer birer sola kaydırın // 4: Dizideki +1 pozisyonuna temp yazın. // Adım 1. T temp = itemArray; // Adım 2. itemArray = itemArray; // Adım 3. for (int current = indexInsertingFrom; current > indexInsertingAt; current--) ( itemArray = itemArray; ) // Adım 4. itemArray = temp;

Seçime göre sıralama

Seçimli sıralama, kabarcık sıralaması ve ekleme sıralaması arasında bir melezdir. Kabarcık sıralaması gibi, bu algoritma da dizide tekrar tekrar dolaşarak bir değeri doğru konuma taşır. Ancak kabarcık sıralamanın aksine, en büyük yerine en küçük sıralanmamış değeri seçer. Eklemeli sıralamada olduğu gibi dizinin sıralı kısmı başlangıçta, kabarcık sıralamasında ise sonunda yer alır.

Sıralanmamış dizimizde seçim sıralamasının nasıl çalıştığına bakalım.

İlk geçişte algoritma, dizideki en küçük değeri bulmak ve onu başlangıca taşımak için FindIndexOfSmallestFromIndex yöntemini kullanır.

Bu kadar küçük bir diziyle en küçük değerin 3 olduğunu ve zaten doğru konumda olduğunu hemen söyleyebiliriz. Bu aşamada dizideki ilk konumun (indeks 0) en küçük değer olduğunu biliyoruz, dolayısıyla dizinin başlangıcı zaten sıralanmıştır. Böylece ikinci bir geçişe başlıyoruz - bu sefer 1'den n - 1'e kadar olan endeksleri kullanarak.

İkinci geçişte en küçük değerin 4 olduğunu belirliyoruz. Bunu ikinci eleman olan yedi ile değiştiriyoruz ve ardından 4'ü doğru konumuna yerleştiriyoruz.

Artık dizinin sıralanmamış kısmı dizin 2'den başlar. Algoritmanın her geçişinde bir öğe artar. Herhangi bir geçişte tek bir değişim yapmamışsak bu, dizinin sıralandığı anlamına gelir.

İki geçişten sonra algoritma işini tamamlar:

Public void Sort(T items) ( int sortedRangeEnd = 0; while (sortedRangeEnd< items.Length) { int nextIndex = FindIndexOfSmallestFromIndex(items, sortedRangeEnd); Swap(items, sortedRangeEnd, nextIndex); sortedRangeEnd++; } } private int FindIndexOfSmallestFromIndex(T items, int sortedRangeEnd) { T currentSmallest = items; int currentSmallestIndex = sortedRangeEnd; for (int i = sortedRangeEnd + 1; i < items.Length; i++) { if (currentSmallest.CompareTo(items[i]) >0) ( currentSmallest = öğeler[i]; currentSmallestIndex = i; )) return currentSmallestIndex; )

Sıralamayı birleştir

Böl ve yönet

Şu ana kadar doğrusal algoritmalara baktık. Çok az ekstra bellek kullanırlar ancak ikinci dereceden karmaşıklığa sahiptirler. Örnek olarak birleştirme sıralamasını kullanarak böl ve yönet algoritmasına bakacağız. (böl ve fethet).

Bu tür algoritma, büyük bir sorunu daha küçük, çözülmesi daha kolay sorunlara bölerek çalışır. Bunları her gün kullanıyoruz. Örneğin, içinde arama Telefon rehberi- böyle bir algoritmanın bir örneği.

Petrov adında birini bulmak istiyorsanız A harfinden başlayıp sayfa sayfa çevirmezsiniz. Büyük olasılıkla kitabı ortada bir yerde açacaksınız. Eğer T'ye basarsanız, birkaç sayfa geriye gidersiniz, belki de O'ya göre çok fazla sayfa. Sonra ileriye gidersiniz. Böylece giderek daha az sayfa arasında ileri geri dolaşarak sonunda ihtiyacınız olanı bulacaksınız.

Bu algoritmalar ne kadar etkili?

Diyelim ki telefon rehberinde 1000 sayfa var. Yarısına kadar açarsanız aradığınız kişinin yer almadığı 500 sayfayı çöpe atmış olursunuz. Eğer doğru sayfaya ulaşamazsanız, sağ veya sol tarafı seçersiniz ve yine mevcut seçeneklerin yarısıyla karşı karşıya kalırsınız. Artık bakmanız gereken 250 sayfanız var. Bu sayede problemimizi tekrar tekrar ikiye bölüyoruz ve telefon rehberinde sadece 10 bakışta bir kişiyi bulabiliyoruz. Bu, doğrusal bir aramada bakmamız gereken toplam sayfa sayısının %1'ini temsil eder.

Sıralamayı birleştir

Birleştirme sıralamasında, her bölüm bir öğe uzunluğunda olana kadar diziyi ikiye böleriz. Daha sonra bu bölümler doğru sırayla yerlerine döndürülür (birleştirilir).

Şöyle bir diziye bakalım:

Ortadan ikiye bölelim:

Ve bir eleman kalana kadar her parçayı ikiye böleceğiz:

Artık diziyi mümkün olan en kısa bölümlere ayırdığımıza göre bunları doğru sırayla birleştiriyoruz.

İlk önce iki sıralı öğeden oluşan gruplar elde ederiz, sonra bunları dört öğeli gruplar halinde "toplarız" ve son olarak her şeyi sıralanmış bir dizide bir araya getiririz.

Algoritmanın çalışması için aşağıdaki işlemleri uygulamamız gerekir:

  1. Bir diziyi yinelemeli olarak gruplara bölme işlemi (Sort yöntemi).
  2. Doğru sırada birleştirme (Birleştirme yöntemi).

farklı olarak şunu belirtmekte fayda var. doğrusal algoritmalar sıralama, birleştirme sıralaması, başlangıçta sıralanıp sıralanmadığına bakılmaksızın diziyi böler ve birleştirir. Bu nedenle, en kötü durumda doğrusaldan daha hızlı performans gösterse de, en iyi durumda performansı doğrusaldan daha düşük olacaktır. Bu nedenle birleştirme sıralaması en iyisi değil En iyi karar Kısmen sıralanmış bir diziyi sıralamanız gerektiğinde.

Genel geçersiz Sırala(T öğeleri) ( if (items.Length<= 1) { return; } int leftSize = items.Length / 2; int rightSize = items.Length - leftSize; T left = new T; T right = new T; Array.Copy(items, 0, left, 0, leftSize); Array.Copy(items, leftSize, right, 0, rightSize); Sort(left); Sort(right); Merge(items, left, right); } private void Merge(T items, T left, T right) { int leftIndex = 0; int rightIndex = 0; int targetIndex = 0; int remaining = left.Length + right.Length; while(remaining >0) ( if (leftIndex >= left.Length) ( items = right; ) else if (rightIndex >= right.Length) ( items = left; ) else if (left.CompareTo(right)< 0) { items = left; } else { items = right; } targetIndex++; remaining--; } }

Hızlı sıralama

Quicksort, başka bir böl ve yönet algoritmasıdır. Aşağıdaki adımları yinelemeli olarak tekrarlayarak çalışır:

  1. Bir anahtar dizini seçin ve onu kullanarak diziyi iki parçaya bölün. Bu yapılabilir Farklı yollar, ancak bu makalede rastgele bir sayı kullanıyoruz.
  2. Anahtardan büyük tüm öğeleri dizinin sağ tarafına, anahtardan küçük tüm öğeleri ise sol tarafa taşıyın. Anahtar öğe artık doğru konumdadır; soldaki herhangi bir öğeden daha büyük ve sağdaki herhangi bir öğeden daha küçüktür.
  3. Dizi tamamen sıralanana kadar ilk iki adımı tekrarlıyoruz.

Aşağıdaki dizideki algoritmanın işleyişine bakalım:

İlk önce rastgele bir anahtar öğe seçiyoruz:

Int pivotIndex = _pivotRng.Next(sol, sağ);

Artık anahtar indeksini (4) bildiğimize göre, o indekste (6) bulunan değeri alıyoruz ve dizideki değerleri, anahtara eşit veya ondan büyük tüm sayılar sağ tarafta olacak şekilde hareket ettiriyoruz. tuştan küçük sayılar soldadır. Değer aktarma işlemi sırasında anahtar elemanın indeksinin değişebileceğini unutmayın (bunu birazdan göreceğiz).

Değerlerin taşınması bölümleme yöntemi kullanılarak yapılır.

Bu noktada 6 değerinin doğru konumda olduğunu biliyoruz. Şimdi bu işlemi dizinin sağ ve sol tarafları için tekrarlıyoruz.

Her bir parça üzerinde yinelemeli olarak hızlı sıralama yöntemini çağırıyoruz. Sol taraftaki anahtar unsur beştir. Değerleri taşıdığınızda indeksi değişecektir. Önemli olan bizim için önemli olanın endeksi değil, anahtar değer olduğunu hatırlamaktır.

Hızlı sıralamayı tekrar kullanalım:

Bir kez daha:

Elimizde sıralanmamış bir değer kaldı ve diğer her şeyin zaten sıralanmış olduğunu bildiğimiz için algoritma sona eriyor.

Rastgele _pivotRng = new Rastgele(); public void Sort(T items) ( quicksort(items, 0, items.Length - 1); ) Private void quicksort(T items, int left, int right) ( if (left)< right) { int pivotIndex = _pivotRng.Next(left, right); int newPivot = partition(items, left, right, pivotIndex); quicksort(items, left, newPivot - 1); quicksort(items, newPivot + 1, right); } } private int partition(T items, int left, int right, int pivotIndex) { T pivotValue = items; Swap(items, pivotIndex, right); int storeIndex = left; for (int i = left; i < right; i++) { if (items[i].CompareTo(pivotValue) < 0) { Swap(items, i, storeIndex); storeIndex += 1; } } Swap(items, storeIndex, right); return storeIndex; }

Çözüm

Bu, yeni başlayanlar için algoritmalar ve veri yapıları hakkındaki makale serimizi sonlandırıyor. Bu süre zarfında bağlantılı listelere, dinamik dizilere, ikili arama ağaçlarına ve C# kod örnekleri içeren kümelere baktık.

Belki de en basit sıralama algoritması seçimli sıralamadır. Sıralamanın adına bakılırsa, bir şey seçmeniz gerekir (dizinin maksimum veya minimum öğeleri). Seçimli sıralama algoritması, diziyi artan veya azalan şekilde nasıl sıralamak istediğinize bağlı olarak kaynak dizideki maksimum veya minimum öğeleri bulur. Dizinin artan düzende sıralanması gerekiyorsa minimum elemanların orijinal diziden seçilmesi gerekir. Dizinin azalan düzende sıralanması gerekiyorsa maksimum eleman seçilmelidir.

Diyelim ki bir diziyi artan düzende sıralamanız gerekiyor. Orijinal dizideki minimum elemanı bulup onu dizinin ilk elemanıyla değiştiriyoruz. Zaten dizinin tüm elemanlarından bir tanesi yerindedir. Şimdi dizinin sıralanmamış kısmını, yani dizinin ilki dışındaki tüm elemanlarını ele alacağız. Dizinin sıralanmamış kısmında yine minimum elemanı arıyoruz. Bulunan minimum öğe, dizinin ikinci öğesi vb. ile değiştirilir. Böylece, seçim sıralama algoritmasının özü, dizinin sıralanmamış kısmındaki minimum (maksimum) öğelerin tekrar tekrar aranmasına indirgenir. Seçimli Sıralama algoritmasına göre yedi sayıdan oluşan bir diziyi sıralayalım.

orijinal dizi: 3 3 7 1 2 5 0
1) Böylece dizideki minimum elemanı buluyoruz. 0 – minimum eleman
2) Dizinin minimum ve ilk elemanlarını değiştirin.
Geçerli dizi: 0 3 7 1 2 5 3
3) Dizinin sıralanmamış kısmındaki minimum elemanı bulun. 1 – minimum eleman
4) Dizinin minimum ve ilk elemanlarını değiştirin.
Geçerli dizi: 0 1 7 3 2 5 3
5) dk = 2
6) Geçerli dizi: 0 1 2 3 7 5 3
7) dk = 3
8) Mevcut dizi: 0 1 2 3 7 5 3 onun yerinde 3 olduğu için dizide hiçbir şey değişmedi
9) dk = 3
10) Dizinin son görünümü: 0 1 2 3 3 5 7 – dizi sıralanır

Seçim sıralama algoritmasını C++'da programlayalım.

// sorting_choices.cpp: Konsol uygulaması için giriş noktasını tanımlar. #include "stdafx.h" #include #katmak #katmak ad alanı std'sini kullanma; geçersiz seçimlerSort(int*, int); // seçim sıralama fonksiyonunun prototipi int main(int argc, char* argv) ( srand(time(NULL)); setlocale(LC_ALL, "rus"); cout<< "Введите размер массива: "; int size_array; // длинна массива cin >> size_array; int *sorted_array = yeni int; // tek boyutlu dinamik dizi for (int counter = 0; counter< size_array; counter++) { sorted_array = rand() % 100; // заполняем массив rastgele numaralar cout<< setw(2) << sorted_array << " "; // вывод массива на экран } cout << "\n\n"; choicesSort(sorted_array, size_array); // вызов функции сортировки выбором for (int counter = 0; counter < size_array; counter++) { cout << setw(2) << sorted_array << " "; // печать отсортированного массива } cout << "\n"; delete sorted_array; // высвобождаем память system("pause"); return 0; } void choicesSort(int* arrayPtr, int length_array) // сортировка выбором { for (int repeat_counter = 0; repeat_counter < length_array; repeat_counter++) { int temp = arrayPtr; // временная переменная для хранения значения перестановки for (int element_counter = repeat_counter + 1; element_counter < length_array; element_counter++) { if (arrayPtr >arrayPtr) ( temp = diziPtr; arrayPtr = diziPtr; arrayPtr = temp; )) )) )

Seçim sıralama algoritması maksimum (minimum) öğeyi bulmaya yönelik algoritmaya dayanmaktadır. Aslında arama algoritması seçim sıralamasının en önemli parçasıdır. Sıralamanın asıl amacı bir dizinin elemanlarını sıralamak olduğundan permütasyon yapılması gerekir. Sıralanan dizinin elemanlarının değerleri değiştirilir satırlar 4850 . > oturum aç seçeneğini değiştirirseniz 46. ​​satır bundan daha küçük bir işaretle dizi azalan düzende sıralanacaktır. Programın sonucu Şekil 1'de gösterilmektedir.

Şekil 1 - Seçime göre sıralama

Seçime göre sıralama belki de uygulanması en kolay sıralama algoritmasıdır. Diğer benzer algoritmaların çoğu gibi, karşılaştırma işlemine dayanır. Yöntem, her bir öğeyi birbiriyle karşılaştırarak ve gerekirse değişim yaparak diziyi gerekli sıralı forma getirir.

Algoritmanın fikri çok basittir. Bir dizi olsun A boyut N, ardından seçim sıralaması şu şekilde olur:

1. dizinin ilk elemanını alın A[Ben], Burada Ben– ilki için eleman numarası Ben 1'e eşittir;

2. dizinin minimum (maksimum) elemanını bulun ve numarasını bir değişkende saklayın anahtar;

3. eğer ilk elementin numarası ile bulunan elementin numarası eşleşmiyorsa, yani eğer. anahtar≠1 ise bu iki öğe değer alışverişinde bulunur, aksi halde herhangi bir manipülasyon meydana gelmez;

4. artış Ben 1'e göre sıralayın ve dizinin geri kalanını, yani 2 numaralı öğeden 2'ye kadar sıralamaya devam edin. N, elementten beri Aşimdiden pozisyonunu alıyor.

Sonraki her adımda, algoritmanın çalıştığı alt dizinin boyutu 1 azalır ancak bu, sıralama yöntemini etkilemez; her adım için aynıdır.

Belirli bir tamsayı dizisi örneğini kullanarak algoritmanın çalışmasını ele alalım. Beş tam sayıdan (9, 1, 4, 7, 5) oluşan bir dizi (Şekil 6.2) verilmiştir. Seçim sıralamasını kullanarak elemanlarını artan sırada düzenlemeniz gerekir. Öğeleri sırayla karşılaştırmaya başlayalım. İkinci öğe birinciden daha azdır - bunu unutmayın ( anahtar=2). Üstelik onun diğerlerinden daha küçük olduğunu da görüyoruz. anahtar≠1, birinci ve ikinci elemanların yerini değiştirin. 9 değerine sahip elemanın yerini alacak parçayı bulmaya çalışarak parçanın geri kalanını sipariş etmeye devam edelim. anahtar 3 numaralı eleman en düşük değere sahip olduğundan 3 girilecektir. Görüldüğü gibi, anahtar≠2 olduğundan 2. ve 3. elemanların yerini değiştiririz. Bir sonraki adımda alt dizinin boyutu 1'e eşit olana kadar öğeleri yerlerine yerleştirmeye devam ediyoruz.

Şekil 6.2 – Seçim sıralama örneği

C++'daki program kodu:

void SelectionSort(int A, int n) //seçim sıralaması

için (i=0; ben

sayım=A[i]; anahtar=i;

için (j=i+1; j

eğer (A[j]

cout<<"Результирующий массив: ";

için (i=0; ben

void main() //ana fonksiyon

cout<<"Количество элементов >"; cin>>n;

için (i=0; ben

cout< ";


SeçimSort(A, n);

Pascal'daki program kodu:

type arr=tamsayı dizisi;

var i, j, n: tamsayı;

prosedür SelectionSort(A: arr; n: tamsayı); (seçime göre sırala)

var anahtarı, sayım: tamsayı;

i:=1'den n'ye yapmak için

sayım:=A[i]; anahtar:=i;

j:=i+1'den n'ye yapmak için

if (A>A[j]) sonra anahtar:=j;

eğer (anahtar<>Ben o zaman

write("Sonuç dizisi: ");

i:=1'den n'ye kadar şunu yazın(A[i], " "); (dizi çıkışı)

başla (ana program bloğu)

write("Eleman sayısı > ");

for i:=1 - n do (dizi girişi)

write(i," eleman > ");

SeçimSort(A, n);

Seçimli sıralamanın uygulanması kolaydır ve bazı durumlarda daha karmaşık ve gelişmiş yöntemlere tercih edilebilir. Ancak çoğu durumda, bu algoritma verimlilik açısından ikincisine göre daha düşüktür, çünkü en kötü, en iyi ve ortalama durumlarda ihtiyaç duyacaktır. Ö(N 2 kez.