Φυσική σύντηξη με κωδικό. Αλγόριθμοι και δομές δεδομένων για αρχάριους: Ταξινόμηση

Πρόγραμμα Kerish Doctor. 23.06.2019
Επισκόπηση προγράμματος Η έκδοση υπολογιστή του Microsoft Excel Viewer θα επιτρέψει...

Έχει υπολογιστεί ότι έως και το ένα τέταρτο του χρόνου των κεντρικών υπολογιστών αφιερώνεται στην ταξινόμηση δεδομένων. Αυτό συμβαίνει επειδή είναι πολύ πιο εύκολο να βρείτε μια τιμή σε έναν πίνακα που έχει προ-ταξινομηθεί. Διαφορετικά, η αναζήτηση μοιάζει λίγο με την αναζήτηση βελόνας σε μια θημωνιά.

Υπάρχουν προγραμματιστές που κάνουν τα πάντα ώρες εργασίαςπραγματοποιούνται στη μελέτη και εφαρμογή αλγορίθμων διαλογής. Αυτό συμβαίνει επειδή η συντριπτική πλειονότητα του επιχειρηματικού λογισμικού περιλαμβάνει διαχείριση βάσεων δεδομένων. Οι άνθρωποι αναζητούν βάσεις δεδομένων για πληροφορίες όλη την ώρα. Αυτό σημαίνει ότι οι αλγόριθμοι αναζήτησης έχουν μεγάλη ζήτηση.

Υπάρχει όμως ένα «αλλά». Αλγόριθμοι αναζήτησηςλειτουργούν πολύ πιο γρήγορα με βάσεις δεδομένων που είναι ήδη ταξινομημένες. Σε αυτήν την περίπτωση, απαιτείται μόνο γραμμική αναζήτηση.

Ενώ οι υπολογιστές είναι χωρίς χρήστες σε ορισμένα χρονικά σημεία, οι αλγόριθμοι ταξινόμησης συνεχίζουν να λειτουργούν στις βάσεις δεδομένων. Οι ερευνητές έρχονται ξανά και η βάση δεδομένων έχει ήδη ταξινομηθεί με βάση τον έναν ή τον άλλο σκοπό αναζήτησης.

Αυτό το άρθρο παρέχει παραδείγματα υλοποίησης τυπικούς αλγόριθμουςδιαλογή.

Ταξινόμηση επιλογής

Για να ταξινομήσετε έναν πίνακα με αύξουσα σειρά, πρέπει να βρείτε το στοιχείο με τη μεγαλύτερη τιμή σε κάθε επανάληψη. Με αυτό πρέπει να ανταλλάξετε το τελευταίο στοιχείο. Το επόμενο στοιχείο με την υψηλότερη τιμή τοποθετείται από τη δεύτερη έως την τελευταία θέση. Αυτό θα πρέπει να συμβεί έως ότου τα στοιχεία στις πρώτες θέσεις του πίνακα είναι στη σωστή σειρά.

Κωδικός C++

void SortAlgo::selectionSort(int data, int lenD) ( int j = 0; int tmp = 0; for (int i=0; i δεδομένα[k])( j = k; ) ) tmp = δεδομένα[i];

data[i] = δεδομένα[j];

δεδομένα[j] = tmp;

Κωδικός C++

) ) Ταξινόμηση φυσαλίδων

Η ταξινόμηση με φυσαλίδες συγκρίνει γειτονικά στοιχεία και αλλάζει θέσεις εάν το επόμενο στοιχείο είναι μικρότερο από το προηγούμενο. Απαιτούνται πολλαπλές διελεύσεις μέσω των δεδομένων. Κατά το πρώτο πέρασμα συγκρίνονται τα δύο πρώτα στοιχεία του πίνακα. Εάν δεν είναι σε σειρά, ανταλλάσσονται και στη συνέχεια συγκρίνονται τα στοιχεία του επόμενου ζεύγους. Υπό τον ίδιο όρο αλλάζουν και θέσεις. Έτσι, η ταξινόμηση λαμβάνει χώρα σε κάθε κύκλο μέχρι να φτάσει στο τέλος του πίνακα.

Η ταξινόμηση εισαγωγής χωρίζει τον πίνακα σε δύο περιοχές: τακτοποιημένη και μη διατεταγμένη. Αρχικά, ολόκληρη η συστοιχία είναι μια μη ταξινομημένη περιοχή. Στο πρώτο πέρασμα, το πρώτο στοιχείο από την μη ταξινομημένη περιοχή αφαιρείται και τοποθετείται στη σωστή θέση στην τακτοποιημένη περιοχή.

Σε κάθε πέρασμα, το μέγεθος της διατεταγμένης περιοχής αυξάνεται κατά 1 και το μέγεθος της διαταραγμένης περιοχής μειώνεται κατά 1.

Ο κύριος βρόχος εκτείνεται από το 1 έως το N-1. Στην jη επανάληψη, το στοιχείο [i] εισάγεται στη σωστή θέση στην τακτοποιημένη περιοχή. Αυτό γίνεται μετατοπίζοντας όλα τα στοιχεία της διατεταγμένης περιοχής που είναι μεγαλύτερα από [i] μία θέση προς τα δεξιά. Το [i] εισάγεται στο διάστημα μεταξύ εκείνων των στοιχείων που είναι μικρότερα από [i] και εκείνων που είναι μεγαλύτερα από [i].

Κωδικός C++

void SortAlgo::insertionSort(int data, int lenD) ( int key = 0; int i = 0; for (int j = 1;j =0 && data[i]>key)( data = data[i]; i = i-1; data=key; ) ) )

Συγχώνευση ταξινόμησης

Κωδικός C++

