Κατέβασμα παρουσίασης
Η παρουσίαση φορτώνεται. Παρακαλείστε να περιμένετε
1
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Επανάληψη: Δομές Δεδομένων και Αλγόριθμοι Επαναληπτικό Υλικό Ενδιάμεσης Εξέτασης ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
2
Εργαλεία Εκτίμησης Πολυπλοκότητας
Ορισμός: Θεωρούμε συνάρτηση Τ(n). Ορίζουμε 1. Τ(n) Ο( f (n) ), αν υπάρχουν σταθερές c>0 και n0 1 ώστε Τ(n) cf(n), για κάθε n n0. 2. Τ(n) Ω( f(n) ), αν υπάρχουν σταθερές c>0 και n0 1 ώστε Τ(n)cf(n), για κάθε n n Τ(n) Θ( f(n) ), αν Τ(n)Ο(f(n)) και Τ(n)Ω(f(n)). Αν Τ(n)Ο(f (n)), τότε λέμε πως η συνάρτηση Τ είναι της τάξεως f(n). Αν Τ(n) Ω(f (n)), τότε λέμε πως η Τ είναι της τάξεως ωμέγα της f (n). Αν Τ(n) Θ(f (n)), τότε λέμε πως η Τ είναι της τάξεως θήτα της f (n). (λέγεται και ακριβής τάξη) f(n)μπορεί να είναι οποιαδήποτε συνάρτηση => n, n2, logn ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
3
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Παραδείγματα Βig-O Παράδειγμα 1 7n-3 είναι Ο(n) Τεκμηρίωση Πρέπει να δείξουμε ότι υπάρχει c>0,no1 έτσι ώστε: 7n-3 c*n, Για c=7, no 1 η παραπάνω ανισότητα ικανοποιείται, επομένως 7n-3 είναι Ο(n) π.χ., 7*1-3 7*1 (4 7) 7*2-3 7*2 (11 14) 7*3-3 7*3 (18 21) κοκ ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
4
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Παραδείγματα Βig-O Παράδειγμα 2 20n3+10nlogn+5 είναι Ο(n3) Τεκμηρίωση Πρέπει να δείξουμε ότι υπάρχει c>0,no 1 έτσι ώστε 20n3+10nlogn+5 c* n3 20n3+10nlogn+5 20n3+10n3+5n3 20n3+10n3+5n3 35n (n>1) Για c=35, no=1 η παραπάνω ανισότητα ικανοποιείται, επομένως 20n3+10nlogn+5 είναι Ο(n3) ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
5
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Ιδιότητες Big-O Στις περισσότερες περιπτώσεις μας ενδιαφέρει μόνο το άνω φράγμα (χείριστη περίπτωση εκτέλεσης ενός αλγορίθμου), δηλαδή το O. Η συνάρτηση O έχει τις ακόλουθες ιδιότητες: O(X+Y) = O(X) + O(Y) max (O(X),O(Y)) O(X*Y) = O(X)*O(Y) O(X * Y) O(c * X) = O(X) (c σταθερά > 0 ) Παράδειγμα Έστω ότι ένας αλγόριθμος έχει την ακόλουθη συνάρτηση αύξησης f(n) = 16n2logn + 3n + 7 Τότε απλοποιούμε την συνάρτηση αύξησης ως εξής = 16n2logn (διαγράφουμε τις μικρότερες τιμές του n – Κανόνας 1) = n2logn (διαγράφουμε τις σταθερές – Κανόνας 3) δηλαδή f(n) Ο(n2logn) Ισχύει η ακόλουθη σειρά 1< logn< n< nlogn< n2< n3< 2n< n!< nn “<“ είναι ασυμπτωτικά μικρότερο ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
6
Παραδείγματα Ιδιοτήτων Big-O
Παραδείγματα: 15n Ο(n) 200*(n+n7) Ο(n7) 1324 Ο(1) (n+n7)*m2 Ο(n7 m2) 5n2 O(n2) 5nlgn + 4 Ο(nlgn) Προφανώς, κατά την ανάλυση αλγορίθμων στόχος μας είναι αυτά τα όρια να είναι όσο το δυνατό πιο ακριβή. Εάν 7n-4 είναι Ο(n), τότε προφανώς 7n-4 είναι Ο(n2), Ο(n3), Ο(n4), …. Ωστόσο, οι χαρακτηρισμοί big-O πρέπει να είναι όσο το δυνατό πιο μικρής τάξης. (δηλαδή το Ο(n) είναι το πιο στενό άνω όριο). ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
7
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Παράδειγμα Ανάλυσης Υποθέστε ότι θέλετε να βρείτε την θέση κάποιου στοιχείου K σε μια λίστα Χ[] (μήκους n). int index( int X[],int n,int k){ 1 int i=0; 2 while (i < n) { 3 if (X[i]==k) // item found 4 return i; i+=1; } 6 return error; } Μέγεθος δεδομένων εισόδου: n Χειρότερη Περίπτωση: εξέταση όλων των στοιχείων (π.χ. ψάχνουμε το 5) t(n) = n => O(n) Βέλτιστη Περίπτωση: το στοιχείο βρίσκετε στην θέση 1 (π.χ. ψάχνουμε το 3) t(n) = => Ω(1) Άρα εκτός από το μέγεθος εισόδου και το ίδιο το στιγμιότυπο εισόδου παίζει κάποιο σημαντικό ρόλο στην ανάλυση κάποιου αλγορίθμου (η συνάρτησης) 7? => θέση 2 Χ 3 1 7 2 9 5 n ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
8
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάλεξη 05: Αφηρημένοι Τύποι Δεδομένων Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Αφηρημένοι Τύποι Δεδομένων (ΑΤΔ) - Οι ΑΤΔ Στοίβα και Ουρά - Υλοποίηση των ΑΤΔ Στοίβα και Ουρά ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
9
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Λίστες (συν.) Οι πιο σημαντικές πράξεις στον ορισμό ενός ΑΤΔ-λίστας είναι η εισαγωγή και η εξαγωγή κόμβων στα άκρα της λίστας. Με βάση την προδιαγραφή αυτών των πράξεων, διακρίνουμε δύο βασικούς τύπους λίστας που έχουν πολλές και σημαντικές εφαρμογές σε κλάδους επιστημών που χρησιμοποιούν υπολογιστικές μεθόδους. Είναι οι ακόλουθες: Η στοίβα (stack) που έχει μόνο ένα άκρο προσιτό για εισαγωγές και εξαγωγές κόμβων. (LIFO – Last In First Out) Η ουρά (queue) όπου γίνονται εισαγωγές στο ένα άκρο και εξαγωγές από το άλλο. (FIFO – First In First Out) Υπάρχουν και άλλοι ΑΤΔ-λίστας μικρότερης πρακτικής σημασίας, όπως: ουρά με δύο άκρα, πολλαπλή στοίβα, κλπ. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
10
ΑΤΔ Λίστα 3 : Κυκλική Ουρά
Για λόγους χώρου μνήμης μπορούμε να πραγματοποιήσουμε την ουρά με μια κυκλική διάταξη των λέξεων της μνήμης. Δηλαδή θα θεωρούμε ότι η περιοχή μνήμης δεν αρχίζει με τη λέξη Α[0] και τελειώνει με τη λέξη Α[n-1], αλλά ότι μετά την Α[n-1] ακολουθεί η Α[0]. Έτσι μετά από μια ακολουθία εισαγωγών και εξαγωγών η ουρά μας πιθανόν να έχει την πιο κάτω μορφή όπου θεωρούμε ότι η αρχή της ουράς βρίσκεται στη θέση k και το τέλος της ουράς στη θέση 4. k A[0] A[1] A[n-1] A[n-2] . . . ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
11
ΑΤΔ Λίστα 4 : Ουρές με Δύο Άκρα
Ο ΑΤΔ ‘ουρά με δύο άκρα είναι παρόμοιος με το ΑΤΔ ουρά, με τη διαφορά ότι έχει δύο άκρα και επιτρέπει εισαγωγές και εξαγωγές και στα δύο. Μια ουρά δύο άκρων ορίζεται ως μια λίστα συνοδευόμενη από τις πιο κάτω πράξεις: makeEmpty(), isEmpty() insert(x) εισήγαγε το στοιχείο x στο μπροστινό άκρο της Q eject() διέγραψε τον κόμβο στο πίσω άκρο της Q enQueue (x) εισήγαγε τον στοιχείο x στο πίσω μέρος της Q. deQueue() διέγραψε τον κόμβο στο μπροστινό άκρο της Q front(Q) δώσε τον κόμβο στο μπροστινό άκρο της Q rear(Q) δώσε τον κόμβο στο πίσω άκρο της Q ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
12
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάλεξη 06: Συνδεδεμένες Λίστες & Εφαρμογές Στοιβών και Ουρών Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Υλοποίηση ΑΤΔ με Συνδεδεμένες Λίστες - Εφαρμογή Στοιβών 1: Αναδρομικές συναρτήσεις - Εφαρμογή Στοιβών 2: Ισοζυγισμός Παρενθέσεων - Εφαρμογή Στοιβών 3: Αντίστροφος Πολωνικός Συμβολ. - Εφαρμογή Ουρών 1: Διερεύνηση κατά Πλάτος ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
13
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Συνδεδεμένες Λίστες Μία απλή συνδεδεμένη λίστα είναι μία συμπαγής δομή δεδομένων η οποία αποτελείται από μία ακολουθία κόμβων. Κάθε κόμβος αποτελείται από: Δεδομένα Δείκτη/Αναφορά προς τον επόμενο κόμβο της λίστας Επίσης μπορεί να χρησιμοποιηθεί μία βοηθητική δομή που να αποθηκεύει χρήσιμες πληροφορίες, π.χ., αναφορά στον πρώτο κόμβο, μέγεθος των στοιχείων, κτλ. data next node ADT data next data next data next Size=3 head NULL node node node ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
14
Στοίβα ως Συνδεδεμένη Λίστα
Μπορούμε να υλοποιήσουμε μία Στοίβα με μία απλά συνδεδεμένη λίστα Κάθε κόμβος αποτελείται από ένα στοιχείο (στοιχεία της στοίβας) και από ένα δείκτη (προς τον επόμενο κόμβο της στοίβας). Η κορυφή της στοίβας είναι ο πρώτος κόμβος της λίστας, Χρησιμοποιούμε μια μεταβλητή για να φυλάγουμε στοιχεία σχετικά με τη στοίβα π.χ. μέγεθος (size) και δείκτη προς την κορυφή της στοίβας (head) που βρίσκεται και το στοιχείο top. top STACK data next data next data next Size=3 head NULL node node node ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
15
Υλοποίηση Στοίβας με Συνδεδεμένη Λίστα
Με χρήση εσωτερικής κλάσης για τους κόμβους public class Stack<E> implements IStack<E> private class StackNode<E>{ private E obj; private StackNode<E> next; StackNode(E obj, StackNode<E> head) { this.obj = obj; this.next = head; } public E getElement(){ return this.obj; private StackNode<E> head; private int size; public Stack() { head = null; size=0; public boolean isEmpty(){ return this.size==0; public void makeEmpty(){ this.head = null; this.size=0; } public int size() { return this.size; public E top() { return isEmpty()? null : head.getElement(); public void push(E obj) { StackNode<E> newNode = new StackNode<E>(obj, head); this.head = newNode; size+=1; public void pop() { if (!isEmpty()) { this.head = this.head.next; size-=1; ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
16
Ουρά ως Συνδεδεμένη Λίστα
Μπορούμε να υλοποιήσουμε μία Ουρά με μία απλά συνδεδεμένη λίστα Κάθε κόμβος αποτελείται από ένα στοιχείο (στοιχεία της ουράς) και από ένα δείκτη (προς τον επόμενο κόμβο της ουράς). Το πρώτο στοιχείο αποθηκεύεται στον πρώτο κόμβο Κάθε νέο στοιχείο αποθηκεύεται στο πίσω μέρος Χρησιμοποιούμε μια μεταβλητή για να φυλάγουμε στοιχεία σχετικά με την ουρά π.χ., μέγεθος (size) και δείκτες προς την κορυφή της ουράς (head) ) που βρίσκεται και το στοιχείο front και προς το τελευταίο στοιχείο της ουράς (rear). front rear QUEUE data next data next data next Size=3 head NULL node node node ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
17
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάλεξη 07: Λίστες Ι – Υλοποίηση & Εφαρμογές Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Ευθύγραμμες Απλά Συνδεδεμένες Λίστες (εισαγωγή, εύρεση, διαγραφή) - Ευθύγραμμες Διπλά Συνδεδεμένες Λίστες - Σύγκριση Συνδεδεμένων Λιστών με Πίνακες ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
18
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάλεξη 08: Λίστες ΙΙ – Κυκλικές Λίστες Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Κυκλικές Απλά Συνδεδεμένες Λίστες - Κυκλικές Διπλά Συνδεδεμένες Λίστες ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
19
Κυκλικές Απλά Συνδεδεμένες Λίστες
Μια κυκλική λίστα μπορεί να παίξει το ρόλο της ουράς, της οποίας και τα δύο άκρα είναι προσιτά με τη βοήθεια ενός μόνο δείκτη. Το πίσω άκρο, δηλαδή το άκρο όπου γίνονται οι εισαγωγές, είναι αυτό που δείχνεται από τον δείκτη στη λίστα (rear). Άρα οι εξαγωγές γίνονται ακριβώς μετά από τον κόμβο που δείχνεται από τον δείκτη rear. C-LIST data next data next data next Size=3 rear head node node node ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
20
Το πρόβλημα του Josephus
Titus Flavius Josephus (37 – 100 μ. Χ.) N άτομα τοποθετούνται σε έναν κύκλο Αφαιρούμε το M-οστό άτομο, για κάποιο δοσμένο Μ (ξεκινώντας από το 1) Ξεκινώντας από Μ+1 αφαιρούμε πάλι το Μ-οστό άτομο κ.ο.κ. μέχρι να μείνει μόνο ένα άτομο Josephus(N,M): το άτομο που θα μείνει στο τέλος Josephus(9,5) 1 2 3 4 5 6 7 8 9 1 2 3 4 6 7 8 9 2 3 4 6 7 8 9 8 2 8 2 8 9 2 6 8 9 2 3 6 8 9 2 3 4 6 8 9 ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
21
Κυκλικά Απλά Συνδεδεμένης Λίστα (συν.)
Άμεση λύση με χρήση κυκλικής λίστας Κατασκευή λίστας N κόμβων Ο τελευταίος κόμβος δείχνει στον πρώτο Διατρέχουμε τη λίστα μέχρι να αδειάσει Κυκλική διάσχιση χωρίς ειδικό κώδικα Διαγράφουμε το M-οστό στοιχείο κάθε φορά Εύκολη αφαίρεση στοιχείων ακολουθώντας δείκτες Δεν υπάρχει αλλαγή στις δομές (κλάσεις), μόνο στις συναρτήσεις διαγραφής και εισαγωγής Αναθέτουμε στον τελευταίο κόμβο να δείχνει στον πρώτο ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
22
Το πρόβλημα του Josephus: Υλοποίηση
class Node { int val; Node next; Node(int v) { val = v; } } public class Josephus { public static void main(String[] args) { int N = 9; int M = 5; Node t = new Node(1); Node x = t; // t στην αρχή for (int i = 2; i <= N; i++) { x.next = new Node(i); x = x.next; x.next = t;// τελευταίος κόμβος δείχνει την αρχή while (x != x.next) { for (int i = 1; i < M; i++) System.out.println("Executing " + x.next.val); x.next = x.next.next; // διαγραφή System.out.println("Survivor is " + x.val); } } ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
23
Το πρόβλημα του Josephus: Υλοποίηση (συν.)
Γενικά μπορούμε να υλοποιούμε λίστες με πίνακες (δεν είναι πάντα βολικό όμως) Χρειαζόμαστε 2 πίνακες: val[i]: στοιχείο κόμβου i next[i]: δείκτης επόμενου κόμβου Διαγραφή κόμβου γίνεται με ενημέρωση του next[] next[x] = next[next[x]] ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
24
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάλεξη 09: Αλγόριθμοι Ταξινόμησης I Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Οι αλγόριθμοι ταξινόμησης: Α. SelectionSort – Ταξινόμηση με Επιλογή Β. InsertionSort – Ταξινόμηση με Εισαγωγή Γ. MergeSort – Ταξινόμηση με Συγχώνευση ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
25
A. Ταξινόμηση με Επιλογή - Selection Sort
επιλογή του ελάχιστου στοιχείου ανταλλαγή με το i-οστό στοιχείο (i είναι μια μεταβλητή που αυξάνεται κατά ένα). επανάληψη των βημάτων 1 και 2 για τα υπόλοιπα στοιχεία. Το ελάχιστο μεταξύ i στοιχείων μπορεί να βρεθεί με τη χρήση ενός while-loop, σε χρόνο Ο(i). Άρα ο χρόνος εκτέλεσης του Selection Sort είναι: ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
26
Ταξινόμηση με επιλογή: SelectionSort
SelectionSort: ο αλγόριθμος αυτός ταξινομεί μία λίστα με στοιχεία. Σε κάθε βήμα i βρίσκει το i-οστό πιο μικρό στοιχείο και το τοποθετεί στην θέση i. Παράδειγμα: int x = {4, 8, 2, 1, 3, 5}; swap 4 8 2 1 3 5 Βήμα 1 Βήμα 2 1 8 2 4 3 5 Βήμα 3 1 2 8 4 3 5 Βήμα 4 1 2 3 4 8 5 Βήμα 5 1 2 3 4 8 5 Βήμα 6 1 2 3 4 5 8 ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
27
Ταξινόμηση με επιλογή: SelectionSort (συν.)
Πρότυπο Συνάρτησης void selectionSort(int x[], //ο πίνακας int length) //το μέγεθος του x Αλγόριθμος Αρχικοποίησε μία μεταβλητή min_index η οποία θα αποθηκεύει τη θέση του ελάχιστου αριθμού Με ένα βρόγχο for που θα ελέγχει ένα ένα τα στοιχεία του πίνακα (i<length) κάνε τα εξής: Θέσε σαν min_index το i Ψάξε ένα-ένα τα στοιχεία με ένα δεύτερο εσωτερικό βρόγχο αρχίζοντας από j=i Αν το στοιχείο που βρίσκεται στη θέση j είναι μικρότερο από το στοιχείο που βρίσκεται στη θέση min_index τότε θέσε min_index = j Με την έξοδο από το βρόγχο, αντάλλαξε τα στοιχεία που βρίσκονται στη θέση i με το στοιχείο που βρίσκεται στη θέση min_index ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
28
SelectionSort: Παράδειγμα Εκτέλεσης
Θέση Αρχικός Πίνακας Με i= Με i= Με i= Με i= Με i= min_index Στοιχείο που θα εισαχθεί στην θέση i ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
29
SelectionSort: Υλοποίηση
void SelectionSort(int A[]) { int n = A.length; int min_index; int tmp; for(int i=0; i<n-1; i++){ min_index = i; for(int j=i+1; j<n; j++){ if(A[j]<A[min_index]) min_index=j; } tmp = A[i]; A[i] = A[min_index]; A[min_index]=tmp; Αρχικοποίηση θέσης μικρότερου στοιχείου Βρες το μικρότερο στοιχείο Αντάλλαξε το μικρότερο στοιχείο με το τρέχον στοιχείο στη θέση [i] ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
30
SelectionSort: Ανάλυση Χρόνου Εκτέλεσης
void SelectionSort(){ … for (i=0; i<n-1; i++){ for (j=i+1; j<n; j++){ sum++; } Εξωτερικός Βρόγχος Εσωτερικός Βρόγχος Εσωτερικός Βρόγχος: Εξωτερικός Βρόγχος: ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
31
B. Ταξινόμηση με Εισαγωγή - Insertion Sort
υποθέτουμε πως ο πίνακας A[0..(i-1)] είναι ταξινομημένος, εισάγουμε το στοιχείο Α[i] στην ακολουθία Α[0..(i-1)] στη σωστή θέση. Αυτό επιτυγχάνεται μετακινώντας όλα τα στοιχεία που είναι μεγαλύτερα του Α[i] μια θέση δεξιά. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
32
B. Ταξινόμηση με Εισαγωγή - Insertion Sort (συν.)
υποθέτουμε πως ο πίνακας A[0..(i-1)] είναι ταξινομημένος, εισάγουμε το στοιχείο Α[i] στην ακολουθία Α[0..(i-1)] στη σωστή θέση. Αυτό επιτυγχάνεται μετακινώντας όλα τα στοιχεία που είναι μεγαλύτερα του Α[i] μια θέση δεξιά. 1 2 3 ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
33
InsertionSort: Παράδειγμα Εκτέλεσης
Θέση Αρχικός Πίνακας Με i= Με i= Με i= Με i= Με i= min_index Στοιχείο που θα εισαχθεί στην θέση i ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
34
InsertionSort: Υλοποίηση
void InsertionSort(int A[]){ int n = x.length; int index, i, j; for (i=1; i < n; i++) { index = A[i]; for (j=i; j>0; j--){ if (A[j-1] <= index) { break; } A[j] = A[j-1]; } A[j] = index; Τρέχον στοιχείο εισαγωγής μετακίνηση στοιχείων προς δεξιά θέλουμε να μετακινήσουμε μόνο τα A[j-1] > index τοποθέτηση στοιχείου j=i index=9 A[j-1] ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
35
Insertion Sort: Παράδειγμα Εκτέλεσης 2
BEFORE:[8,4,8,43,3,5,2,] (Το κόκκινο δείχνει τα ταξινομημένα στοιχεία) i:1(index:4) >8 (“>” Δείχνει τις μετακινήσεις) [4,8,8,43,3,5,2,] (Νέος Πίνακας) i:2(index:8) (Τίποτα δεν μετακινείται) [4,8,8,43,3,5,2,] i:3(index:43) (Τίποτα δεν μετακινείται) i:4(index:3) >43>8>8>4 [3,4,8,8,43,5,2,] i:5(index:5) >43>8>8 [3,4,5,8,8,43,2,] i:6(index:2) >43>8>8>5>4>3 [2,3,4,5,8,8,43,] AFTER:[2,3,4,5,8,8,43,] ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
36
InsertionSort: Ανάλυση Χρόνου Εκτέλεσης
void InsertionSort(){ … for (i=1; i<n; i++){ for (j=i; j>0; j--){ sum++; } Εξωτερικός Βρόγχος Εσωτερικός Βρόγχος Εσωτερικός Βρόγχος: Εξωτερικός Βρόγχος: ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
37
Σύγκριση InsertionSort και SelectionSort
Υπάρχουν δυο κριτήρια: Βήματα (μέχρι να βρω ένα στοιχείο) και Μετακινήσεις (όταν το βρω, πόσα swap κάνω) . Ο αλγόριθμος Selection sort απαιτεί πάντα Ο(n²) βήματα (δεν είναι δυνατή η γρήγορη έξοδος από τους βρόχους), έτσι η βέλτιστη περίπτωση είναι η ίδια με τη χείριστη περίπτωση. Στον αλγόριθμο Insertion Sort, είναι δυνατό να βγούμε από το δεύτερο βρόχο γρήγορα. Στη βέλτιστη περίπτωση (ο πίνακας είναι ήδη ταξινομημένος), ο χρόνος εκτέλεσης είναι της τάξης Ω(n) βήματα. Παρά τούτου, το Selection Sort είναι πιο αποδοτικός αν κρίνουμε τους αλγόριθμους με βάση τον αριθμό των μετακινήσεων (swaps) που απαιτούν: το selection sort απαιτεί Ο(n) μετακινήσεις, το insertion sort, απαιτεί Ο(n²) μετακινήσεις (στη χείριστη περίπτωση όπου ο αρχικός πίνακας είναι ταξινομημένος σε φθίνουσα σειρά). ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
38
Γ. Ταξινόμηση με Συγχώνευση (Merge sort)
Η ταξινόμηση με συγχώνευση είναι διαδικασία διαίρει και βασίλευε ( Divide and Conquer: αναδρομική διαδικασία όπου το πρόβλημα μοιράζεται σε μέρη τα οποία λύνονται ξεχωριστά, και μετά οι λύσεις συνδυάζονται. ) Περιγραφή του Mergesort Διαίρεση: Αναδρομικά μοιράζουμε τον πίνακα στα δύο μέχρι να φτάσουμε σε πίνακες μεγέθους ένα (DIVIDE) Συγχώνευση: Ταξινομούμε αναδρομικά τους πίνακες αυτούς με την συγχώνευση γειτονικών πινάκων (χρησιμοποιώντας βοηθητικό πίνακα). (CONQUER) ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
39
MergeSort: Βασική Ιδέα
(#στοιχείων αριστερά) n=7 (#στοιχείων δεξιά) Divide (split) Conquer (merge) ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
40
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Συγχώνευση 2 Λιστών Υποθέστε ότι θέλετε να συγχωνεύσετε 2 ταξινομημένες λίστες L1, L2 και να δημιουργήσετε μια νέα ταξινομημένη λίστα TEMP Διαδικασία Τοποθέτησε τους δείκτες i, j στην κεφαλή κάθε λίστας. Διάλεξε το μικρότερο από την λίστα L1 και L2 και τοποθέτησε τον στον πίνακα TEMP στην θέση κ Προχώρησε τον δείκτη i (αν το μικρότερο στοιχείο ήταν από την λίστα L1) ή τον δείκτη j στην αντίθετη περίπτωση. Επανέλαβε τα βήματα 2-4 μέχρι να εισαχθούν όλα τα στοιχεία στον TEMP Μέτα από 6 εκτελέσεις: (Το i,j ξεκινάνε από την αρχή της κάθε λίστας) κ ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
41
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
MergeSort: Υλοποίηση void MergeSort(int A[], int temp[], int l, int r){ // η συνθήκη τερματισμού της ανάδρομης if (l==r) return; int mid = (l+r)/2; // για ελαχιστοποίηση overflow(για μεγάλα l,r) // int mid = l + ((r - l) / 2); // μοιράζουμε αναδρομικά τον πίνακα MergeSort(A, temp, l, mid); MergeSort(A, temp, mid+1, r); // συνεχίζεται DIVIDE ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
42
MergeSort: Υλοποίηση (συν.)
// Τώρα οι πίνακες [l..mid] και [mid+1..r] είναι // ταξινομημένοι Η διαδικασία συγχώνευσης int k=l, i=l, j=mid+1; // συγχώνευση στον temp μέχρι μια από // τις λίστες αδειάσει while ((i<=mid) && (j<=r)) { if (A[i]<A[j]){ temp[k] = A[i]; i++; } else { temp[k] = A[j]; j++; k++; // συνεχίζεται MERGE ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
43
MergeSort: Υλοποίηση (συν.)
// copy όλων των υπόλοιπων στοιχείων λίστας L1 while (i<=mid) { temp[k] = A[i]; k++;i++; } // copy όλων των υπόλοιπων στοιχείων λίστας L2 while (j<=r) { temp[k] = A[j]; k++;j++; // αντιγραφή όλων των στοιχείων από TEMP -> A for (i=l; i<=r; i++) { A[i] = temp[i]; ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
44
Παράδειγμα Εκτέλεσης Merge Sort
BEFORE:[8,4,8,43,3,5,2,1,10] 0,8: [8,4,8,43,3, 5,2,1,10] 0,4: [8,4,8,43,3] 0,2: [8,4,8] 0,1: [8,4] 0,0: [8] 1,1: [4] Merging: [A0,A0] [A1,A1] => [4,8,] 2,2: [8] Merging: [A0,A1] [A2,A2] => [4,8,8,] 3,4: [43,3] 3,3: [43] 4,4: [3] Merging: [A3,A3] [A4,A4] => [3,43] Merging: [A0,A2] [A3,A4] => [3,4,8,8,43] 5,8: [5,2,1,10] 5,6: [5,2] 5,5: [5] 6,6: [2] Merging: [A5,A5] [A6,A6] => [2,5] 7,8: [1,10] 7,7: [1] 8,8: [10] Merging: [A7,A7] [A8,A8] => [1,10] Merging: [A5,A6] [A7,A8] => [1,2,5,10] Merging: [A0,A4] [A5,A8] => [1,2,3,4,5,8,8,10,43] AFTER:[1,2,3,4,5,8,8,10,43] Index: divide divide divide divide divide ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
45
MergeSort: Ανάλυση Χρ. Εκτέλ. - Αναδρομή
Το πρόβλημα μοιράζει αναδρομικά σε δυο μέρη την λίστα που θέλουμε να ταξινομήσουμε. Όταν φτάσουμε στην λίστα που έχει μέγεθος 1 τότε σταματά η αναδρομή και το πρόγραμμα αρχίζει να συνδυάζει (merge) τις επιμέρους λίστες. Παρατηρούμε ότι πάνω σε μια λίστα μεγέθους Ν η αναδρομή εκτελείται 2log2n φορές. Δηλαδή ο πίνακας μοιράζεται ως εξής: n, n/2, n/4, …, 2, 1 void MergeSort(int A[], int temp[], int l, int r){ if (l==r) return; int mid = (l+r)/2; MergeSort(A, temp, l, mid); MergeSort(A, temp, mid+1, r); Θ(logn) ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
46
MergeSort: Ανάλυση Χρ. Εκτέλ. - Συγχώνευση
Σε κάθε επίπεδο της ανάδρομης περνάμε μια φορά από το κάθε στοιχείο. Επομένως η συγχώνευση των στοιχείων σε κάθε επίπεδο της εκτέλεσης χρειάζεται γραμμικό χρόνο Θ(n). Σημειώστε ότι η διαδικασία απαιτεί τη χρήση βοηθητικού πίνακα. Μπορούμε να χρησιμοποιούμε τον ίδιο βοηθητικό πίνακα temp για όλες τις (αναδρομικές) κλήσεις του ΜergeSort. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
47
MergeSort: Ανάλυση Χρ. Εκτέλ. - Συνολικός
Η αντιγραφή και η συγχώνευση παίρνουν χρόνο Θ(n) και η αναδρομή παίρνει χρόνο Θ(log n). Συνολικά Θ(n log n). Ο χρόνος εκτέλεσης εκφράζεται και από την αναδρομική εξίσωση Τ(0) = Τ(1) = 1 Τ(n) = 2T(n/2) + n … η οποία μπορεί να λυθεί με το Master Theorem ή με την μέθοδο της αντικατάστασης. Πλεονέκτημα MergeSort: O συνολικός χρόνος εκτέλεσης είναι Θ(n log n) (σε αντίθεση με το SelectionSort (Θ(n2)) και το InsertionSort (Ο(n2)) Μειονέκτημα: Απαιτεί τη χρήση βοηθητικού πίνακα (δηλαδή χρειάζεται διπλάσιο χώρο αποθήκευσης για την εκτέλεση του). Αυτό δεν καθιστά την μέθοδο πολύ εύχρηστη. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
48
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάλεξη 10: Αλγόριθμοι Ταξινόμησης II Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Οι αλγόριθμοι ταξινόμησης: Δ. QuickSort – Γρήγορη Ταξινόμηση Ε. BucketSort – Ταξινόμηση με Κάδους - Έμμεση Ταξινόμηση - Εξωτερική Ταξινόμηση ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
49
Δ. Γρήγορη Ταξινόμηση (QuickSort)
Η γρήγορη ταξινόμηση (QuickSort) είναι, όπως ο MergeSort, διαδικασία διαίρει και βασίλευε (divide and conquer, δηλ. αναδρομική διαδικασία όπου το πρόβλημα μοιράζεται σε μέρη τα οποία λύνονται ξεχωριστά, και μετά οι λύσεις συνδυάζονται). Δεν χρειάζεται βοηθητικό πίνακα (όπως στην MergeSort) Πρακτικά, ο πιo γρήγορος αλγόριθμος. Στη χείριστη περίπτωση ο αλγόριθμος QuickSort είναι Ο(n²) αλλά στην καλύτερη περίπτωση χρειάζεται Ω(nlogn): Τα περισσότερα συστήματα χρησιμοποιούν το QuickSort (π.χ., Unix) και οι περισσότερες γλώσσες προγραμματισμού το προσφέρουν σαν μέρος των βασικών βιβλιοθηκών τους πχ. C, JAVA, C++, etc. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
50
QuickSort: Αλγόριθμος
Aν το δεδομένο εισόδου περιέχει 0 ή 1 στοιχεία δεν κάνουμε τίποτα. Διαφορετικά, αναδρομικά: διαλέγουμε ένα στοιχείο p (ακόμα δεν ορίζουμε πιο ακριβώς), το οποίο ονομάζουμε το άξονα (pivot) στοιχείο και το αφαιρούμε από το δεδομένο εισόδου. χωρίζουμε τον πίνακα σε δύο μέρη S1 και S2, όπου το S1 θα περιέχει όλα τα στοιχεία του πίνακα που είναι μικρότερα από το p, και το S2 θα περιέχει τα υπόλοιπα στοιχεία (όλα τα στοιχεία που είναι μεγαλύτερα ή ίσα από το p). Καλούμε αναδρομικά τον αλγόριθμο στο S1, και παίρνουμε απάντηση το Τ1, και στο S2, και παίρνουμε απάντηση το Τ2. Επιστρέφουμε τον πίνακα [Τ1, p, T2]. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
51
QuickSort: Βασική Ιδέα
[72, 12, 1, 34, 3, 50, 28, 6, 5, 22, 91, 73] χωρίζουμε με pivot το 28 (μπορούσε να είναι οποιοδήποτε άλλο στοιχείο) Θέτουμε αριστερά του pivot τα μικρότερα και δεξιά τα μεγαλύτερα του [12, 1, 3, 6, 5, 22] [72,34,50,91,73] Quicksort Quicksort 1, 3, 5, 6, 12, , 50, 72, 73, 91 Αποτέλεσμα: 1, 3, 5, 6, 12, 22, , , 50, 72, 73, 91 < 28 >= 28 ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
52
QuickSort: Ψευδοκώδικας
void Quicksort(int A[], int l, int r){ if (l>=r) return; int pivotIndex = findPivotIndex(A, l, r); int pivot = A[pivotIndex]; // κάνουμε swap τον pivot με το τελευταίο. swap(A, pivotIndex, r); /* H διαδικασία partition χωρίζει τον πίνακα Α[l…r-1] έτσι ώστε Α[l..k-1] να περιέχει στοιχεία < pivot, A[k…r-1] να περιέχει στοιχεία >=pivot, και επιστρέφει την τιμή k. */ int k = partition (A, l, r-1, pivot); // κάνουμε swap τον k με το τελευταίο. swap(A, k, r); Quicksort(A, l, k-1); QuickSort(A, k+1, r); } Έστω ότι διαλέξαμε τον μεσαίο, δηλ., (l+r)/2;. Θα μπορούσαμε να διαλέξουμε οποιονδήποτε άλλο ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
53
Διαδικασία Partition(A, l, r, p)
Με δεδομένο εισόδου τον πίνακα Α[l…r], και pivot p, θέλουμε να χωρίσουμε τον πίνακα σε δύο μέρη ως προς το p. To πιο πάνω πρέπει να επιτευχθεί χωρίς τη χρήση δεύτερου πίνακα (το sorting θα γίνει επί τόπου). Βασική Ιδέα: Επαναλαμβάνουμε τα εξής μέχρις ότου τα l και r να συναντηθούν. Προχώρα το r προς τα αριστερά όσο τα στοιχεία που βρίσκεις είναι μεγαλύτερα (ή ίσα) του p, Προχώρα το l προς τα δεξιά όσο τα στοιχεία που βρίσκεις είναι μικρότερα του p, αντάλλαξε τα στοιχεία που δείχνονται από τα l και r. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
54
Παράδειγμα Εκτέλεσης Partition
Δεδομένο Εισόδου: pivot = 48, μετακίνηση του pivot στο τέλος (swap(4, 8)): l r εκτέλεση του Partition(A, l, r, 48): l r l r r l index pivot ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
55
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Quicksort: Υλοποίηση public static void QuickSort(int A[], int l, int r) { int pivot, pivotIndex; if (l >= r) return; // Διαλέγουμε το pivot pivotIndex = findPivotIndex(l, r); pivot = A[pivotIndex]; // Κάνουμε swap το pivot με το τελευταίο swap(A, pivotIndex, r); /* H διαδικασία partition χωρίζει τον πίνακα Α[l…r-1] έτσι ώστε * Α[l..k-1] να περιέχει στοιχεία < pivot, A[k…r-1] να περιέχει στοιχεία * >=pivot, και επιστρέφει την τιμή k. */ int k = partition(A, l, r - 1, pivot); // Κάνουμε swap το k με το τελευταίο if (A[r] < A[k]) swap(A, k, r); QuickSort(A, l, k - 1); QuickSort(A, k + 1, r); } ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
56
Quicksort: Υλοποίηση (συν.)
public static int findPivotIndex(int l, int r) { return (l + r) / 2; } public static void swap(int A[], int pivot, int r) { int tmp = A[pivot]; A[pivot] = A[r]; A[r] = tmp; ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
57
Quicksort: Υλοποίηση (συν.)
public static int partition(int A[], int l, int r, int pivot) { while (l < r) { // προχωρούμε από l στο r μέχρι να χρειαστεί ένα swap while (A[l] < pivot && l < r) l++; // leave “<pivot“ on left // προχωρούμε από r στο l μέχρι να χρειαστεί ένα swap while (pivot <= A[r] && l < r) r--; // leave “>=pivot“ on right if (l == r) break; // τώρα κάνε το swap if (A[l] >= pivot) swap(A, l, r); // move “>=“ to the right } // επέστρεψε το σημείο στο οποίο θέλουμε να γίνει η // εισαγωγή του pivot return l; ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
58
Παράδειγμα Εκτέλεσης QuickSort
BEFORE:[72,6,37,48,30,42,83,75] ** QuickSort [0,7] [72,6,37,48,30,42,83,75,] PivotIndex: 3(48) => Swapping 48, 75 [72,6,37,75,30,42,83,48,] Partitioning [0,6] Swapping 72, 42 [42,6,37,75,30,72,83,48,] Swapping 75, 30 [42,6,37,30,75,72,83,48,] Inserting Pivot at Position:4 Swapping 75, 48 [42,6,37,30,48,72,83,75,] ** QuickSort [0,3] PivotIndex: 1(6) => Swapping 6, 30 [42,30,37,6,48,72,83,75,] Partitioning [0,2] Inserting Pivot at Position:0 Swapping 42, 6 ** QuickSort [0,-1] -> RETURN ** QuickSort [1,3] [6,30,37,42,48,72,83,75,] PivotIndex: 2(37) => Swapping 37, 42 [6,30,42,37,48,72,83,75,] Partitioning [1,2] Inserting Pivot at Position:2 Swapping 42, 37 ** QuickSort [1,1] -> RETURN ** QuickSort [3,3] -> RETURN ** QuickSort [5,7] PivotIndex: 6(83) => Swapping 83, 75 [6,30,37,42,48,72,75,83,] Partitioning [5,6] with pivot:83 Inserting Pivot at Position:6 ** QuickSort [5,5] -> RETURN ** QuickSort [7,7] -> RETURN AFTER:[6,30,37,42,48,72,75,83,] Index: ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
59
Ανάλυση του Χρόνου Εκτέλεσης
Η εύρεση του pivot απαιτεί χρόνο Ο(1) και η διαδικασία Partition(Α, l, r, p) εκτελείται σε χρόνο O(n) σε κάθε επίπεδο της αναδρομής. Η αναδρομική εκτέλεση του QuickSort παίρνει στην χείριστη περίπτωση χρόνο O(n) και στην καλύτερη περίπτωση χρόνο Ω(logn) Χείριστη περίπτωση: Κάθε φορά που επιλέγουμε τον pivot όλα τα στοιχεία τυγχάνει να ταξινομούνται είτε μόνο αριστερά του (δηλαδή <pivot) ή μόνο δεξιά του (δηλαδή >=pivot) πχ 9,9,9,9,9,9,9 Συνολικός χρόνος εκτέλεσης Τ(n) O(n²). Βέλτιστη περίπτωση: Κάθε φορά που επιλέγουμε τον pivot τα μισά στοιχεία ταξινομούνται αριστερά του (δηλαδή <pivot) και τα υπόλοιπα μισά δεξιά του (δηλαδή >=pivot) πχ 1,2,3,4,5,6,7 Συνολικός χρόνος εκτέλεσης Τ(n) Ω(n log n). ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
60
Ανάλυση Μέσης Περίπτωσης
Θεωρούμε όλες τις πιθανές περιπτώσεις της συμπεριφοράς της μεθόδου Partition(A, i, j, p), όπου j-i = n-1. Υπάρχουν n τέτοιες περιπτώσεις: το αριστερό κομμάτι του partition μπορεί να έχει από 0 μέχρι n-1 στοιχεία. Ας υποθέσουμε πως οι n αυτές περιπτώσεις είναι ισοπίθανες, δηλαδή η κάθε μια έχει πιθανότητα 1/n. Τότε η μέση περίπτωση του Τ(n) δίνεται ως Επίλυση της αναδρομικής σχέσης δίνει T(n)O(n log n). ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
61
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Παρατηρήσεις Πως δουλεύει η μέθοδος Partition με δεδομένο εισόδου πίνακα με πολλά στοιχεία ίσα με το pivot; Υπάρχουν άλλες στρατηγικές για επιλογή του pivot; pivot = mid(A[1], A[n], A[n/2]) Επέλεξε τυχαία κάποιο στοιχείο του πίνακα Στην πράξη, για δεδομένα εισόδου μικρού μεγέθους το InsertionSort δουλεύει πιο αποδοτικά. Επομένως μια καλή στρατηγική θα ήταν να συνδυάσουμε τους δύο αλγόριθμους ώστε σε μικρούς πίνακες (π.χ. n10) να χρησιμοποιείται το ΙnsertionSort και σε μεγάλους το Quicksort: στη μέθοδος Quicksort ανταλλάξτε την πρώτη γραμμή με την εξής: if (j-i)<= 10 InsertionSort(A[i…j], j-i); Ακόμα ένας πιθανός τρόπος βελτίωσης του χρόνου εκτέλεσης είναι η χρήση στοίβας αντί αναδρομής. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
62
Κάτω φράγμα για αλγόριθμους ταξινόμησης
Ξέρουμε πως το πρόβλημα ταξινόμησης μπορεί να λυθεί σε χρόνο Ο(n log n) (QuickSort και MergeSort). Υπάρχει πιο αποδοτικός αλγόριθμος ταξινόμησης; Θα δείξουμε πως κάθε αλγόριθμος ταξινόμησης είναι Ω(n log n). Ως μονάδα μέτρησης αποδοτικότητας θα χρησιμοποιήσουμε τον αριθμό συγκρίσεων που απαιτεί κάποιος αλγόριθμος. Υποθέτουμε ότι κάθε στοιχείο του πίνακα που θέλουμε να ταξινομήσουμε είναι διαφορετικό από όλα τα άλλα. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
63
Σειριακή διάταξη στοιχείων και ταξινόμηση
Η συμπεριφορά ενός αλγόριθμου ταξινόμησης εξαρτάται μόνο από τη σχετική σειρά μεταξύ των στοιχείων που ταξινομούμε και όχι από τα συγκεκριμένα στοιχεία. Δηλαδή: αν Α και Β είναι δύο πίνακες τέτοιοι ώστε για κάθε i και j, A[i] < A[j] αν και μόνο αν Β[i] < B[j], τότε ο αριθμός των βημάτων (όπως και ο αριθμός των συγκρίσεων) που θα εκτελέσει κάποιος αλγόριθμος με δεδομένο εισόδου Α θα είναι ο ίδιος με τον ανάλογο αριθμό που θα εκτελέσει με δεδομένο εισόδου Β. Άρα, η σειριακή διάταξη των στοιχείων του δεδομένου εισόδου Α, Α[1], Α[2], …, Α[n], έχει κύρια σημασία. Υπάρχουν n! ‘διαφορετικές’ τοποθετήσεις n ξεχωριστών στοιχείων. Άρα υπάρχουν n! διαφορετικά δεδομένα εισόδου. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
64
Ανάλυση Αλγόριθμων Ταξινόμησης
Θα περιγράψουμε τη συμπεριφορά ενός αλγόριθμου ως ένα δένδρο αποφάσεων (decision tree). Στη ρίζα επιτρέπονται όλες οι διαφορετικές ‘σειρές’ των στοιχείων. Ας υποθέσουμε πως ο αλγόριθμος συγκρίνει τα δύο πρώτα στοιχεία Α[1] και Α[2]. Τότε το αριστερό παιδί του δένδρου αντιστοιχεί στην περίπτωση Α[1] < Α[2] και το δεξί παιδί της ρίζας στην περίπτωση Α[2] < Α[1]. Σε κάθε κόμβο, μια σειρά στοιχείων είναι νόμιμη αν ικανοποιεί όλες τις συγκρίσεις στο μονοπάτι από τη ρίζα στον κόμβο. Τα φύλλα αντιστοιχούν στον τερματισμό του αλγόριθμου και κάθε φύλλο περιέχει το πολύ μια νόμιμη σειρά στοιχείων. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
65
Δένδρο αποφάσεων για 3 στοιχεία
a<b<c a<c<b b<a<c b<c<a c<a<b c<b<a a<b b<a a<b<c a<c<b c<a<b b<a<c b<c<a c<b<a b<c c<b b<c c<b a<c<b c<a<b b<a<c b<c<a c<b<a a<b<c c<a a<c c<a a<c a<c<b c<a<b b<a<c b<c<a ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
66
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Κάτω φράγμα Έστω P ένας αλγόριθμός ταξινόμησης, και έστω T το δένδρο αποφάσεων του P με δεδομένο εισόδου μεγέθους n. Ο αριθμός των φύλλων του Τ είναι n! Το ύψος του Τ είναι ένα κάτω φράγμα του χείριστου χρόνου εκτέλεσης του αλγόριθμου P. Ένα δυαδικό δένδρο ύψους d έχει το πολύ 2d φύλλα. Άρα το Τ έχει ύψος το λιγότερο log n! Συμπέρασμα: P Ω(log n!) = Ω(n log n). H μέση περίπτωση είναι επίσης n log n. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
67
Ε. Ταξινόμηση με Κάδους - BucketSort
Έστω ότι ο πίνακας A n στοιχείων περιέχει στοιχεία που ανήκουν στο διάστημα [1..m]. O αλγόριθμος BucketSort βασίζεται πάνω στα ακόλουθα βήματα: Δημιουργούμε ένα πίνακα buckets μήκους m και θέτουμε buckets[i]=0, για όλα τα i (Αυτά θα είναι τα buckets - κάδοι) Διαβάζουμε τον πίνακα Α ξεκινώντας από το πρώτο στοιχείο. Αν διαβάσουμε το στοιχείο α, τότε αυξάνουμε την τιμή του buckets[α] κατά ένα. Επαναλαμβάνουμε το βήμα μέχρι το τελευταίο στοιχείο. Τέλος, διαβάζουμε γραμμικά τον πίνακα buckets, o oποίος περιέχει αναπαράσταση του ταξινομημένου πίνακα, και θέτουμε τα στοιχεία του πίνακα Α με την ταξινομημένη ακολουθία. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
68
BucketSort: Βασική Ιδέα
Δεδομένο Εισόδου: Τα στοιχεία είναι στο εύρος m=[0,14], n=8 Μετά την 1η εκτέλεση του Bucketsort (Εισαγωγή του 1) Μετά τη 2η εκτέλεση του Bucketsort (Εισαγωγή του 11) Μετά τη 3η εκτέλεση του Bucketsort (Εισαγωγή του 1) Μετά την 8ή εκτέλεση του Bucketsort n-1 m m m m ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
69
BucketSort: Υλοποίηση
// Α: Πίνακας για ταξινόμηση μεγέθους n // Buckets: Βοηθητικός πίνακας μεγέθους m void BucketSort(int A[], int buckets[], int n, int m){ int i, j, k=0; // Κατανομή στοιχείων στους σωστούς κάδους for (i=0; i<n; i++) { buckets[A[i]]++; } // Αντιγραφή στοιχείων από πίνακα BUCKETS // στον πίνακα A for (i=0; i<m; i++) { for (j=0; j<buckets[i]; j++) { A[k] = i; k++; Ο(n) Ο(n+m) Συνολικά περνάμε 1 φορά από τα στοιχεία του πίνακα BUCKETS (m) και μια φορά από αυτά του A (n) ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
70
BucketSort: Χρόνος Εκτέλεσης
O αλγόριθμος BucketSort πετυχαίνει ταξινόμηση του Α σε χρόνο Θ(n+m) Σημαίνει ότι ο αλγόριθμος είναι καλύτερος από τον MergeSort Θ(nlogn); Αυτό διαψεύδει το κάτω φράγμα Ω(n log n); (θυμειθείτε ότι έχουμε αποδείξει ότι όλοι οι αλγόριθμοι ταξινόμησης, με δυαδική σύγκριση, έχουν σαν κάτω φράγμα Ω(n log n)) ΟΧΙ, γιατί το μοντέλο είναι διαφορετικό. Μέχρι τώρα υποθέσαμε ότι η μόνη πράξη που μπορούμε να εφαρμόσουμε στα δεδομένα είναι η δυαδική σύγκριση ή ανταλλαγή στοιχείων. Ο αλγόριθμος BucketSort όμως στο Βήμα 2 ουσιαστικά εφαρμόζει m-αδική (m-ary) σύγκριση, σε χρόνο Ο(1). Αυτό μας υπενθυμίζει πως σχεδιάζοντας ένα αλγόριθμο και λαμβάνοντας υπόψη κάποια αποδεδειγμένα κάτω φράγματα πρέπει πάντα να αναλύουμε το μοντέλο στο οποίο δουλεύουμε. Η ύπαρξη και αξιοποίηση περισσότερων πληροφοριών πιθανόν να επιτρέπουν τη δημιουργία αποδοτικότερων αλγορίθμων. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
71
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάλεξη 11: Δέντρα Ι - Εισαγωγή σε Δενδρικές Δομές Δεδομένων Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Εισαγωγή σε δενδρικές δομές δεδομένων, - Ορισμοί και πράξεις - Αναπαράσταση δενδρικών δομών δεδομένων στη μνήμη - Διάσχιση Δέντρων ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
72
Βάθος και ύψος ενός δένδρου
Βάθος ενός κόμβου (Node Depth) (ή επίπεδο Level) => αριθμός προγόνων + 1. π.χ. το βάθος της ρίζας ενός δένδρου είναι 1. Βάθος ενός δένδρου ονομάζουμε τον μέγιστο από τα βάθη των κόμβων. Μονοπάτι ή διαδρομή (path) ενός δένδρου είναι μια ακολουθία κόμβων v1, v2,, …, vk, όπου κάθε vi είναι πατέρας του vi+1. Το μήκος του μονοπατιού v1, v2,, …, vk είναι k-1. Ύψος ενός κόμβου : το μήκος του μονοπατιού από το βαθύτερο κόμβο (στο ίδιο υπό-δένδρο) προς τον κόμβο. π.χ. το ύψος οποιουδήποτε φύλλου είναι 0. Ύψος ενός δένδρου : το ύψος της ρίζας του δένδρου. Βάθος(6) = 3 Ύψος(6): 1 ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
73
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Ορολογία Δένδρων Ρίζα(Root): κόμβος χωρίς γονέα (A) Εσωτερικός κόμβος: κόμβος με τουλάχιστον ένα παιδί (A, B, C, F) Εξωτερικός κόμβος(ή φύλλο-leaf): κόμβος χωρίς παιδιά (E, I, J, K, G, H, D) Πρόγονοι ενός κόμβου: γονιός, παππούς, προ-παππούς κτλ. Απόγονοι ενός κόμβου: παιδιά, εγγόνια, δισέγγονα, etc. Υπόδενδρο: δένδρο αποτελούμενο από ένα κόμβο και τους απογόνους του A B D C G H E F I J K Υποδέντρο subtree ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
74
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Παράδειγμα βάθος 6 Βάθος (Depth/Level) κόμβου:1 Ύψος (Height) κόμβου: 0 ύψος Βαθμός Δένδρου = 0 Βάθος (Δένδρου): 2 Ύψος (Δένδρου): 1 Βάθος κόμβου 37: 1 Ύψος κόμβου 37: 1 Βάθος κόμβου 24: 2 Ύψος κόμβου 24: 0 Βάθος (Δένδρου) : 3 Ύψος (Δένδρου) : 2 Βάθος κόμβου 24: 2 Ύψος κόμβου 24: 1 Βάθος κόμβου 6: 3 Ύψος κόμβου 6: 0 Βαθμός Δένδρου = 2 Βαθμός Δένδρου = 2 ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
75
Αναπαράσταση Δένδρων στη Μνήμη
Υπάρχουν πολλοί τρόποι να αναπαραστήσουμε ένα κόμβο δέντρου στη μνήμη. Κάθε κόμβος αποτελείται από κάποιο αριθμό πεδίων: Τα πραγματικά δεδομένα του κόμβου (το κλειδί), και τα υπόλοιπα πεδία περιέχουν στοιχεία σχετικά με τη δομή του δένδρου, δηλαδή περιγράφουν με κάποιο τρόπο τη σχέση του συγκεκριμένου κόμβου με άλλους κόμβους του δένδρου. Για παράδειγμα: αναφορές στα παιδιά του Αν είναι η ρίζα, εσωτερικός ή φύλο ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
76
Αναπαράσταση Δένδρων στη Μνήμη
Για δένδρο βαθμού n κάθε κόμβος έχει την πιο κάτω μορφή: Info p1 p2 … pn όπου ο pi είναι δείκτης στο i-οστό παιδί του κόμβου. Εναλλακτικά θα μπορούσαμε να αναπαραστήσουμε ένα δένδρο μέσα σε ένα στατικά ορισμένο πίνακα. Π.χ. όπου οι δείκτες είναι απλά αριθμοί (θέσεις του πίνακα). Και στις δυο περιπτώσεις θέλουμε 4 bytes ανά δείκτη (σε x86) ή integer θέσης πίνακα. Αν έχουμε ένα 5-αδικό δέντρο τότε κάθε κόμβος χρειάζεται επιπλέον 20 bytes. Αν ένας κόμβος δεν έχει παιδιά τότε αυτοί οι δείκτες θα μένουν αναξιοποίητοι, επομένως θα έχουμε σπάταλη χώρου. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
77
Αναπαράσταση Δένδρων στη Μνήμη
Πρόβλημα: Μείωση της σπατάλης χώρου από αχρησιμοποίητους δείκτες/αναφορές. Λύση: Μπορούμε να μετατρέψουμε ένα δέντρο n-αδικό (κάθε κόμβος με n παιδιά) σε ένα 2-αδικό (κάθε κόμβος με 2 παιδιά) χρησιμοποιώντας την πολιτική «Πρώτο Παιδί / Επόμενο Αδέρφι» (First Child/Next Sibling) Δηλαδή κάθε κόμβος u περιέχει τα δεδομένα του κόμβου, δείκτη στο αριστερότερο (πρώτο) παιδί του u, δείκτη στον επί δεξιά αδελφό του u, αν υπάρχει. Αυτή η πολιτική μας επιτρέπει να μειώσουμε τον αριθμό των δεικτών ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
78
Παράδειγμα αναπαράστασης δένδρου
[Normal Tree] A B Γ Δ Ε Ζ Η Θ Ι Δένδρο Βαθμού n (n=4) Συνολικές Αναφορές: 9 nodes *4 refs = 36 Δένδρο Βαθμού n (n=2) Συνολικές Αναφορές: 9 nodes *2 refs = 18 [First Child/Next Sibling] Α Β Γ Δ NULL Pointer Ε Ζ Η Θ Ι Next sibling ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
79
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάσχιση Δένδρων Αν θέλουμε να επισκεφθούμε όλους τους κόμβους ενός δένδρου, μπορούμε να χρησιμοποιήσουμε ένα από τους πιο κάτω τρόπους, οι οποίοι διαφέρουν στη σειρά με την οποία εξετάζουν τους κόμβους. Προθεματική Διάσχιση: (Preorder Traversal) επισκεπτόμαστε (εκτυπώνουμε) πρώτα τη ρίζα και ύστερα τα παιδιά της. Αναδρομικά η πράξη ορίζεται ως εξής: Print_Preorder(treenode u) Print u; foreach child v of u Print_Preorder(v) Μεταθεματική Διάσχιση: (Postorder Traversal) επισκεπτόμαστε (εκτυπώνουμε) πρώτα τα παιδιά και ύστερα τη ρίζα του δένδρου. Αναδρομικά η πράξη ορίζεται ως εξής: Print_Postorder(treenode u) Print_Postorder(v) ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
80
Παράδειγμα Διάσχισης δένδρου
Print_Preorder(treenode u) Print u; foreach child v of u Print_Preorder(v) A B Γ Δ Ε Ζ Η Θ Ι Προθεματική Διάσχιση (Preorder) : Α Β Ε Γ Δ Ζ Η Θ Ι Μεταθεματική Διάσχιση (Postorder) : Ε Β Γ Ζ Η Θ Ι Δ Α Print_Postorder(treenode u) foreach child v of u Print_Postorder(v) Print u; ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
81
Άλλες Χρήσιμες Πράξεις του ΑΤΔ Δένδρο
Parent(u) επέστρεψε τον πατέρα του u Children(u) επέστρεψε τα παιδιά του u FirstChild(u) επέστρεψε το πρώτο παιδί του u RightSibling(u) επέστρεψε τον κόμβο στα δεξιά του u LeftSibling(u) επέστρεψε τον κόμβο στα αριστερά του u IsLeaf(u) αν το u είναι φύλλο τότε δώσε true, διαφορετικά δώσε false IsRoot(u) αν το u είναι η ρίζα του δένδρου δώσε true, διαφορετικά δώσε false Depth(u) δώσε το βάθος του u στο δένδρο. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
82
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάλεξη 12: Δέντρα ΙΙ - Δυαδικά Δέντρα Στην ενότητα αυτή θα μελετηθούν τα εξής επιμέρους θέματα: - Δυαδικά Δένδρα - Δυαδικά Δένδρα Αναζήτησης (ΔΔΑ) - Εύρεση Τυχαίου, Μέγιστου, Μικρότερου στοιχείου - Εισαγωγή στοιχείου - Διαγραφή Μικρότερου και Τυχαίου στοιχείου - Σύγκριση 2 ΔΔΑ, Διάσχιση ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
83
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Δυαδικά Δένδρα Ένα δένδρο είναι δυαδικό αν όλοι οι κόμβοι του έχουν βαθμό 2. Ορισμός: Δυαδικό δένδρο λέγεται ένα δένδρο το οποίο : είτε είναι κενό, ή αποτελείται από μια ρίζα και δύο δυαδικά υπόδενδρα. Αναφερόμαστε στα δύο υποδένδρα ως το αριστερό και το δεξιό υπόδενδρο. Το ύψος ενός δυαδικού δένδρου με n κόμβους μπορεί να είναι το πολύ : n-1 (συνδεδεμένη λίστα) και το λιγότερο lg n. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
84
Αναπαράσταση Δένδρων στη Μνήμη
Αφού κάθε κόμβος σε ένα δυαδικό δένδρο έχει το πολύ δύο παιδιά, μπορούμε να κρατούμε δείκτες στο καθένα από αυτά. Δηλαδή, ένας κόμβος μπορεί να υλοποιηθεί ως μια εγγραφή TreeNode με τρία πεδία (παρόμοια με κόμβο διπλά συνδεδεμένης λίστας). private class TreeNode<E> { E key; BinaryTreeNode<E> left; BinaryTreeNode<E> right; //other useful fields boolean isLeaf, isInternal, isRoot; Object data; // data BinaryTreeNode(E key){ this.key = key; this.left = null; this.right = null; } key, όπου αποθηκεύουμε τo κλειδί του κόμβου, left, τύπου pointer, ο οποίος δείχνει το αριστερό, υπόδενδρο που ριζώνει στον συγκεκριμένο κόμβο, και right, τύπου pointer, ο οποίος δείχνει το δεξιό υπόδενδρο που ριζώνει στον συγκεκριμένο κόμβο. ... διάφορα άλλα χρήσιμα στοιχεία ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
85
Αναπαράσταση Δένδρων στη Μνήμη (συν.)
Έτσι, ένα δυαδικό δένδρο υλοποιείται ως ένα δείκτης προς τη ρίζα του δένδρου, δηλαδή μία αναφορά σε εγγραφή τύπου TreeNode. Επίσης μπορεί να υλοποιηθεί σαν μία ξεχωριστή δομή Tree η οποία να περιλαμβάνει και πληροφορίες όπως μέγεθος, ύψος, κ.τ.λ. Παρόμοια με Λίστες, Στοίβες, Ουρές, κτλ. public class Tree<E extends Comparable>{ private class TreeNode<E> {...} private BinaryTreeNode<E> head; private int size; private int height; public void makeEmpty() { this.head=null; this.size=0; } public boolean isEmpty() { return this.size==0; public int size() { return this.size; … ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
86
Δυαδικά Δένδρα Αναζήτησης ΔΔΑ (Binary Search Trees)
Το πιο σημαντικό πλεονέκτημα της χρήσης δυαδικών δένδρων η αποδοτική αναζήτηση σε ένα σύνολο στοιχείων Υποθέτουμε την ύπαρξη μιας σχέσης στο σύνολο των στοιχείων που επεξεργαζόμαστε, έστω τη σχέση < πάνω στο σύνολο των ακεραίων. Ένα δυαδικό δένδρο αναζήτησης (ΔΔΑ) είναι ένα δυαδικό δένδρο κάθε κόμβος u του οποίου ικανοποιεί τα εξής: τα κλειδιά του αριστερού υποδένδρου του u είναι μικρότερα από το κλειδί του u τα κλειδιά του δεξιού υποδένδρου του u είναι μεγαλύτερα (ή ίσο) από το κλειδί του u. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
87
Διαδικασία Εύρεσης Τυχαίου Στοιχείου: Υλοποίηση
public BinaryTreeNode<E> find(E key) { return this.find(key, this.head); } private BinaryTreeNode<E> find(E key, BinaryTreeNode<E> node) { if(node==null){ return null; else if (key.compareTo(node.key) == 0) { return node; else if (key.compareTo(node.key) < 0) { return find(key, node.left); else { return find(key, node.right); ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
88
Διαδικασία Εύρεσης Ελαχίστου/Μεγίστου στοιχείου
Στόχος: να επιστραφεί ο κόμβος που περιέχει το μικρότερο (μεγαλύτερο) κλειδί στο δένδρο. Αλγόριθμος: Ξεκινάμε από τη ρίζα και πηγαίνουμε αριστερά (δεξιά) όσο υπάρχει ένα αριστερό (δεξιό) παιδί. Το σημείο που σταματάμε είναι το μικρότερο (μεγαλύτερο) στοιχείο. Χρόνος Εκτέλεσης; Παρόμοια με find ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
89
Διαδικασία Εύρεσης Ελαχ./Μεγίστ.: Υλοποίηση
public E findMin(){ BinaryTreeNode<E> tmp = this.findMin(this.head); if(tmp!=null) return tmp.key; return null; } public BinaryTreeNode<E> findMin(BinaryTreeNode<E> node){ if(node!=null) while(node.left!=null) node = node.left; return node; ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
90
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διαδικασία Εισαγωγής Σημαντικό: παίζει ρόλο η σειρά εισαγωγής Παραδείγματα Α. Εισαγωγή 37, 24, 42, 6, 40, Β. Εισαγωγή 60, 42, 6, 24, 37, 40 60 37 42 24 42 6 6 40 60 24 37 40 ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
91
Διαδικασία Εισαγωγής Κόμβου
Διασχίζουμε το δέντρο, όπως θα κάναμε με τη find Εάν το X βρεθεί, δεν κάνουμε καμία ενέργεια Διαφορετικά, εισάγουμε το X στο τελευταίο σημείο του μονοπατιού που διασχίστηκε Χρονική πολυπλοκότητα= O(h - ύψος του δένδρου) ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
92
Διαδικασία Εισαγωγής Κόμβου: Υλοποίηση
public void insert(E key) { this.head = this.insert(key, this.head); this.size++; } private BinaryTreeNode<E> insert(E key,BinaryTreeNode<E> node) { if(node==null){ node = new BinaryTreeNode<E>(key); else if (key.compareTo(node.key) < 0) { node.left = insert(key, node.left); else if (key.compareTo(node.key) > 0) { node.right = insert(key, node.right); return node; ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
93
Εξαγωγή του μικρότερου κόμβου – DeleteMin()
Ακολουθούμε τους αριστερούς δείκτες όσο μπορούμε, φθάνοντας στον κόμβο με το μικρότερο στοιχείο, u. Βρίσκουμε τον πατέρα v του u και αλλάζουμε τον αριστερό δείκτη του v ώστε να δείχνει στο δεξιό παιδί του u. 71 v u ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
94
Διαδικασία Διαγραφής Στοιχείου
Για να αφαιρέσουμε ένα κλειδί i από ένα ΔΔΑ: Βρίσκουμε τον κόμβο u που περιέχει το i. Ας υποθέσουμε πως v είναι ο πατέρας του u. Αν u είναι φύλλο, τότε αλλάζουμε τον δείκτη του v που δείχνει στο u, ώστε να γίνει null. Αν ο u έχει ένα παιδί, τότε αλλάζουμε τον δείκτη του v που δείχνει τον u, ώστε να δείχνει στο παιδί του u. Αν ο u έχει δύο παιδιά, αλλάζουμε το κλειδί του u ώστε να γίνει το μικρότερο από τα κλειδιά όλων των απογόνων του που έχουν κλειδιά μεγαλύτερα του i. καλούμε τη μέθοδο deleteMin στο αριστερό παιδί του u. ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
95
Διαγραφή στοιχείου με δύο απογόνους
Βρίσκουμε το μικρότερο στοιχείο (min) στο δεξιό υπόδεντρο του κόμβου που θέλουμε να διαγράψουμε Αντικαθιστούμε το στοιχείο που θέλουμε να διαγράψουμε με το min και τώρα έχουμε το ίδιο στοιχείο 2 φορές στο δέντρο μας Εφαρμόζουμε την deleteMin στο δεξιό υπόδεντρο ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
96
Σύγκριση Δυαδικών Δένδρων
Η σύγκριση 2 ΔΔΑ επιτυγχάνεται μην διάσχιση του κάθε κόμβου αναδρομικά. Αν όλοι οι κόμβοι έχουν το ίδιο κλειδί τότε επιστρέφει “true” και “false” στην αντίθετη περίπτωση public boolean sameTree(BinaryTree<E> t) { return this.sameTree(this.head, t.head); } private boolean sameTree(BinaryTreeNode<E> a, BinaryTreeNode<E> b) { // και τα δυο δένδρα είναι κενά => άρα επιστρέφουμε true if (a == null && b == null) return true; // και τα δυο δένδρα δεν είναι κενά – συγκρίνουμε τις ρίζες τους else if (a != null && b != null) return ((a.key.compareTo(b.key) == 0) && sameTree(a.left, b.left) && sameTree(a.right, b.right)); // το ένα εκ των δυο υπό-δένδρων είναι κενό => επιστρέφουμε false else return false; ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
97
ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Διάσχιση ΔΔΑ Αν θέλουμε να επισκεφθούμε όλους τους κόμβους ενός δένδρου, μπορούμε να χρησιμοποιήσουμε ένα από τους πιο κάτω τρόπους, οι οποίοι διαφέρουν στη σειρά με την οποία εξετάζουν τους κόμβους. Προθεματική Διάσχιση: (Preorder Traversal) επισκεπτόμαστε (εκτυπώνουμε) πρώτα κάποιο κόμβο και μετά τα παιδιά του. Μεταθεματική Διάσχιση: (Postorder Traversal) επισκεπτόμαστε (εκτυπώνουμε) πρώτα τα παιδιά και ύστερα τον κόμβο. Ενδοθεματική Διάσχιση: (Inorder Traversal) επισκεπτόμαστε (εκτυπώνουμε) πρώτα τα αριστερά παιδιά, μετά τον κόμβο και μετά τα δεξιά παιδιά. PreOrder: PostOrder: InOrder: ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
98
Διάσχιση ΔΔΑ: Υλοποίηση
public void preOrder() { this.preOrder(this.head); } private void preOrder(BinaryTreeNode<E> node) { if (node != null) { System.out.print(node.key + " "); preOrder(node.left); preOrder(node.right); } public void inOrder() { this.inOrder(this.head); } private void inOrder(BinaryTreeNode<E> node) { inOrder(node.left); inOrder(node.right); public void postOrder() { this.postOrder(this.head); } private void postOrder(BinaryTreeNode<E> node) { if (node != null) { postOrder(node.left); postOrder(node.right); System.out.print(node.key + " "); } ΕΠΛ231 – Δομές Δεδομένων και Αλγόριθμοι
Παρόμοιες παρουσιάσεις
© 2024 SlidePlayer.gr Inc.
All rights reserved.