void SortAlgo::mergeSort(int data, int lenD) ( if (lenD>1)( int middle = lenD/2; int rem = lenD-middle; int * L = new int; int * R = new int; for ( int i=0;i

Γρήγορη ταξινόμηση

Η γρήγορη ταξινόμηση χρησιμοποιεί έναν αλγόριθμο διαίρει και βασίλευε. Ξεκινά χωρίζοντας τον αρχικό πίνακα σε δύο περιοχές. Αυτά τα μέρη βρίσκονται αριστερά και δεξιά του σημειωμένου στοιχείου, που ονομάζεται στήριγμα. Στο τέλος της διαδικασίας, το ένα μέρος θα περιέχει στοιχεία μικρότερα από την αναφορά και το άλλο μέρος θα περιέχει στοιχεία μεγαλύτερα από την αναφορά.

Κωδικός C++

void SortAlgo::quickSort(int * δεδομένα, int const len) ( int const lenD = len; int pivot = 0; int ind = lenD/2; int i,j = 0,k = 0; εάν (lenD>1) ( int * L = new int ; int * R = new int ; pivot = data; for (i=0;i Λεπτομέρειες Κατηγορία: Ταξινόμηση και Αναζήτηση

Συγχώνευση ταξινόμησης συγχώνευση ταξινόμησης) είναι ένας αλγόριθμος ταξινόμησης που τακτοποιεί λίστες (ή άλλες δομές δεδομένων των οποίων τα στοιχεία μπορούν να προσπελαστούν μόνο διαδοχικά, όπως ροές) με συγκεκριμένη σειρά. Αυτή η ταξινόμηση είναι ένα καλό παράδειγμα χρήσης της αρχής διαίρει και βασίλευε. Πρώτον, η εργασία χωρίζεται σε πολλές μικρότερες δευτερεύουσες εργασίες. Αυτά τα προβλήματα στη συνέχεια επιλύονται χρησιμοποιώντας μια αναδρομική κλήση ή απευθείας εάν το μέγεθός τους είναι αρκετά μικρό. Τέλος, οι λύσεις τους συνδυάζονται για να δώσουν λύση στο αρχικό πρόβλημα.

  1. ο πίνακας χωρίζεται αναδρομικά στη μέση και κάθε μισό διαιρείται έως ότου το μέγεθος του επόμενου υποσυστοιχίας γίνει ίσο με ένα.
  2. Στη συνέχεια, εκτελείται μια λειτουργία αλγορίθμου που ονομάζεται συγχώνευση. Δύο πίνακες μονάδων συγχωνεύονται σε έναν κοινό πίνακα που προκύπτει και το μικρότερο στοιχείο επιλέγεται από τον καθένα (ταξινομημένο σε αύξουσα σειρά) και γράφεται στο ελεύθερο αριστερό κελί του πίνακα που προκύπτει. Μετά από αυτό, ένας τρίτος κοινός ταξινομημένος πίνακας συναρμολογείται από τους δύο πίνακες που προκύπτουν και ούτω καθεξής. Εάν εξαντληθεί ένας από τους πίνακες, τα στοιχεία του άλλου προστίθενται στον συναρμολογημένο πίνακα.
  3. στο τέλος της λειτουργίας συγχώνευσης, τα στοιχεία ξαναγράφονται από τον πίνακα που προκύπτει στον αρχικό.

Εφαρμογή του αλγορίθμου σε διάφορες γλώσσες προγραμματισμού:

C++

/** * @brief Ταξινόμηση στοιχείων l έως r του πίνακα buf * @param buf - πίνακας προς ταξινόμηση * @param l - αριστερό περίγραμμα. Στην πρώτη επανάληψη, l = 0 * @param r - δεξιό όριο. Στην πρώτη επανάληψη, r = buf.size() - 1 * * Ως αποτέλεσμα, όλα τα στοιχεία του πίνακα buf από το l έως το r είναι ταξινομημένα. */ πρότυπο void MergeSort(διάνυσμα & buf, size_t l, size_t r) ( //! Συνθήκη για έξοδο από την αναδρομή if(l >= r) return; size_t m = (l + r) / 2; //! Αναδρομική ταξινόμηση των προκυπτόντων πινάκων MergeSort(buf, l, m; Συγχώνευση (buf, m+1, r); * @param buf - πίνακας * @param l - αριστερό περίγραμμα. Στην πρώτη επανάληψη l = 0 * @param r - δεξιό όριο. Στην πρώτη επανάληψη, r = buf.size() - 1 * @param m - το όριο των υποσυστοιχιών.< l || m >* * Ο πίνακας buf περιέχει δύο ταξινομημένους υποπίνακες: * - - τον πρώτο ταξινομημένο υποπίνακα. * - - δευτερεύουσα ταξινόμηση.<= r; ++i) { if (j >* Το αποτέλεσμα είναι ένας ταξινομημένος πίνακας που αποτελείται από δύο υποσυστοιχίες, * που αποθηκεύεται σε buf.< tmp[k]) ? tmp : tmp; } } }

*/ πρότυπο

συγχώνευση στατικού κενού (διάνυσμα & buf, size_t l, size_t r, size_t m) ( if (l >= r || m< *m2) { ret[n] = *m1; m1++; --l1; } else { ret[n] = *m2; ++m2; --l2; } ++n; } // Если закончился первый массив if (l1 == 0) { for (int i = 0; i < l2; ++i) { ret = *m2++; } } else { // Если закончился второй массив for (int i = 0; i < l1; ++i) { ret = *m1++; } } return ret; } // Функция восходящего слияния template void mergeSort(T * mas, int len) ( int n = 1, l, ost; T * mas1; ενώ (n< len) { l = 0; while (l < len) { if (l + n >= λεν) σπάσιμο;< n + ost; ++i) mas = mas1[i];//тут нужно что-то поменять, потому что это лишнее копирование, и оно увеличивает время работы алгоритма в два раза delete mas1; l += n * 2; } n *= 2; } }

ost = (l + n * 2 > len) ? (len - (l + n)) : n;

mas1 = συγχώνευση(mas + l, mas + l + n, n, ost); για (int i = 0; i<= 1) return; Item *TmpMas = new Item; for (int i = 0; i < count; ++i) { if (j <= medium && k <= right) { if (Mas[j] < Mas[k]) TmpMas[i] = Mas; else TmpMas[i] = Mas; } else { if (j <= medium) TmpMas[i] = Mas; else TmpMas[i] = Mas; } } j = 0; for (int i = left; i <= right; ++i) { Mas[i] = TmpMas; } delete TmpMas; } template Παράδειγμα: char a = "ASORTINGEXAMPLE"; mergeSort(a, 16); Μια εναλλακτική έκδοση του αλγορίθμου Συγχώνευση Ταξινόμησης.

Περίγραμμα

void Merge(Αντικείμενο Mas, int αριστερά, int right, int medium) ( int j = αριστερά; int k = medium + 1; int count = δεξιά - αριστερά + 1; εάν (μέτρηση< mass1.Length + mass2.Length; i++) { if (b < mass2.Length && a < mass1.Length) if (mass1[a] >void MergeSort(Item a, int l, int r) ( int m; // Συνθήκη για έξοδο από την αναδρομή if(l >= r) return; m = (l + r) / 2; // Αναδρομική ταξινόμηση των προκύπτων πινάκων MergeSort (a , l, m);< mass2.Length) merged[i] = mass2; else merged[i] = mass1; } return merged; } static void Main(string args) { Int32 arr = new Int32; //заполняем массив случайными числами Random rd = new Random(); for(Int32 i = 0; i < arr.Length; ++i) { arr[i] = rd.Next(1, 101); } System.Console.WriteLine("The array before sorting:"); foreach (Int32 x in arr) { System.Console.Write(x + " "); } //сортировка arr = Merge_Sort(arr); System.Console.WriteLine("\n\nThe array after sorting:"); foreach (Int32 x in arr) { System.Console.Write(x + " "); } System.Console.WriteLine("\n\nPress the ΝΤΟ#

Περίγραμμα

Static Int32 Merge_Sort(Int32 massive) ( if (massive.Length == 1) return massive; Int32 mid_point = massive.Length / 2; return Merge(Merge_Sort(massive.Take(mid_point).ToArray()), Merge_Sort(massive. Skip(mid_point).ToArray()));< mass1.Length + mass2.Length; i++) { if (b.CompareTo(mass2.Length) < 0 && a.CompareTo(mass1.Length) < 0) if (mass1[a].CompareTo(mass2[b]) >mass2[b]) συγχωνευμένο[i] = mass2;< mass2.Length) merged[i] = mass2; else merged[i] += mass1; } return merged; } static void Main(string args) { IComparable arr = new IComparable; Random rd = new Random(); for (int i = 0; i < arr.Length; ++i) arr[i] = rd.Next(1, 101); Console.WriteLine("Массив перед сортировкой:"); foreach (int x in arr) System.Console.Write(x + " "); arr = Merge_Sort(arr); Console.WriteLine("\n\nМассив после сортировки:"); foreach (int x in arr) System.Console.Write(x + " "); Console.WriteLine("\n\nДля выхода нажмите else //if int πάει για συγχωνευμένο[i] = mass1;

αλλιώς αν (β

κλειδί"); System.Console.ReadLine(); )<=$last;$i++){ if(($j<$n)&&(($k==(($last-$first)+1))||($work[$j]lt$work[$k]))){ $$array[$i]=$work[$j++] }else{ $$array[$i]=$work[$k++]; } } } } sortM(\@out,0,$#out+1);

//το προηγούμενο παράδειγμα ταξινομεί μόνο ακέραιους αριθμούς. Το επόμενο λειτουργεί με όλους τους τύπους δεδομένων. static IComparable Merge_Sort(ICcomparable massive) ( if (massive.Length == 1) return massive; int mid_point = massive.Length / 2; return Merge(Merge_Sort(massive.Take(mid_point).ToArray()), Merge_Sort(massive. Skip(mid_point).ToArray())).

0) συγχωνευμένο[i] = mass2;

other merged[i] = mass1; 0 τότε ξεκινάει tmp:=kol div 2;<>Ενώ tmp mod s

0 ξεκινάει Read(f,a1);

Write(f1,a1," ");

Inc(tmp);

Τέλος; Τέλος; Ενώ δεν EOF(f) ξεκινά Read(f,a2);<= Spl) and (j <=R) do begin if IsBigger(Arr[i], Arr[j]) then begin tmp[k] := Arr[j]; Inc(j); end else begin tmp[k] := Arr[i]; Inc(i); end; Inc(k); end; //Просто дописываем в tmp оставшиеся эл-ты if i <= Spl then //Если первая часть не пуста for j:= i to Spl do begin tmp[k] := Arr[j]; Inc(k); end else //Если вторая часть не пуста for i:= j to R do begin tmp[k] := Arr[i]; Inc(k); end; //Перемещаем из tmp в arr Move(tmp, Arr[L], k*SizeOf(TItem)); end; //Сортировка procedure sort(L, R: Integer); var splitter: Integer; begin //Массив из 1-го эл-та упорядочен по определению if L >Write(f2,a2," ");

Τέλος; Κλείσιμο(f); Κλείσιμο (f1); Κλείσιμο(f2);

Επανεγγραφή(f); Επαναφορά (f1); Επαναφορά (f2);< length; ++k) { array[k] = (leftArray[i] <= rightArray[j]) ? leftArray : rightArray; } } if (array.length >Read(f1,a1);

Read(f2,a2);

Ενώ (όχι EOF(f1)) και (όχι EOF(f2)) αρχίζουν i:=0; j:=0;< right else right).pop(0)) return merge(right=right, left=left, result=result) if left and right else result+left+right merge_sort = (lambda arr: arr if len(arr) == 1 else merge(merge_sort(arr), merge_sort(arr[:len(arr)/2]), ))

Το μειονέκτημα της ταξινόμησης συγχώνευσης είναι ότι χρησιμοποιεί επιπλέον μνήμη. Αλλά όταν πρέπει να εργαστείτε με αρχεία ή λίστες που έχουν πρόσβαση μόνο διαδοχικά, τότε είναι πολύ βολικό να χρησιμοποιήσετε αυτήν τη συγκεκριμένη μέθοδο. Επίσης, στα πλεονεκτήματα του αλγορίθμου περιλαμβάνεται η σταθερότητα και η καλή ταχύτητα λειτουργίας O(n*logn).

Κατά τη σύνταξη αυτού του άρθρου, χρησιμοποιήθηκαν ανοιχτές πηγές στο Διαδίκτυο:

Συγχώνευση ταξινόμησης

(Βλέπε σελίδα 430 των F.M. Carrano και J.J. Pritchard)

Δύο σημαντικοί αλγόριθμοι ταξινόμησης διαίρει και βασίλευε, η συγχώνευση ταξινόμησης και η γρήγορη ταξινόμηση, έχουν μια κομψή αναδρομική εφαρμογή και είναι εξαιρετικά αποτελεσματικοί. Σε αυτήν την ενότητα θα εξετάσουμε τη συγχώνευση ταξινόμησης, αλλά το Κεφάλαιο 14 θα δείξει ότι αυτός ο αλγόριθμος μπορεί να γενικευτεί σε εξωτερικά αρχεία. Κατά τη διαμόρφωση του αλγόριθμου, θα χρησιμοποιήσουμε τη σημείωση για ένα τμήμα πίνακα η Συστοιχία.

Ο αλγόριθμος ταξινόμησης συγχώνευσης είναι αναδρομικός. Η αποτελεσματικότητά του δεν εξαρτάται από τη σειρά των στοιχείων στον αρχικό πίνακα. Ας υποθέσουμε ότι χωρίσαμε τον πίνακα στη μέση, ταξινομήσαμε αναδρομικά και τα δύο μισά και μετά τα συνδυάσαμε σε ένα ενιαίο σύνολο, όπως φαίνεται στο Σχ. 9.8. Το σχήμα δείχνει ότι τα μέρη του πίνακα<1, 4, 8>Και<2, 3>συγχωνεύτηκαν σε έναν πίνακα<1, 2, 3, 4, 8>. Κατά τη συγχώνευση, στοιχεία που βρίσκονται σε διαφορετικά μέρη του πίνακα συγκρίνονται μεταξύ τους σε ζεύγη και το μικρότερο στοιχείο αποστέλλεται σε έναν προσωρινό πίνακα.Αυτή η διαδικασία συνεχίζεται μέχρι να χρησιμοποιηθεί ένα από τα δύο μέρη του πίνακα. Τώρα πρέπει απλώς να αντιγράψετε τα υπόλοιπα στοιχεία σε έναν προσωρινό πίνακα. Τέλος, τα περιεχόμενα του προσωρινού πίνακα αντιγράφονται πίσω στον αρχικό πίνακα.

Ρύζι. 9.8. Συγχώνευση ταξινόμησης χρησιμοποιώντας έναν βοηθητικό πίνακα

Αν και η συγχώνευση οδηγεί σε έναν ταξινομημένο πίνακα, παραμένει ασαφές πώς γίνεται η ταξινόμηση στα προηγούμενα βήματα. Η ταξινόμηση συγχώνευσης εκτελείται αναδρομικά. Ο ψευδοκώδικάς του μοιάζει με αυτό:

συγχώνευση (μέσα στον πίνακα: ItemArray,

στο πρώτο: ακέραιος, στο τελευταίο: ακέραιος)

// Παραγγελίες του τμήματος Array,

// 1) ταξινόμηση του πρώτου μισού του πίνακα.

// 2) ταξινόμηση του δεύτερου μισού του πίνακα.

// 3) συνδυάζοντας δύο διατεταγμένα μισά του πίνακα.

Αν (πρώτα< last)

mid = (πρώτο + τελευταίο)/2 // Προσδιορίστε το μέσο

// Ταξινόμηση του τμήματος theArray mergesort(theArray, first, mid)

// Ταξινόμηση του τμήματος theArray mergesort(theArray, mid + 1, last)

// Συνδυάστε τα διατεταγμένα τμήματα του Array

// και το Array

συγχώνευση (theArray, πρώτος, μέσος, τελευταίος)

) // Τέλος της δήλωσης if

// Εάν το πρώτο >= τελευταίο, οι λειτουργίες ολοκληρώνονται

Είναι σαφές ότι οι κύριες λειτουργίες αυτού του αλγορίθμου εκτελούνται κατά το στάδιο της συγχώνευσης, και όμως γιατί καταλήγει σε μια διατεταγμένη διάταξη; Οι αναδρομικές κλήσεις συνεχίζουν να χωρίζουν τμήματα του πίνακα στη μέση μέχρι να μείνει μόνο ένα στοιχείο. Προφανώς, διατάσσεται ένας πίνακας που αποτελείται από ένα στοιχείο. Στη συνέχεια, ο αλγόριθμος συνδυάζει τα θραύσματα του πίνακα μέχρι να σχηματιστεί ένας ενιαίος διατεταγμένος πίνακας. Αναδρομικές κλήσεις συναρτήσεων συγχώνευσηκαι τα αποτελέσματα των συγχωνεύσεων απεικονίζονται στο Σχ. 9.9 χρησιμοποιώντας ένα παράδειγμα πίνακα που αποτελείται από έξι ακέραιους αριθμούς.


Ρύζι. 9.9. Συγχώνευση ενός είδους πίνακα έξι ακεραίων

Παρακάτω είναι μια συνάρτηση C++ που υλοποιεί τον αλγόριθμο ταξινόμησης συγχώνευσης. Για να ταξινομήσετε έναν πίνακα η Συστοιχία,που αποτελείται από νστοιχεία, πραγματοποιείται μια αναδρομική κλήσησυγχώνευση (theArray, 0, n-1).

const int MAX_SIZE = μέγιστος αριθμός στοιχείων πίνακα, -

κενόςσυγχώνευση (DataType theArray, ενθπρώτα, ενθστα μέσα ενθτελευταίος)

// Συνδυάζει δύο διατεταγμένα τμήματα theArray και

//theArray σε έναν διατεταγμένο πίνακα.

// Προϋπόθεση:πρώτα<= mid <= last. Оба подмассива

// Το Array και το Array είναι διατεταγμένα

// αύξουσα.

// Μετασυνθήκη:το τμήμα Array είναι διατεταγμένο.

// Σημείωση υλοποίησης:η συνάρτηση συγχωνεύει δύο

// υποπίνακες σε έναν προσωρινό πίνακα και στη συνέχεια τον αντιγράφει

// περιεχόμενα στον αρχικό πίνακα theArray.

// _________________________________________________________

Ανάλυση.Εφόσον οι κύριες λειτουργίες σε αυτόν τον αλγόριθμο εκτελούνται στο στάδιο της συγχώνευσης, ας ξεκινήσουμε την ανάλυση με αυτό. Σε κάθε βήμα, οι υποσυστοιχίες συγχωνεύονται η ΣυστοιχίαΚαι η Συστοιχία.Στο Σχ. Το Σχήμα 9.10 δείχνει ένα παράδειγμα στο οποίο απαιτείται ο μέγιστος αριθμός συγκρίσεων. Αν ο συνολικός αριθμός των στοιχείων των συγχωνευμένων τμημάτων πίνακα είναι ίσος με p,τότε κατά τη συγχώνευσή τους, θα πρέπει να γίνουν συγκρίσεις n-1. (Για παράδειγμα, στο Σχ. 9.10 δείχνει έναν πίνακα που αποτελείται από έξι στοιχεία, επομένως πραγματοποιούνται πέντε συγκρίσεις.) Επιπλέον, μετά τις συγκρίσεις, εκτελείται ένα αντίγραφο nστοιχεία του προσωρινού πίνακα στον αρχικό. Έτσι, σε κάθε βήμα συγχώνευσης, εκτελούνται 3*n-1 κύριες λειτουργίες.



Σε λειτουργία συγχώνευσηγίνονται δύο αναδρομικές κλήσεις. Όπως φαίνεται στο Σχ. 9.11, εάν η αρχική κλήση της συνάρτησης συγχώνευσηανήκει στο επίπεδο μηδέν, τότε πραγματοποιούνται δύο αναδρομικές κλήσεις στο επίπεδο ένα. Κάθε μία από αυτές τις κλήσεις δημιουργεί στη συνέχεια δύο ακόμη αναδρομικές κλήσεις δεύτερου επιπέδου και ούτω καθεξής. Πόσα επίπεδα αναδρομής θα υπάρχουν; Ας προσπαθήσουμε να τα μετρήσουμε.

Κάθε κλήση στη συνάρτηση συγχώνευσης χωρίζει τον πίνακα στη μέση. Στο πρώτο στάδιο, ο αρχικός πίνακας χωρίζεται σε δύο μέρη. Στην επόμενη κλήση αναδρομικής συνάρτησης συγχώνευσηκαθένα από αυτά τα μέρη χωρίζεται και πάλι στο μισό, σχηματίζοντας τέσσερα μέρη του αρχικού πίνακα. Στην επόμενη αναδρομική κλήση, καθένα από αυτά τα τέσσερα μέρη χωρίζεται και πάλι στη μέση, σχηματίζοντας οκτώ μέρη του πίνακα, και ούτω καθεξής. Οι αναδρομικές κλήσεις συνεχίζονται έως ότου τα μέρη του πίνακα περιέχουν μόνο ένα στοιχείο, με άλλα λόγια, έως ότου ο αρχικός πίνακας χωριστεί σε nμέρη, που αντιστοιχεί στον αριθμό των στοιχείων του, αν ο αριθμός nείναι δύναμη δύο (n=2 m), το βάθος αναδρομής είναι k=log 2 n Για παράδειγμα, όπως φαίνεται στο Σχ. 9.11, εάν ο αρχικός πίνακας περιέχει οκτώ στοιχεία (8=2 3), τότε το βάθος αναδρομής είναι 3. Εάν ο αριθμός nδεν είναι δύναμη δύο, το βάθος αναδρομής είναι 1+ log 2 n (στρογγυλοποιημένη τιμή).

Κλήση αρχικής λειτουργίας συγχώνευση(επίπεδο 0) έχει πρόσβαση στη συνάρτηση συγχώνευσημόνο μια φορά. Στη συνέχεια η συνάρτηση συγχώνευσηπραγματοποιεί συγχώνευση nστοιχεία, εκτελώντας λειτουργίες 3*n-1. Στο πρώτο επίπεδο αναδρομής, πραγματοποιούνται δύο κλήσεις συναρτήσεων συγχώνευσηκαι επομένως οι λειτουργίες συγχώνευση.Κάθε μία από αυτές τις δύο κλήσεις συγχωνεύει n/2 στοιχεία και απαιτεί 3*(n/2)-1 λειτουργίες. Έτσι, σε αυτό το επίπεδο εκτελούνται 2*(3*(n/2)-1)=3*n-2 πράξεις. Σε επίπεδο mth πραγματοποιούνται αναδρομές 2 τσυγχώνευση κλήσεων συνάρτησης. Κάθε μία από αυτές τις κλήσεις οδηγεί σε συγχώνευση p/2 tστοιχεία, και ο συνολικός αριθμός πράξεων είναι 3*(n/2 m)-2. Συνολικά, 2 m αναδρομικές κλήσεις συναρτήσεων συγχώνευσηπαράγει λειτουργίες 3*n-2 m. Έτσι, οι λειτουργίες O(l) εκτελούνται σε κάθε επίπεδο αναδρομής. Δεδομένου ότι ο αριθμός των επιπέδων αναδρομής είναι ίσος με log2nή log 2 n+l, στις χειρότερες και μέσες περιπτώσεις η συνάρτηση συγχώνευσηέχει πολυπλοκότητα O(n*log 2 n). Κοιτάξτε το σχ. 9.3 και βεβαιωθείτε για άλλη μια φορά ότι η ποσότητα O(n*log 2 n) αυξάνεται πολύ πιο αργά από την ποσότητα O(n g).



Αν και ο αλγόριθμος ταξινόμησης συγχώνευσης είναι εξαιρετικά γρήγορος, έχει ένα μειονέκτημα. Για να εκτελέσετε την επέμβαση

Συγχώνευση διατεταγμένων υποσυστοιχιών theArray και theArray

Χρειάζεται ένας βοηθητικός πίνακας που αποτελείται από n στοιχεία. Εάν η διαθέσιμη μνήμη είναι περιορισμένη, αυτή η απαίτηση ενδέχεται να μην είναι αποδεκτή.


Το διαμέρισμα που φαίνεται στο Σχ. 9.12, χαρακτηρίζεται από το γεγονός ότι όλα τα στοιχεία του συνόλου Si=theArrayλιγότερο στοιχείο υποστήριξης p,και πολλά S 2 = ο Πίνακαςαποτελείται από στοιχεία μεγαλύτερα ή ίσα με το υποστηρικτικό. Αν και αυτή η ιδιότητα δεν υπονοεί ότι ο πίνακας είναι ταξινομημένος, υπονοεί ένα εξαιρετικά χρήσιμο γεγονός: εάν ο πίνακας είναι ταξινομημένος, τα στοιχεία σε θέσεις από πρώτανα pivotlndex-l,παραμένουν στις θέσεις τους, αν και οι θέσεις τους σε σχέση μεταξύ τους μπορεί να αλλάξουν. Μια παρόμοια δήλωση ισχύει για στοιχεία που βρίσκονται σε θέσεις από pivotlndex+lνα τελευταίος.Το στοιχείο αναφοράς στον προκύπτον διατεταγμένο πίνακα θα παραμείνει στη θέση του.

στη θέση του

Αυτή η διαίρεση του πίνακα καθορίζει την αναδρομική φύση του αλγορίθμου. Διαίρεση ενός πίνακα σε σχέση με ένα στοιχείο αναφοράς rδημιουργεί δύο μικρότερες εργασίες ταξινόμησης - ταξινόμηση του αριστερού (S 1) και του δεξιού (S 2) τμήματος του πίνακα. Έχοντας λύσει αυτά τα δύο προβλήματα, λαμβάνουμε μια λύση στο αρχικό πρόβλημα. Με άλλα λόγια, ο διαχωρισμός του πίνακα πριν από αναδρομικές κλήσεις αφήνει το στοιχείο αγκύρωσης στη θέση του και διασφαλίζει ότι το αριστερό και το δεξί τμήμα του πίνακα είναι σε τάξη. Επιπλέον, ο αλγόριθμος γρήγορης ταξινόμησης είναι πεπερασμένος: τα μεγέθη του αριστερού και του δεξιού τμήματος του πίνακα είναι μικρότερα από το μέγεθος του αρχικού πίνακα και κάθε βήμα αναδρομής μας φέρνει πιο κοντά στη βάση όταν ο πίνακας αποτελείται από ένα στοιχείο. Αυτό προκύπτει από το γεγονός ότι το στοιχείο αναφοράς p δεν ανήκει σε κανέναν από τους πίνακες S lκαι S 2 .

Ο ψευδοκώδικας για τον αλγόριθμο γρήγορης ταξινόμησης μοιάζει με αυτό:

γρήγορη ταξινόμηση (εντός του Array.- ItemArray,

in first:integer, in last:integer) // Orders theArray

αν (πρώτα< last)

Επιλέξτε ένα στοιχείο αναφοράς p από τον πίνακα theArray Διαχωρίστε σχετικά τον πίνακα theArray

στοιχείο αναφοράς p // Το διαμέρισμα μοιάζει με το Array

// Παραγγελία του πίνακα SI

γρήγορη ταξινόμηση (theArray, first, pivotlndex-l)

// Παραγγελία πίνακα S2

quicksort(theArray, pivotlndex+l, last) ) // Τέλος της εντολής if // if first >= last, do nothing


Πώς να επιλέξετε ένα στοιχείο υποστήριξης; Εάν τα στοιχεία πίνακα είναι γραμμένα με τυχαία σειρά, μπορείτε να επιλέξετε οποιοδήποτε στοιχείο ως αναφορά, για παράδειγμα η Συστοιχία.(Η διαδικασία για την επιλογή ενός στοιχείου αναφοράς θα συζητηθεί με περισσότερες λεπτομέρειες αργότερα.) Κατά τη διαίρεση ενός πίνακα, είναι βολικό να τοποθετήσετε το στοιχείο αναφοράς σε ένα κελί η Συστοιχία,ανεξάρτητα από το ποιο στοιχείο επιλέγεται ως αναφορά.

Το τμήμα του πίνακα που περιέχει στοιχεία που δεν έχουν ακόμη κατανεμηθεί στα τμήματα S1 και S2 ονομάζεται απροσδιόριστο. Λοιπόν, εξετάστε τον πίνακα που φαίνεται στο Σχ. 9.14. Πρώτα τα ευρετήρια, lastS1, firstΆγνωστοκαι τελευταία χωρίστε τη συστοιχία σε τρία μέρη. Σχέσεις μεταξύ του στοιχείου αναφοράς και των στοιχείων του απροσδιόριστου μέρους η Συστοιχίαάγνωστος.

Puc. 9.14. Αμετάβλητο του αλγόριθμου κατάτμησης

Κατά τον διαχωρισμό ενός πίνακα, πρέπει να πληρούται η ακόλουθη προϋπόθεση.

Τα στοιχεία του συνόλου S1 πρέπει να είναι μικρότερα από το στοιχείο στήριξης και τα στοιχεία του συνόλου S 2- μεγαλύτερο ή ίσο με αυτό.

Αυτή η δήλωση είναι αμετάβλητη του αλγορίθμου διαμερισμάτων. Για να εκτελεστεί το αμετάβλητό του στην αρχή του αλγορίθμου, είναι απαραίτητο να αρχικοποιηθούν οι δείκτες του πίνακα έτσι ώστε ολόκληρος ο πίνακας, εκτός από το στοιχείο αναφοράς, να θεωρείται απροσδιόριστος.

πρώτος πρώτος Άγνωστος τελευταίος

Puc. 9.15. Αρχική κατάσταση του πίνακα


Σε κάθε βήμα του αλγορίθμου κατάτμησης, ελέγχεται ένα στοιχείο από το απροσδιόριστο τμήμα. Ανάλογα με την τιμή του, τοποθετείται στο σύνολο S1 ή S2.Έτσι, σε κάθε βήμα το μέγεθος του αβέβαιου τμήματος μειώνεται κατά ένα. Ο αλγόριθμος σταματά όταν το μέγεθος του απροσδιόριστου τμήματος γίνει μηδέν, δηλ. πληρούται η προϋπόθεση πρώτος Άγνωστος > τελευταίος.

Ας δούμε τον ψευδοκώδικα αυτού του αλγορίθμου.

χώρισμα (μέσα στον πίνακα: ItemArray,

in first:integer, in last:integer,

out pivotlndex:integer) // Διαχωρίζει τον πίνακα

// Αρχικοποίηση

Επιλέξτε το στοιχείο υποστήριξης και αλλάξτε τις θέσεις του

με το στοιχείο Array
p = theArray // p
- στοιχείο υποστήριξης

// Ορίστε τα κενά σύνολα S1 και S 2 και αρχικοποιήστε το απροσδιόριστο // τμήμα του πίνακα με ένα τμήμα

// theArray lastSl= πρώτος πρώτος Άγνωστος = πρώτος + 1

// Ορισμός συνόλων Sj και S 2 ενώ (πρώτο Άγνωστο<= last)

// Υπολογίστε τον δείκτη του πιο αριστερού στοιχείου // του απροσδιόριστου τμήματος του πίνακααν (ο Συστοιχία< р)

Τοποθετήστε το στοιχείο Array στο Siαλλού

Τοποθετήστε το στοιχείο Array στο S 2 ) // Τέλος της εντολής while

// Τοποθετήστε ένα στοιχείο υποστήριξης μεταξύ των σετ S 2 και S 2 // και θυμηθείτε το νέο του ευρετήριο

Εναλλάξτε το Array και το Array pivotlndex = lastSl

Ο αλγόριθμος είναι αρκετά απλός, αλλά η λειτουργία κίνησης απαιτεί διευκρίνιση. Εξετάστε δύο πιθανές ενέργειες που πρέπει να εκτελεστούν σε κάθε επανάληψη του βρόχου ενώ.

Στοιχείο θέσης η Συστοιχίαστο σύνολο S t . Το σύνολο S1 και το αόριστο μέρος, κατά κανόνα, δεν γειτνιάζουν. Συνήθως το σετ S 2 βρίσκεται ανάμεσά τους. Ωστόσο, αυτή η λειτουργία μπορεί να εκτελεστεί πιο αποτελεσματικά. Στοιχείο η Συστοιχίαμπορεί να αντικατασταθεί με το πρώτο στοιχείο του συνόλου S 2, δηλ. με στοιχείο η Συστοιχία, όπως φαίνεται στο Σχ. 9.16. Πώς να γίνεις στοιχείο ενός συνόλου S2,που τοποθετήθηκε στο κελί η Συστοιχία;Εάν αυξήσετε τον δείκτη πρώτος Άγνωστοςκατά ένα, αυτό το στοιχείο γίνεται το πιο δεξί στο σύνολο S 2 . Έτσι, για να μετακινήσετε ένα στοιχείο η Συστοιχίαγια τον πίνακα S1, πρέπει να εκτελέσετε τα ακόλουθα βήματα.

Αλλάξτε τα στοιχεία του Array

και ο Δείκτης Αύξησης Πίνακας τελευταίοςSl κατά ένα Δείκτης Αύξησης πρώτα Άγνωστος κατά ένα

Αυτή η στρατηγική παραμένει έγκυρη ακόμα και αν το σύνολο S 2 είναι κενό. Σε αυτή την περίπτωση η τιμή lastSl+lίσο με δείκτη πρώτο Άγνωστοκαι το στοιχείο απλά παραμένει στη θέση του.

Στοιχείο θέσης η Συστοιχίαστο σετ S 2 . Αυτή η λειτουργία είναι εύκολη στην εκτέλεση. Θυμηθείτε ότι ο δείκτης του δεξιότερου στοιχείου του συνόλου S 2 είναι ίσος με πρώτο Άγνωστο-1,εκείνοι. το σύνολο S 2 και το άγνωστο μέρος είναι γειτονικά (Εικ. 9.17). Έτσι για να μετακινήσετε ένα στοιχείο η Συστοιχίαστο σύνολο S 2, πρέπει απλώς να αυξήσετε τον δείκτη πρώτος Άγνωστοςκατά ένα, επεκτείνοντας το σύνολο S 2 προς τα δεξιά. Στην περίπτωση αυτή δεν παραβιάζεται το αμετάβλητο.

Μετά τη μεταφορά όλων των στοιχείων από το απροσδιόριστο μέρος στα σύνολα S 1και το S 2 μένει να λύσει το τελευταίο πρόβλημα. Πρέπει να τοποθετήσετε ένα στοιχείο υποστήριξης μεταξύ των συνόλων S1 και S2. Σημειώστε ότι το στοιχείο η Συστοιχίαείναι



Ρύζι. 9.17. Μετακίνηση του στοιχείου Array στη ρύθμιση S2 μετά την αύξηση του πρώτου Άγνωστου ευρετηρίου κατά ένα

είναι το πιο δεξί στοιχείο του συνόλου S1.Εάν το αντικαταστήσετε με το στοιχείο υποστήριξης, θα βρίσκεται στη σωστή θέση. Επομένως, ο χειριστής

pivotlndex = lastSl

σας επιτρέπει να προσδιορίσετε το ευρετήριο του στοιχείου αναφοράς. Αυτός ο δείκτης μπορεί να χρησιμοποιηθεί ως όριο μεταξύ των συνόλων Sj και 5r. Τα αποτελέσματα της ανίχνευσης του αλγορίθμου για την κατάτμηση ενός πίνακα που αποτελείται από έξι ακέραιους αριθμούς, όταν το πρώτο στοιχείο είναι η αναφορά, φαίνονται στο Σχ. 9.18.

Πριν αρχίσουμε να εφαρμόζουμε τον αλγόριθμο γρήγορης ταξινόμησης, ας ελέγξουμε την ορθότητα του αλγορίθμου διαμερισμάτων χρησιμοποιώντας τα αμετάβλητά του. Το αμετάβλητο του κύκλου που περιλαμβάνεται στον αλγόριθμο έχει την ακόλουθη μορφή.

Όλα τα στοιχεία του σετ S 2 (theArray) είναι μικρότερο από το ένα αναφοράς και όλα τα στοιχεία του συνόλου S 2 (theArray) είναι μεγαλύτερα ή ίσα με το ένα αναφοράς

Θυμηθείτε ότι για να προσδιοριστεί η ορθότητα ενός αλγορίθμου χρησιμοποιώντας τα αμετάβλητά του, πρέπει να εκτελεστούν τέσσερα βήματα.

1. Το αμετάβλητο πρέπει να είναι αληθές από την αρχή, πριν εκτελεστεί ο βρόχος. Στον αλγόριθμο κατάτμησης, το στοιχείο αναφοράς είναι η Συστοιχία,άγνωστο τμήμα - τμήμα πίνακα η Συστοιχία,α του συνόλου S1 και S 2αδειάζω. Προφανώς, υπό αυτές τις συνθήκες το αμετάβλητο είναι αληθές.

2. Οι επαναλήψεις βρόχου δεν πρέπει να παραβιάζουν το αμετάβλητο. Στον αλγόριθμο κατάτμησης, κάθε επανάληψη του βρόχου μεταφέρει ένα στοιχείο από το άγνωστο τμήμα στο σύνολο S1 ή S2, ανάλογα με την τιμή του σε σύγκριση με την αναφορά. Έτσι, εάν το αμετάβλητο ήταν αληθές πριν από τη μεταφορά, πρέπει να παραμείνει αληθές και μετά τη μεταφορά.

3. Το αμετάβλητο πρέπει να καθορίσει την ορθότητα του αλγορίθμου. Με άλλα λόγια, η ορθότητα του αλγορίθμου πρέπει να προκύπτει από την αλήθεια του αμετάβλητου. Ο αλγόριθμος κατάτμησης σταματά όταν η απροσδιόριστη περιοχή γίνει κενή. Σε αυτήν την περίπτωση, κάθε στοιχείο του τμήματος theArray πρέπει να ανήκει είτε στο σύνολο S1 είτε στο σύνολο S2. Σε κάθε περίπτωση, από την ορθότητα του αμετάβλητου προκύπτει ότι
ο αλγόριθμος έχει πετύχει τον στόχο του.

4. Ο κύκλος πρέπει να είναι πεπερασμένος. Με άλλα λόγια, πρέπει να δείξετε ότι ο βρόχος θα ολοκληρωθεί μετά από έναν πεπερασμένο αριθμό επαναλήψεων. Στον αλγόριθμο κατάτμησης, το μέγεθος του αβέβαιου τμήματος μειώνεται κατά ένα σε κάθε επανάληψη. Επομένως, μετά από έναν πεπερασμένο αριθμό επαναλήψεων, το απροσδιόριστο τμήμα γίνεται κενό και ο βρόχος τελειώνει.



Ρύζι. 9.18. Πρώτη διαίρεση ενός πίνακα όταν το πρώτο στοιχείο είναι το pivot


Πριν καλέσετε τη συνάρτηση γρήγορης ταξινόμησης, ο πίνακας χωρίζεται σε μέρη S1 και S2. Στη συνέχεια, ο αλγόριθμος διατάσσει τα τμήματα S1 και S2 ανεξάρτητα το ένα από το άλλο, αφού οποιοδήποτε στοιχείο στο τμήμα S1 βρίσκεται στα αριστερά οποιουδήποτε στοιχείου στο τμήμα S2. Στη συνάρτηση mergeSort, από την άλλη πλευρά, δεν γίνεται καμία εργασία πριν από τις αναδρομικές κλήσεις. Ο αλγόριθμος διατάσσει κάθε μέρος του πίνακα, λαμβάνοντας συνεχώς υπόψη τις σχέσεις μεταξύ των στοιχείων και των δύο μερών. Για το λόγο αυτό, ο αλγόριθμος πρέπει να συγχωνεύσει τα δύο μισά του πίνακα μετά την πραγματοποίηση αναδρομικών κλήσεων.

Ανάλυση. Η κύρια εργασία στον αλγόριθμο γρήγορης ταξινόμησης γίνεται στο στάδιο της κατάτμησης πίνακα. Κατά την ανάλυση κάθε στοιχείου που ανήκει στο απροσδιόριστο τμήμα, είναι απαραίτητο να συγκρίνετε το στοιχείο Array με το στοιχείο αναφοράς και να το τοποθετήσετε είτε στο τμήμα S1 είτε στο τμήμα S2. Ένα από τα τμήματα S1 ή S2 μπορεί να είναι κενό. για παράδειγμα, εάν το στοιχείο υποστήριξης είναι το μικρότερο στοιχείο του τμήματος, το σύνολο S1 θα παραμείνει κενό. Αυτό συμβαίνει στη χειρότερη περίπτωση επειδή το μέγεθος του τμήματος S2 μειώνεται μόνο κατά ένα κάθε φορά που καλείται η συνάρτηση γρήγορης ταξινόμησης. Έτσι, σε αυτήν την περίπτωση, θα πραγματοποιηθεί ο μέγιστος αριθμός αναδρομικών κλήσεων στη συνάρτηση γρήγορης ταξινόμησης.

Την επόμενη φορά που θα καλέσετε τη γρήγορη ταξινόμηση αναδρομικά, η κατάτμηση θα εξετάσει στοιχεία n-1. Για τη διανομή τους σε τμήματα, χρειάζονται n-2 συγκρίσεις. Εφόσον το μέγεθος του τμήματος που εξετάζεται από τη συνάρτηση γρήγορης ταξινόμησης μειώνεται μόνο κατά ένα σε κάθε επίπεδο αναδρομής, θα υπάρχουν n-1 επίπεδα αναδρομής. Επομένως, η συνάρτηση γρήγορης ταξινόμησης εκτελεί 1 + 2 + ...+ (n-1) = n * (n-1)/2 συγκρίσεις. Ας θυμηθούμε, ωστόσο, ότι όταν μεταφέρουμε ένα στοιχείο στο σύνολο S2, δεν είναι απαραίτητο να αναδιατάξουμε τα στοιχεία. Για να το κάνετε αυτό, απλά πρέπει να αλλάξετε το πρώτο Άγνωστο ευρετήριο.

Ομοίως, εάν το σύνολο S2 παραμείνει κενό σε κάθε αναδρομική κλήση, θα απαιτηθούν συγκρίσεις n * (n-1)/2. Επιπλέον, σε αυτή την περίπτωση, για να μεταφερθεί κάθε στοιχείο από το άγνωστο μέρος στο σύνολο S1, θα χρειαστεί να αναδιατάξουμε τα στοιχεία. Έτσι, θα χρειαστούν *(n-1)/2 μεταθέσεις. (Θυμηθείτε ότι κάθε μετάθεση εκτελείται χρησιμοποιώντας τρεις πράξεις ανάθεσης.) Έτσι, στη χειρότερη περίπτωση, η πολυπλοκότητα του αλγορίθμου γρήγορης ταξινόμησης είναι O(n2).

Για αντίθεση, στο Σχ. Το σχήμα 9.20 δείχνει ένα παράδειγμα όπου τα σύνολα S1 και S2 αποτελούνται από τον ίδιο αριθμό στοιχείων. Στη μέση περίπτωση, όταν τα σύνολα S1 και S2 αποτελούνται από τον ίδιο - ή περίπου τον ίδιο - αριθμό στοιχείων, γραμμένων με τυχαία σειρά, θα απαιτούνται λιγότερες αναδρομικές κλήσεις για γρήγορη ταξινόμηση. Όπως και στην ανάλυση του αλγορίθμου mergeSort, είναι εύκολο να δείξουμε ότι το βάθος αναδρομής στον αλγόριθμο γρήγορης ταξινόμησης είναι ίσο με log2n ή log2n+l. Κάθε κλήση σε γρήγορη ταξινόμηση εκτελεί m συγκρίσεις και το πολύ m μεταθέσεις, όπου m είναι ο αριθμός των στοιχείων στον υποπίνακα που πρόκειται να ταξινομηθεί.



Γρήγορη ταξινόμηση: χειρότερη περίπτωση O(n 2), μέση περίπτωση O(n*logn).

Έτσι, με μεγάλους πίνακες ο αλγόριθμος

Το quicksort είναι σημαντικά ταχύτερο από το insertionSort, αν και στη χειρότερη περίπτωση έχουν και τα δύο περίπου την ίδια απόδοση.

Ο αλγόριθμος γρήγορης ταξινόμησης χρησιμοποιείται συχνά για την ταξινόμηση μεγάλων πινάκων. Ο λόγος της δημοτικότητάς του είναι η εξαιρετική του απόδοση, παρά τις αποθαρρυντικές εκτιμήσεις στη χειρότερη περίπτωση. Το γεγονός είναι ότι αυτή η επιλογή είναι εξαιρετικά σπάνια και στην πράξη ο αλγόριθμος γρήγορης ταξινόμησης λειτουργεί εξαιρετικά με σχετικά μεγάλους πίνακες.

Η σημαντική διαφορά μεταξύ των εκτιμήσεων της μέσης και της χειρότερης πολυπλοκότητας κάνει το Quicksort να ξεχωρίζει από τους άλλους αλγόριθμους που συζητούνται σε αυτό το κεφάλαιο. Εάν η σειρά των στοιχείων στον αρχικό πίνακα είναι "τυχαία", ο αλγόριθμος γρήγορης ταξινόμησης αποδίδει τουλάχιστον το ίδιο καλά με οποιονδήποτε άλλο αλγόριθμο που χρησιμοποιεί συγκρίσεις στοιχείων. Εάν ο αρχικός πίνακας είναι εντελώς ακατάλληλος, ο αλγόριθμος γρήγορης ταξινόμησης λειτουργεί καλύτερα.

Ο αλγόριθμος mergeSort έχει περίπου την ίδια αποτελεσματικότητα. Σε ορισμένες περιπτώσεις ο αλγόριθμος γρήγορης ταξινόμησης είναι ταχύτερος, σε άλλες ο αλγόριθμος mergeSort είναι ταχύτερος. Αν και η εκτίμηση της χειρότερης πολυπλοκότητας του mergeSort είναι της ίδιας τάξης μεγέθους με την εκτίμηση της μέσης πολυπλοκότητας της γρήγορης ταξινόμησης, η γρήγορη ταξινόμηση είναι ελαφρώς ταχύτερη στις περισσότερες περιπτώσεις. Ωστόσο, στο χειρότερο σενάριο, η απόδοση του αλγόριθμου γρήγορης ταξινόμησης είναι πολύ χαμηλότερη.

Κάποιος το είπε κάποτε

...όποιος επιστήμονας δεν μπορούσε να εξηγήσει σε ένα οκτάχρονο τι έκανε ήταν τσαρλατάνος.

Αποδεικνύεται ότι ήταν ο Kurt Vonnegut.

Δεν επιδίωξα να αποδείξω αυτή τη δήλωση. Προσπάθησα να διαψεύσω την βλακεία μου.

Ας υποθέσουμε ότι έχουμε δύο πίνακες αριθμών, ταξινομημένους με αύξουσα σειρά.

Int a1 = new int (21, 23, 24, 40, 75, 76, 78, 77, 900, 2100, 2200, 2300, 2400, 2500); int a2 = νέο int (10, 11, 41, 50, 65, 86, 98, 101, 190, 1100, 1200, 3000, 5000);
Είναι απαραίτητο να τα συγχωνεύσετε σε έναν διατεταγμένο πίνακα.

Int a3 = new int;
Αυτή είναι μια εργασία για ταξινόμηση συγχώνευσης.

Τι είναι αυτό; Υπάρχει μια απάντηση στο Διαδίκτυο, υπάρχει μια περιγραφή του αλγορίθμου, αλλά δεν το κατάλαβα σε μια συνεδρίαση και αποφάσισα να το καταλάβω μόνος μου. Για να γίνει αυτό, πρέπει να κατανοήσετε τη βασική αρχή του αλγορίθμου, ώστε να μπορείτε να δημιουργήσετε ξανά τον αλγόριθμο από τη μνήμη σε σχέση με το πρόβλημά σας.

Ας ξεκινήσουμε για την υγεία

Ας πάμε σταδιακά και ας χρησιμοποιήσουμε αυτό που υπάρχει στην επιφάνεια: θα πάρουμε ένα στοιχείο από κάθε πίνακα ένα προς ένα, θα τα συγκρίνουμε και θα τα «συγχωνεύσουμε» σε έναν πίνακα. Θα τοποθετήσουμε πρώτα το μικρότερο στοιχείο, δεύτερο το μεγαλύτερο. Μετά το πρώτο πέρασμα όλα είναι καλά:

10, 21
Και μετά το δεύτερο πέρασμα δεν είναι τόσο καλό:

10, 21, 11, 23
Είναι σαφές ότι πρέπει να συγκρίνουμε τα στοιχεία με αυτά που έχουν ήδη προστεθεί.

Ας ξεκινήσουμε πάλι

Ας έχουμε ένα συγκεκριμένο προσωρινό buffer στοιχείων σε σύγκριση σε κάθε βήμα. Μετά το πρώτο πέρασμα, θα περιέχει 21 και 10. Μετά τη σύγκριση, θα μετακινήσουμε το μικρότερο στοιχείο 10 από το buffer στον πίνακα που προκύπτει και θα αφήσουμε το μεγαλύτερο στοιχείο 21, γιατί δεν ξέρουμε τι θα βρίσκεται πίσω από αυτό.

Μετά το δεύτερο πέρασμα, το buffer θα περιέχει 21, 23 και 11. Δεν είναι σαφές τι να κάνετε με αυτά, μπορείτε να συγκρίνετε περισσότερα από δύο στοιχεία, αλλά δεν είναι τόσο εύκολο.

Ας συμφωνήσουμε στη συνέχεια ότι θα πάρουμε ένα στοιχείο από κάθε πίνακα σε αυτό το buffer. Δεδομένου ότι είναι πιο εύκολο να συγκρίνουμε δύο στοιχεία μεταξύ τους και γενικά έχουμε δύο οντότητες - δύο πίνακες. Στη συνέχεια, μετά το δεύτερο πέρασμα θα υπάρχουν 21 και 11 στο buffer, επειδή ο "αντιπρόσωπος" του πρώτου πίνακα είναι ήδη στο buffer - είναι 21. Τα συγκρίνουμε και στέλνουμε τον μικρότερο στον πίνακα που προκύπτει. Στη συνέχεια, μετά το δεύτερο πέρασμα θα έχουμε στον πίνακα που προκύπτει:

10, 11
Και στο buffer - 21.

Στο τρίτο πέρασμα, παίρνουμε 41 από τον δεύτερο πίνακα στο buffer, επειδή ο "αντιπρόσωπος" του πρώτου πίνακα παραμένει στο buffer. Συγκρίνουμε το 21 και το 41 και τελικά αφαιρούμε το 21 από το buffer.

Μετά το τρίτο πέρασμα θα έχουμε στον πίνακα που προκύπτει:

10, 11, 21
Στο τέταρτο πέρασμα θα συγκρίνουμε δύο τιμές από το buffer - 41 και 23. Ο πίνακας που προκύπτει θα περιέχει:

10, 11, 21, 23
Δηλαδή, μόνο τώρα - στο τέταρτο, και όχι στο δεύτερο πέρασμα - το αποτέλεσμα αποδείχθηκε σωστό. Αποδεικνύεται ότι σε έναν βρόχο πρέπει να θυμάστε τον τρέχοντα δείκτη για κάθε πίνακα και ο ίδιος ο βρόχος μπορεί να είναι όσο το άθροισμα των μηκών των πινάκων.

Φτάνουμε στο τέλος, αλλά ξαφνικά

Τι θα κάνουμε όταν ο πίνακας που προκύπτει αποτελείται από:

10, 11, 21, 23, 24, 40, 41, 50, 65, 75, 76, 78, 77, 86, 98, 101, 190, 900, 1100, 1200, 2100, 2200, 2300, 2400, 2500,
Το buffer θα περιέχει 3000 από τον δεύτερο πίνακα και στον πρώτο - θα εξαντληθούν όλα τα στοιχεία; Εφόσον οι πίνακες μας είναι ταξινομημένοι, παίρνουμε απλώς 3000 από το buffer και τα υπόλοιπα 5000. Δηλαδή, πρέπει να ελέγξουμε για κάθε ευρετήριο για να δούμε αν έχουμε υπερβεί τον αριθμό των στοιχείων σε κάθε πίνακα.

Ας περιπλέκουμε το έργο

Τι γίνεται αν έχουμε μη ταξινομημένους πίνακες; Συνήθως η εργασία καταλήγει στην ταξινόμηση ενός πίνακα. Στη συνέχεια, μπορεί να χρησιμοποιηθεί και η ταξινόμηση συγχώνευσης.

Αφήστε τον πρώτο πίνακα (για παράδειγμα, να πάρει πολλά στοιχεία από αυτόν) να έχει την ακόλουθη διάταξη στοιχείων:

2100, 23, 40, 24, 2, 1.
Θα το ταξινομήσουμε. Επειδή είναι ευκολότερο να συγκρίνουμε δύο στοιχεία τη φορά, ας χωρίσουμε τον πίνακα εξίσου στα δύο:

2150, 23, 40
Και
24, 2, 1.
Θα λάβετε τρία στοιχεία. Πολοί! Ας χωρίσουμε κάθε πίνακα εξίσου, παίρνουμε τέσσερις πίνακες:

2100, 23 40 24, 2 1
Ας ταξινομήσουμε τώρα κάθε έναν από τους πίνακες συγκρίνοντας απλώς το πρώτο και το δεύτερο στοιχείο (όπου υπάρχουν):

23, 2100 40 2, 24 1
Και θα το συγχωνεύσουμε ξανά σύμφωνα με τον προηγούμενο αλγόριθμο - μέσω ενός buffer. Μετά την πρώτη συγχώνευση παίρνουμε δύο πίνακες:

23, 40, 2100 1, 2, 24
Και συγχωνευόμαστε ξανά - σε έναν πίνακα:

1, 2, 23, 24, 40, 2100
Έτσι συγχωνεύουμε ταξινομημένο τον πίνακα.

Κατώτατη γραμμή

Έτσι, η ταξινόμηση συγχώνευσης περιλαμβάνει τη διαίρεση του πίνακα εξίσου έως ότου ένας πίνακας παράγει πολλά μικρά - όχι περισσότερα από δύο στοιχεία σε μέγεθος. Τα δύο στοιχεία μπορούν εύκολα να συγκριθούν μεταξύ τους και να τακτοποιηθούν ανάλογα με την απαίτηση: αύξουσα ή φθίνουσα.

Μετά τον διαχωρισμό, ακολουθεί μια αντίστροφη συγχώνευση, στην οποία σε μια χρονική στιγμή (ή ανά πέρασμα κύκλου) επιλέγεται ένα στοιχείο από κάθε πίνακα και συγκρίνεται μεταξύ τους. Το μικρότερο (ή μεγαλύτερο) στοιχείο αποστέλλεται στον πίνακα που προκύπτει, το υπόλοιπο στοιχείο παραμένει σχετικό για σύγκριση με ένα στοιχείο από άλλο πίνακα στο επόμενο βήμα.

Ας το εκφράσουμε σε κώδικα (Java)

Ένα παράδειγμα ταξινόμησης σε αύξουσα σειρά δύο ταξινομημένων πινάκων:

Int a1 = new int (21, 23, 24, 40, 75, 76, 78, 77, 900, 2100, 2200, 2300, 2400, 2500); int a2 = νέο int (10, 11, 41, 50, 65, 86, 98, 101, 190, 1100, 1200, 3000, 5000);< a2[j]) { int a = a1[i]; a3[k] = a; i++; } else { int b = a2[j]; a3[k] = b; j++; } }
int a3 = νέο int;

int i=0, j=0;
για (int k=0; k
a1.length-1) ( int a = a2[j]; a3[k] = a; j++; ) αλλιώς εάν (j > a2.length-1) (int a = a1[i]; a3[k] = a;

Εδώ:

A1 και a2 – πίνακες που πρέπει να συγχωνευθούν.

a3 – πίνακας που προκύπτει.

Τα i και j είναι δείκτες για τους πίνακες a1 και a2, αντίστοιχα, που δείχνουν τα τρέχοντα στοιχεία σε κάθε βήμα και σχηματίζουν την ίδια προσωρινή μνήμη.<= lo) return; int mid = lo + (hi - lo) / 2; SortUnsorted(a, lo, mid); SortUnsorted(a, mid + 1, hi); int buf = Arrays.copyOf(a, a.length); for (int k = lo; k <= hi; k++) buf[k] = a[k]; int i = lo, j = mid + 1; for (int k = lo; k <= hi; k++) { if (i >Οι δύο πρώτες συνθήκες ελέγχουν ότι τα ευρετήρια δεν υπερβαίνουν τον αριθμό των στοιχείων στους πίνακες. Η τρίτη και η τέταρτη συνθήκη διασφαλίζουν ότι το μικρότερο στοιχείο από τον πρώτο και τον δεύτερο πίνακα μετακινείται στον πίνακα, αντίστοιχα.< buf[i]) { a[k] = buf[j]; j++; } else { a[k] = buf[i]; i++; } } }
int a3 = νέο int;

Συνάρτηση ταξινόμησης συγχώνευσης
Ας μορφοποιήσουμε τον παραπάνω κώδικα ως αναδρομική συνάρτηση που θα διαχωρίζει τους πίνακες όσο το δυνατόν περισσότερο, με παραμέτρους που αντιστοιχούν σε ολόκληρο τον πίνακα στην πρώτη κλήση, τα μισά του στη δεύτερη και τρίτη κλήση κ.λπ.
Private void SortUnsorted(int a, int lo, int hi) ( if (hi

Όπως είδαμε με τη γρήγορη ταξινόμηση, οι περισσότεροι αναδρομικοί αλγόριθμοι μπορούν να βελτιωθούν με το χειρισμό μικρών αρχείων με έναν ειδικό τρόπο. Λόγω της αναδρομικής φύσης τους, οι συναρτήσεις καλούνται συχνά ειδικά για μικρά αρχεία, επομένως η βελτίωση της επεξεργασίας τους οδηγεί σε βελτιώσεις σε ολόκληρο τον αλγόριθμο. Επομένως, όπως συμβαίνει με τη γρήγορη ταξινόμηση, η μετάβαση σε ταξινόμηση εισαγωγής σε μικρά μεγέθη υποαρχείων θα βελτιώσει τον χρόνο εκτέλεσης μιας τυπικής εφαρμογής ταξινόμησης συγχώνευσης κατά 10-15%.

Η επόμενη χρήσιμη βελτίωση είναι η εξάλειψη του χρόνου που απαιτείται για την αντιγραφή δεδομένων στον βοηθητικό πίνακα που χρησιμοποιείται από τη συγχώνευση. Για να γίνει αυτό, οι αναδρομικές κλήσεις θα πρέπει να οργανωθούν με τέτοιο τρόπο ώστε σε κάθε επίπεδο η διαδικασία υπολογισμού να αλλάζει τους ρόλους των συστοιχιών εισόδου και των βοηθητικών συστοιχιών. Ένας τρόπος για την υλοποίηση αυτής της προσέγγισης είναι η δημιουργία δύο εκδόσεων των προγραμμάτων: μία για τα δεδομένα εισόδου στον πίνακα aux και τα δεδομένα εξόδου στον πίνακα a, και η άλλη για τα δεδομένα εισόδου στον πίνακα a και τα δεδομένα εξόδου στον πίνακα aux array, και οι δύο αυτές εκδόσεις καλούν η μία την άλλη με τη σειρά τους. Μια άλλη προσέγγιση παρουσιάζεται στο Πρόγραμμα 8.4, το οποίο αρχικά δημιουργεί ένα αντίγραφο του πίνακα εισόδου και στη συνέχεια χρησιμοποιεί το Πρόγραμμα 8.1 και αλλάζει τα ορίσματα στις αναδρομικές κλήσεις, εξαλείφοντας έτσι τη λειτουργία της ρητής αντιγραφής του πίνακα. Αντίθετα, το πρόγραμμα εναλλάσσει την έξοδο του αποτελέσματος συγχώνευσης είτε σε βοηθητικό αρχείο είτε σε αρχείο εισόδου. (Αυτό είναι ένα πολύ έξυπνο πρόγραμμα.)

Πρόγραμμα 8.4. Συγχώνευση ταξινόμησης χωρίς αντιγραφή

Αυτό το αναδρομικό πρόγραμμα ταξινομεί τον πίνακα b, τοποθετώντας το αποτέλεσμα της ταξινόμησης στον πίνακα a. Επομένως, οι αναδρομικές κλήσεις γράφονται έτσι ώστε τα αποτελέσματά τους να αποθηκεύονται στον πίνακα b και το Πρόγραμμα 8.1 χρησιμοποιείται για τη συγχώνευσή τους στον πίνακα a. Έτσι, όλες οι μεταφορές δεδομένων εκτελούνται κατά τις συγχωνεύσεις.

περίγραμμα void mergesortABr(Item a, Item b, int l, int r) ( if (r-l<= 10) { insertion(a, l, r); return; } int m = (l+r)/2; mergesortABr(b, a, l, m); mergesortABr(b, a, m+1, r); mergeAB(a+l, b+l, m-l+1, b+m+1, r-m); } template void mergesortAB(Item a, int l, int r) ( static Item aux; for (int i = l; i<= r; i++) aux[i] = a[i]; mergesortABr(a, aux, l, r); }

Αυτή η μέθοδος σάς επιτρέπει να αποφύγετε την αντιγραφή του πίνακα με το κόστος της επιστροφής στον εσωτερικό βρόχο ελέγχου για εξάντληση των αρχείων εισόδου. (Θυμηθείτε ότι η εξάλειψη αυτών των ελέγχων στο Πρόγραμμα 8.2 μετέτρεψε αυτό το αρχείο σε bitonic κατά τον χρόνο αντιγραφής.) Η κατάσταση μπορεί να αποκατασταθεί με μια αναδρομική υλοποίηση της ίδιας ιδέας: κάποιος θα εφαρμόσει δύο προγράμματα τόσο για συγχώνευση όσο και για ταξινόμηση συγχώνευσης: ένα για έξοδο του πίνακα με αύξουσα, και το άλλο για να εμφανίζει τον πίνακα με φθίνουσα σειρά. Αυτό επιτρέπει στη στρατηγική bitonic να χρησιμοποιηθεί ξανά και εξαλείφει την ανάγκη για κλειδιά σήματος στον εσωτερικό βρόχο.

Δεδομένου ότι χρησιμοποιεί τέσσερα αντίγραφα των βασικών προγραμμάτων και συνεστραμμένη αναδρομική εναλλαγή ορισμάτων, μια τέτοια υπερ-βελτιστοποίηση μπορεί να συνιστάται μόνο για ειδικούς (ή φοιτητές), αλλά εξακολουθεί να επιταχύνει σημαντικά την ταξινόμηση συγχώνευσης. Τα πειραματικά αποτελέσματα, τα οποία θα συζητηθούν στην Ενότητα 8.6, δείχνουν ότι ο συνδυασμός όλων των βελτιώσεων που προτείνονται παραπάνω καθιστά τη συγχώνευση γρηγορότερη κατά 40 τοις εκατό, αλλά εξακολουθεί να είναι 25 τοις εκατό πιο αργή από τη γρήγορη ταξινόμηση. Αυτές οι μετρήσεις διαφέρουν ανάλογα με την εφαρμογή και τη μηχανή, αλλά τα αποτελέσματα είναι παρόμοια σε διαφορετικές καταστάσεις.

Άλλες υλοποιήσεις συγχώνευσης που χρησιμοποιούν ρητούς ελέγχους εξάντλησης του πρώτου αρχείου ενδέχεται να οδηγήσουν σε περισσότερες (αλλά όχι πολύ) αξιοσημείωτες διακυμάνσεις στο χρόνο εκτέλεσης ανάλογα με τη φύση των δεδομένων εισόδου. Για αρχεία με τυχαία παραγγελία, μόλις εξαντληθεί ένα υποαρχείο, το μέγεθος του άλλου υποαρχείου θα είναι μικρό και το κόστος προώθησης στο βοηθητικό αρχείο εξακολουθεί να είναι ανάλογο με το μέγεθος αυτού του υποαρχείου. Μπορείτε επίσης να προσπαθήσετε να βελτιώσετε την απόδοση της ταξινόμησης συγχώνευσης σε περιπτώσεις όπου το αρχείο είναι σε μεγάλο βαθμό οργανωμένο και να παραλείψετε την κλήση συγχώνευσης όταν το αρχείο είναι πλήρως οργανωμένο, αλλά για πολλούς τύπους αρχείων αυτή η στρατηγική είναι αναποτελεσματική.

Γυμνάσια

8.16. Υλοποιήστε μια αφηρημένη συγχώνευση ανταλλαγής που χρησιμοποιεί πρόσθετη μνήμη ανάλογη με το μέγεθος του μικρότερου συγχωνευμένου αρχείου. (Αυτή η μέθοδος θα πρέπει να μειώσει τις απαιτήσεις μνήμης της ταξινόμησης στο μισό.)

8.17. Εκτελέστε ταξινόμηση συγχώνευσης σε μεγάλα τυχαία ταξινομημένα αρχεία και προσδιορίστε πειραματικά το μέσο μήκος ενός άλλου υποαρχείου τη στιγμή που εξαντλείται το πρώτο υποαρχείο ως συνάρτηση του N (το άθροισμα των μηκών των δύο υποαρχείων που συγχωνεύονται).

8.18. Ας υποθέσουμε ότι το Πρόγραμμα 8.3 έχει τροποποιηθεί έτσι ώστε να μην καλεί τη μέθοδο συγχώνευσης όταν ένα [m]< a . Сколько сравнений экономится в этом случае, если сортируемый файл уже упорядочен?

8.19. Εκτελέστε τον τροποποιημένο αλγόριθμο που προτείνεται στην Άσκηση 8.18 για μεγάλα, τυχαία ταξινομημένα αρχεία. Προσδιορίστε πειραματικά τον μέσο αριθμό φορών που χάνεται μια κλήση συγχώνευσης ως συνάρτηση του N (το μέγεθος του αρχικού αρχείου που ταξινομείται).

8.20. Ας υποθέσουμε ότι μια ταξινόμηση συγχώνευσης πρέπει να εκτελεστεί σε ένα αρχείο με ταξινόμηση h για μια μικρή τιμή h. Ποιες αλλαγές πρέπει να γίνουν στη ρουτίνα συγχώνευσης για να αξιοποιηθεί αυτή η ιδιότητα των δεδομένων εισόδου; Πειραματιστείτε με υβρίδια ταξινόμησης και συγχώνευσης Shell βάσει αυτής της ρουτίνας.

8.21. Σχεδιάστε μια υλοποίηση συγχώνευσης που μειώνει την πρόσθετη απαίτηση μνήμης στο max(M, N/M) χρησιμοποιώντας την ακόλουθη ιδέα. Διαχωρίστε τον πίνακα σε N/M μπλοκ μεγέθους M (για απλότητα, ας υποθέσουμε ότι το N είναι πολλαπλάσιο του M). Στη συνέχεια, (1) αντιμετωπίζετε αυτά τα μπλοκ ως εγγραφές των οποίων τα πρώτα κλειδιά είναι κλειδιά ταξινόμησης, ταξινομήστε τα χρησιμοποιώντας ταξινόμηση επιλογής και (2) επαναλάβετε τον πίνακα, συγχωνεύοντας το πρώτο μπλοκ με το δεύτερο, μετά το δεύτερο μπλοκ με το τρίτο και ούτω καθεξής επί.

8.22. Αποδείξτε ότι η μέθοδος που περιγράφεται στην Άσκηση 8.21 εκτελείται σε γραμμικό χρόνο.

8.23. Εφαρμογή bitonic merge sort χωρίς αντιγραφή.

Αύξουσα ταξινόμηση συγχώνευσης

Όπως συζητήθηκε στο Recursion and Trees, κάθε αναδρομικό πρόγραμμα έχει ένα μη αναδρομικό αντίστοιχο που, αν και εκτελεί ισοδύναμες ενέργειες, μπορεί να το κάνει με διαφορετική σειρά. Οι μη αναδρομικές υλοποιήσεις της ταξινόμησης συγχώνευσης αξίζει να διερευνηθούν λεπτομερώς ως παραδείγματα της φιλοσοφίας των αλγορίθμων διαίρει και βασίλευε.

Εξετάστε μια ακολουθία συγχωνεύσεων που εκτελούνται από έναν αναδρομικό αλγόριθμο. Από το παράδειγμα που φαίνεται στο Σχ.

8.2, μπορείτε να δείτε ότι ένα αρχείο μεγέθους 15 ταξινομείται με την ακόλουθη σειρά συγχωνεύσεων:

1-and-1 1-and-1 2-and-2 1-and-1 1-and-1 2-and-2 4-and-4

1-και-1 1-και-1 2-και-2 1-και-1 2-και-1 4-και-3 8-και-7.

Η σειρά με την οποία εκτελούνται οι συγχωνεύσεις καθορίζεται από την αναδρομική δομή του αλγορίθμου. Ωστόσο, τα υποαρχεία επεξεργάζονται ανεξάρτητα, επομένως οι συγχωνεύσεις μπορεί να πραγματοποιηθούν με διαφορετική σειρά. Στο Σχ.

Το Σχήμα 8.4 δείχνει μια στρατηγική από κάτω προς τα πάνω στην οποία η ακολουθία των συγχωνεύσεων είναι η εξής:


1-and-1 1-and-1 1-and-1 1-and-1 1-and-1 1-and-1 1-and-1

Κάθε γραμμή δείχνει το αποτέλεσμα της κλήσης της μεθόδου συγχώνευσης κατά την εκτέλεση μιας αύξουσας ταξινόμησης συγχώνευσης. Πρώτον, πραγματοποιούνται συγχωνεύσεις 1-και-1: η συγχώνευση των A και S παράγει A S. κατά τη συγχώνευση O και R, το αποτέλεσμα είναι O R, κ.λπ. Λόγω του περίεργου μεγέθους του αρχείου, το τελευταίο Ε δεν συμμετέχει στη συγχώνευση. Το δεύτερο πέρασμα εκτελεί συγχωνεύσεις 2-και-2: Το A S συγχωνεύεται με το O R για να παραχθεί A O R S, και ούτω καθεξής, μέχρι την τελευταία συγχώνευση 2-και-1. Μετά από αυτό, εκτελούνται οι συγχωνεύσεις 4-και-4, 4-και-3 και τελικές 8-και-7.

Και στις δύο περιπτώσεις, επτά συγχωνεύσεις 1-και-1, τρεις συγχωνεύσεις 2-και-2 και μία η κάθε μία 2-και-1, 4-και-4, 4-και-3 και 8-και-7, αλλά εκτελούνται με διαφορετική σειρά. Η στρατηγική από κάτω προς τα πάνω προτείνει τη συγχώνευση των μικρότερων από τα υπόλοιπα αρχεία, διασχίζοντας τη διάταξη από αριστερά προς τα δεξιά.

Η ακολουθία των συγχωνεύσεων που εκτελούνται από τον αναδρομικό αλγόριθμο καθορίζεται από το δέντρο διαίρει και βασίλευε που φαίνεται στο Σχήμα.

Είδαμε σε πολλά παραδείγματα στο Recursion and Trees ότι ο συλλογισμός από κάτω προς τα πάνω έχει νόημα να μετατοπίσει τη σκέψη προς μια στρατηγική ενώνει και κατακτά, όπου μικρά υποπροβλήματα λύνονται πρώτα και μετά συνδυάζονται για να λύσουν ένα μεγαλύτερο πρόβλημα. Συγκεκριμένα, μια μη αναδρομική έκδοση της έκδοσης "merge-and-conquer" της ταξινόμησης συγχώνευσης στο Πρόγραμμα 8.5 λαμβάνεται ως εξής: πρώτα, όλα τα στοιχεία ενός αρχείου αντιμετωπίζονται ως διατεταγμένες υπολίστες μήκους 1. Στη συνέχεια 1-και- Πραγματοποιούνται 1 συγχωνεύσεις σε αυτές, με αποτέλεσμα διατεταγμένες υπολίστες μεγέθους 2, στη συνέχεια εκτελούνται μια σειρά από συγχωνεύσεις 2 και 2, με αποτέλεσμα ταξινομημένες υπολίστες μεγέθους 4 και ούτω καθεξής μέχρι να παραγγελθεί ολόκληρη η λίστα. Εάν το μέγεθος του αρχείου δεν είναι δύναμη 2, τότε η τελευταία υπολίστα δεν έχει πάντα το ίδιο μέγεθος με όλες τις άλλες, αλλά εξακολουθεί να μπορεί να συγχωνευθεί.

Εάν το μέγεθος του αρχείου είναι δύναμη 2, τότε το σύνολο των συγχωνεύσεων που εκτελούνται από την αύξουσα ταξινόμηση συγχώνευσης είναι ακριβώς το ίδιο με το σύνολο των συγχωνεύσεων που εκτελούνται από την αναδρομική ταξινόμηση συγχώνευσης, αλλά η ακολουθία των συγχωνεύσεων θα είναι διαφορετική. Η αύξουσα ταξινόμηση συγχώνευσης αντιστοιχεί σε μια διάσχιση δέντρου διαίρει και βασίλευε, επίπεδο προς επίπεδο, από κάτω προς τα πάνω. Αντίθετα, ο αναδρομικός αλγόριθμος ονομάζεται ταξινόμηση συγχώνευσης προς τα κάτω επειδή διασχίζει το δέντρο προς τα πίσω από πάνω προς τα κάτω.

Εάν το μέγεθος του αρχείου δεν είναι δύναμη 2, ο αλγόριθμος από κάτω προς τα πάνω παράγει ένα άλλο σύνολο συγχωνεύσεων, όπως φαίνεται στο Σχήμα.


8.5. Ο αλγόριθμος από κάτω προς τα πάνω αντιστοιχεί σε ένα δέντρο ενώνει και κατακτά (βλ. Άσκηση 5.75), το οποίο είναι διαφορετικό από το δέντρο διαίρει και βασίλευε που αντιστοιχεί στον αλγόριθμο από πάνω προς τα κάτω. Είναι δυνατόν να γίνει η ακολουθία των συγχωνεύσεων που εκτελούνται με την αναδρομική μέθοδο ίδια με τη μη αναδρομική μέθοδο, αλλά δεν υπάρχει ιδιαίτερος λόγος για αυτό, καθώς η διαφορά στην απόδοση είναι μικρή σε σχέση με το συνολικό κόστος.

Ρύζι. 8.5.

Εάν το μέγεθος του αρχείου δεν είναι δύναμη 2, τότε οι δομές συγχώνευσης για την αύξουσα ταξινόμηση συγχώνευσης είναι εντελώς διαφορετικές από τις δομές συγχώνευσης για την φθίνουσα ταξινόμηση (Εικόνα 8.3). Στην αύξουσα ταξινόμηση, όλα τα αρχεία (εκτός ίσως από το τελευταίο) έχουν μέγεθος ισχύος 2. Αυτές οι διαφορές βοηθούν στην κατανόηση της βασικής δομής των αλγορίθμων, αλλά έχουν μικρή επίδραση στην απόδοση.

Τα Λήμματα 8.1-8.4 ισχύουν επίσης για αύξουσα ταξινόμηση συγχώνευσης και ισχύουν τα ακόλουθα πρόσθετα λήμματα:Όλες οι συγχωνεύσεις σε κάθε πέρασμα της αύξουσας ταξινόμησης συγχώνευσης χειρίζονται αρχεία των οποίων το μέγεθος είναι δύναμη 2, με πιθανή εξαίρεση το μέγεθος του τελευταίου αρχείου.

Αυτό το γεγονός είναι εύκολο να αποδειχθεί επαγωγικά.

Λήμμα 8.6.Ο αριθμός των περασμάτων σε μια αύξουσα ταξινόμηση συγχώνευσης σε ένα αρχείο με N στοιχεία είναι ακριβώς ίσος με τον αριθμό των bit στη δυαδική αναπαράσταση N (χωρίς αρχικά μηδενικά). Το μέγεθος των υπολιστών μετά από k περάσματα είναι 2 k , επειδή Κάθε πέρασμα της αύξουσας ταξινόμησης συγχώνευσης διπλασιάζει το μέγεθος των ταξινομημένων υποαρχείων. Αυτό σημαίνει ότι ο αριθμός των περασμάτων που απαιτούνται για την ταξινόμηση ενός αρχείου N στοιχείων είναι το μικρότερο k έτσι ώστε, το οποίο είναι ακριβώς ίσο με , δηλ. ο αριθμός των δυαδικών ψηφίων στη δυαδική αναπαράσταση N. Αυτό το αποτέλεσμα μπορεί να αποδειχθεί είτε με επαγωγή είτε με ανάλυση των δομικών ιδιοτήτων των δέντρων unite-and-conquer. ¦

Πρόγραμμα 8.5. Αύξουσα ταξινόμηση συγχώνευσης

Η αύξουσα ταξινόμηση συγχώνευσης αποτελείται από μια ακολουθία περασμάτων σε ολόκληρο το αρχείο, εκτελώντας συγχωνεύσεις m-and-m και διπλασιάζοντας τα m σε κάθε πέρασμα. Το τελευταίο υποαρχείο έχει μέγεθος m μόνο εάν το μέγεθος του αρχείου είναι άρτιο πολλαπλάσιο του m, επομένως η τελευταία συγχώνευση είναι τύπου m-and-x, για μερικά x μικρότερη ή ίση με m.

Συνοψίζοντας, η φθίνουσα και η αύξουσα ταξινόμηση είναι δύο απλοί αλγόριθμοι ταξινόμησης που βασίζονται στη λειτουργία της συγχώνευσης δύο ταξινομημένων υποαρχείων σε ένα προκύπτον συγχωνευμένο ταξινομημένο αρχείο. Και οι δύο αλγόριθμοι είναι στενά συνδεδεμένοι και εκτελούν ακόμη και το ίδιο σύνολο συγχωνεύσεων εάν το μέγεθος του αρχείου προέλευσης είναι δύναμη 2, αλλά σε καμία περίπτωση δεν είναι πανομοιότυποι. Στο Σχ.


Το 8.7 δείχνει τις διαφορές στα δυναμικά χαρακτηριστικά των αλγορίθμων χρησιμοποιώντας το παράδειγμα ενός μεγάλου αρχείου. Κάθε αλγόριθμος μπορεί να χρησιμοποιηθεί στην πράξη εάν η εξοικονόμηση μνήμης δεν είναι σημαντική και εάν επιθυμείτε εγγυημένο χρόνο εκτέλεσης στη χειρότερη περίπτωση. Και οι δύο αλγόριθμοι παρουσιάζουν ενδιαφέρον ως πρωτότυπα των γενικών αρχών κατασκευής αλγορίθμων: «διαίρει και βασίλευε» και «ενώσου και βασίλευε».

Ρύζι. 8.7.

Γυμνάσια

8.24. Δείξτε τι συγχωνεύει η ταξινόμηση συγχώνευσης από κάτω προς τα πάνω (Πρόγραμμα 8.5) για τα πλήκτρα E A S Y Q U E S T I O N .

8.25. Εφαρμόστε μια αύξουσα ταξινόμηση συγχώνευσης που ξεκινά με ένα είδος εισαγωγής μπλοκ στοιχείων Μ. Προσδιορίστε εμπειρικά την τιμή του M για το οποίο το αναπτυγμένο πρόγραμμα ταξινομεί πιο γρήγορα τυχαία ταξινομημένα αρχεία N στοιχείων, με N = 10 3, 10 4, 10 5 και 10 6.

8.26. Σχεδιάστε δέντρα που αντιπροσωπεύουν τις συγχωνεύσεις που εκτελούνται από το Πρόγραμμα 8.5 για N = 16, 24, 31, 32, 33 και 39.

8.27. Γράψτε ένα πρόγραμμα αναδρομικής ταξινόμησης συγχώνευσης που εκτελεί τις ίδιες συγχωνεύσεις με τη συγχώνευση από κάτω προς τα πάνω.

8.28. Γράψτε ένα πρόγραμμα αύξουσας ταξινόμησης συγχώνευσης που εκτελεί τις ίδιες συγχωνεύσεις με μια φθίνουσα ταξινόμηση συγχώνευσης. (Αυτή η άσκηση είναι πολύ πιο δύσκολη από την άσκηση 8.27.)

8.29. Ας υποθέσουμε ότι το μέγεθος του αρχείου είναι δύναμη 2. Καταργήστε την αναδρομή από την φθίνουσα ταξινόμηση συγχώνευσης, έτσι ώστε να λάβετε μια μη αναδρομική ταξινόμηση συγχώνευσης που εκτελεί την ίδια ακολουθία συγχωνεύσεων.

8.30. Αποδείξτε ότι ο αριθμός των περασμάτων που εκτελούνται από την προς τα κάτω ταξινόμηση συγχώνευσης είναι επίσης ίσος με τον αριθμό των bit στη δυαδική αναπαράσταση του N (βλ. Λήμμα 8.6).



Συνιστούμε να διαβάσετε

Κορυφή