Σύνοψη Προηγούμενου Λίστες (Lists) Ορέστης Τελέλης telelis@unipi.gr Τμήμα Ψηφιακών Συστημάτων, Πανεπιστήμιο Πειραιώς Στοίβες (Stacks) : στην κορυφή της στοίβας ( ) από την κορυφή της στοίβας ( ) Ουρές (Queues) : στο τέλος της ουράς ( ) από την αρχή της ουράς ( ) Ουρές Προτεραιότητας (Priority Queues). στη «σωστή θέση» ( ) από το ένα άκρο ( ) Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 1 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 2 / 46 Συνδεδεμένες Λίστες: Εισαγωγή (1/2) Είδαμε δομές υλοποιημένες με Πίνακες, Στοίβες, Ουρές. Συνδεδεμένες Λίστες Στατική δέσμευση επιτρέπει O(1) Η τυχαία προσπέλαση απουσιάζει εξ ορισμού, από στοίβες και ουρές. (που έχουμε ξαναδεί): Πρέπει να γνωρίζουμε άνω φράγμα πλήθους των δεδομένων μας. Μπορεί σημαντικό ποσό δεσμευμένης μνήμης να μένει αχρησιμοποίητο. Που βελτιστοποιούν το χρόνο της αναζήτησης. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 3 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 4 / 46
Συνδεδεμένες λίστες: Εισαγωγή (2/2) Απλά Συνδεδεμένη Λίστα Συλλογή δυναμικά δεσμευμένων κόμβων με συνδέσεις μεταξύ τους. Εξυπηρετεί πολλές περιπτώσεις αποθήκευσης δεδομένων. Διαφορετικές υλοποιήσεις με διαφορετικές εκδοχές βασικών πράξεων. Βάση για άλλες δομές αποθήκευσης αντί του πίνακα: στοίβες, ουρές. Εκτός αν απαιτείται τυχαία προσπέλαση σε μεμονωμένα στοιχεία με τη χρήση δείκτη, π.χ., A[2]. Σύνολο κόμβων που αποθηκεύουν αντικείμενα (δεδομένα). Κάθε κόμβος στη λίστα «δείχνει» τον επόμενο, εκτός από τον τελευταίο. Κόμβος = αντικείμενο + αναφορά στον επόμενο κόμβο. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 5 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 6 / 46 Βασικές Πράξεις Λίστας Κόμβος Λίστας στην αρχή της λίστας / στο τέλος της λίστας, μετά από κόμβο με δεδομένο κλειδί, πρώτου / τελευταίου κόμβου της λίστας, ενός κόμβου με δεδομένο κλειδί, κόμβου με δεδομένο κλειδί. ( ) ( ) ( ) ( ) ( ) Node { i D a t a ; O b j e c t ddata ; Node next ; Node ( id, O b j e c t dd ) { i D a t a = i d ; ddata = dd ; d i s p l a y ( ) { System. out. p r i n t l n ( { + i D a t a +, + ddata. t o S t r i n g ( ) + ) ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 7 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 8 / 46
Εισαγωγή σε Απλά Συνδεδεμένη Λίστα Διαγραφή από Απλά Συνδεδεμένη Λίστα (1/2) Πρέπει να οριστεί η θέση που θα γίνει η εισαγωγή. Έστω current η θέση μετά την οποία θα γίνει η εισαγωγή. tmp. next = c u r r e n t. next ; c u r r e n t. next = tmp ; Διαγραφή του στοιχείου x από τη λίστα. Έστω current η θέση του κόμβου πριν το x. Ο δείκτης προς τον επόμενο του current πρέπει να παρακάμψει τον x. c u r r e n t. next = ( c u r r e n t. next ). next ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 9 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 10 / 46 Βασική Υλοποίηση Υλοποίηση (1/4): Κλάση LinkedList Απλά Συνδεδεμένη Λίστα στην αρχή της λίστας, μετά από κόμβο με δεδομένο κλειδί. L i n k e d L i s t { Node f i r s t ; L i n k e d L i s t ( ) { f i r s t = ; από την αρχή της λίστας, κόμβου με δεδομένο κλειδί. isempty ( ) { ( f i r s t == ) ; κόμβου με δεδομένο κλειδί. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 11 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 12 / 46
Υλοποίηση: Εισαγωγή στην + Διαγραφή από την Αρχή Υλοποίηση: Κλάση LinkedList i n s e r t F i r s t ( id, O b j e c t dd ) { Node newnode = Node ( id, dd ) ; newnode. next = f i r s t ; f i r s t = newnode ; O b j e c t d e l e t e F i r s t ( ) { ( isempty ( ) ) ( ) ; Node tmp = f i r s t ; f i r s t = f i r s t. next ; ( tmp. ddata ) ; d i s p l a y L i s t ( ) { System. out. p r i n t ( L i s t ( f i r s t > l a s t ) : ) ; Node c u r r e n t = f i r s t ; ( c u r r e n t!= ) { c u r r e n t. d i s p l a y ( ) ; c u r r e n t = c u r r e n t. next ; System. out. p r i n t l n ( ) ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 13 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 14 / 46 Υλοποίηση: Αναζήτηση Κόμβου Node findnode ( key ) { ( isempty ( ) ) ( ) ; Node c u r r e n t = f i r s t ; ( c u r r e n t. i D a t a!= key ) { ( c u r r e n t. next == ) ; c u r r e n t = c u r r e n t. next ; ( c u r r e n t ) ; Αυτή η μέθοδος είναι διότι: επιστρέφει αντικείμενο τύπου Node «εσωτερικό» της λίστας, δε θέλουμε ο χρήστης να μπορεί να «πειράζει» τέτοια αντικείμενα, εξυπηρετεί άλλες ( ) μεθόδους: find, inserta er, delete Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 15 / 46 Υλοποίηση: Αναζήτηση (Δεδομένων) O b j e c t f i n d ( key ) { Node somenode = findnode ( key ) ; ( somenode == ) ( ) ; ( somenode. ddata ) ; Αυτή η μέθοδος είναι : διαθέσιμη στον τελικό χρήστη, για την ανάκτηση των δεδομένων του, χρησιμοποιεί εσωτερικά την findnode για ανάκτηση του σχετικού κόμβου. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 16 / 46
Υλοποίηση: «Εισαγωγή μετά από» i n s e r t A f t e r ( key, id, O b j e c t dd ) { Node p r e v i o u s = findnode ( key ) ; ( p r e v i o u s == ) ( ) ; { Node newnode = Node ( id, dd ) ; newnode. next = p r e v i o u s. next ; p r e v i o u s. next = newnode ; ( ) ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 17 / 46 Υλοποίηση: Διαγραφή με Κλειδί O b j e c t d e l e t e ( key ) { ( isempty ( ) ) ( ) ; Node c u r r e n t = f i r s t ; Node p r e v i o u s = f i r s t ; ( c u r r e n t. i D a t a!= key ) { ( c u r r e n t. next == ) ( ) ; { p r e v i o u s = c u r r e n t ; c u r r e n t = c u r r e n t. next ; ( c u r r e n t == f i r s t ) f i r s t = f i r s t. next ; p r e v i o u s. next = c u r r e n t. next ; ( c u r r e n t. ddata ) ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 18 / 46 L i n k e d L i s t A p p { main ( S t r i n g [ ] a r g s ) { L i n k e d L i s t a L i s t = L i n k e d L i s t ( ) ; a L i s t. i n s e r t F i r s t ( 2 2, 2. 9 9 ) ; a L i s t. i n s e r t F i r s t ( 4 4, 4. 9 9 ) ; a L i s t. i n s e r t F i r s t ( 6 6, 6. 9 9 ) ; a L i s t. i n s e r t F i r s t ( 8 8, 8. 9 9 ) ; a L i s t. d i s p l a y L i s t ( ) ; Node f = a L i s t. f i n d ( 4 4 ) ; ( f! = ) System. out. p r i n t l n ( Found Node with key + f. i D a t a!!! ) ; System. out. p r i n t l n ( Can t f i n d node. ) ; Node d = a L i s t. d e l e t e ( 6 6 ) ; ( d! = ) System. out. p r i n t l n ( Deleted node with key + d. i D a t a ) ; System. out. p r i n t l n ( Can t d e l e t e node. ) ; a L i s t. d i s p l a y L i s t ( ) ; Παράδειγμα: Υλοποίηση Στοίβας S t a c k { L i n k e d L i s t l i s t ; S t a c k ( ) { l i s t = L i n k e d L i s t ( ) ; isempty ( ) { ( l i s t. isempty ( ) ) ; push ( O b j e c t dd ) { l i s t. i n s e r t F i r s t ( 0, dd ) ; O b j e c t pop ( ) { ( l i s t. d e l e t e F i r s t ( ) ) ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 19 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 20 / 46
Παράδειγμα: Υλοποίηση Ουράς με Λίστα Η ουρά χρειάζεται στο ένα άκρο, από το άλλο. Απαιτείται η συντήρηση αναφοράς στο τέλος της λίστας: L i s t { Node f i r s t ; Node l a s t ; Υλοποίηση των επιπλέον μεθόδων: insertlast ( id, Object dd); first, αν χρειάζεται. Object deletelast () ; first, αν χρειάζεται. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 21 / 46 Παράδειγμα: Υλοποίηση Ουράς Queue { L i n k e d L i s t l i s t ; Queue ( ) { l i s t = L i n k e d L i s t ( ) ; isempty ( ) { ( l i s t. isempty ( ) ) ; i n s e r t ( id, O b j e c t dd ) { l i s t. i n s e r t L a s t ( id, dd ) ; O b j e c t remove ( ) { ( l i s t. d e l e t e F i r s t ( ) ) ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 22 / 46 Απλοποίηση Υλοποίησης Κόμβος κεφαλής (Head Node) Προβλήματα στην περιγραφή λειτουργιών απλά συνδεδεμένης λίστας. Κάθε φορά που διαγράφεται ένας κόμβος, πρέπει να βρίσκουμε τον προηγούμενο του. διαγραφή πρώτου κόμβου. Ο κόμβος που εισάγουμε πρέπει να ακολουθεί έναν κόμβο. εκτός κι αν είναι ο πρώτος. Απλοποιούμε την υλοποίηση εξασφαλίζοντας την ιδιότητα: (Head) της λίστας: επιπλέον κόμβος που δεν διατηρεί δεδομένα. εξυπηρετεί στο να ικανοποιηθεί η απαίτηση κάθε κόμβος να έχει έναν προηγούμενο κόμβο στη λίστα. Με χρήση του κόμβου κεφαλής μπορούμε να διαχειριστούμε εύκολα τις ειδικές περιπτώσεις εισαγωγής και διαγραφής στην της λίστας. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 23 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 24 / 46
Κόμβος κεφαλής (Head Node) Πλεονεκτήματα Χρήσης Κόμβου Κεφαλής Ενοποίηση των insertfirst και inserta er : Υλοποιούμε μία μέθοδο: myinserta er (Node node, id, Object dd) head. ddata = ; Χρήση κόμβου κεφαλής για συνδεδεμένη λίστα. i n s e r t A f t e r ( key, id, O b j e c t dd ) { node= findnode ( key ) ; m y I n s e r t A f t e r ( node, id, dd ) ; i n s e r t F i r s t ( id, O b j e c t dd ) { m y I n s e r t A f t e r ( head, id, dd ) ; head. next = ; Κενή λίστα όταν χρησιμοποιούμε κόμβο κεφαλής. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 25 / 46 έλεγχος isempty() στην αρχή, ειδικός χειρισμός του πρώτου κόμβου (το if -statement στο τέλος). Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 26 / 46 Διαγραφή / Εισαγωγή στο Τέλος Με την έως τώρα υλοποίηση απαιτούν O(n) χρόνο. Διάσχιση όλης της λίστας μέχρι εύρεσης του τελευταίου κόμβου. Διαγραφή τελευταίου κόμβου / Εισαγωγή μετά τον τελευταίο κόμβο. Σε O(1) χρόνο, αν συντηρούμε αναφορά last, επιπλέον της first. Διπλά Συνδεδεμένες Λίστες Αν έχουμε άδεια λίστα με κόμβο κεφαλή, τότε last = ; Η Εισαγωγή μετά από τον τελευταίο κόμβο ελέγχει αν last == και εισάγει μετά από την κεφαλή, αν αυτό είναι αληθές. Η Διαγραφή του τελευταίου κόμβου ελέγχει αν last == και επιστρέφει, αν αυτό είναι αληθές. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 27 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 28 / 46
Διπλά Συνδεδεμένες Λίστες Διπλά Συνδ/νες Λίστες: Πλεονεκτήματα Δυνατότητα αποδοτικής διάσχισης της λίστας προς μία κατεύθυνση. Δεν μπορούμε να γνωρίζουμε τον προηγούμενο δεδομένου κόμβου. Κάθε κόμβος έχει (next) και (previous). Δυνατότητα διάσχισης διπλής κατεύθυνσης (π.χ., εκτύπωση σε ανάποδη σειρά), εφόσον διατηρούμε και μια αναφορά last. : απλοποιείται, γιατί δε χρειάζεται να κρατάμε τον προηγούμενο: είναι διαθέσιμος σαν current. previous ή από κόμβο με δεδομένο κλειδί. Μπορούμε να έχουμε και τελείως ανάλογα (tail node). Συντηρούν την αναλλοίωτη ιδιότητα ενός και ενός για κάθε κόμβο δεδομένων. Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 29 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 30 / 46 Διπλά Συνδεδεμένη Λίστα Ορισμός Κόμβου Διπλά Συνδεδεμένης Λίστας D o u b l y L i n k e d L i s t N o d e { Κάθε κόμβος έχει δύο αναφορές σε άλλους κόμβους. Μία προς τον επόμενο και μια προς τον προηγούμενο κόμβο. i D a t a ; O b j e c t ddata ; D o u b l y L i n k e d L i s t N o d e next ; D o u b l y L i n k e d L i s t N o d e p r e v i o u s ; D o u b l y L i n k e d L i s t N o d e ( id, O b j e c t dd ) { i D a t a = i d ; ddata = dd ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 31 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 32 / 46
Κενή Διπλά Συνδεδεμένη Λίστα με Κεφαλή & Ουρά Διπλά Συνδ/νη Λίστα: Εισαγωγή D o u b l y L i n k e d L i s t ( ) { head = D o u b l y L i n k e d L i s t N o d e ( 0, ) ; t a i l = D o u b l y L i n k e d L i s t N o d e ( 0, ) ; head. next = t a i l ; t a i l. p r e v i o u s = head ; newnode = D o u b l y L i n k e d L i s t N o d e ( id, dd ) ; 1. newnode. p r e v i o u s = c u r r e n t ; 2. newnode. next = c u r r e n t. next ; 3. newnode. p r e v i o u s. next = newnode ; 4. newnode. next. p r e v i o u s = newnode ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 33 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 34 / 46 Διπλά Συνδ/νη Λίστα: Διαγραφή Ορισμός Διπλά Συνδ/νης Λίστας χωρίς Κεφαλή και Ουρά D o u b l y L i n k e d L i s t { D o u b l y L i n k e d L i s t N o d e f i r s t ; D o u b l y L i n k e d L i s t N o d e l a s t ; D o u b l y L i n k e d L i s t ( ) { f i r s t = ; l a s t = ; Θα εξετάσουμε c u r r e n t. p r e v i o u s. next = c u r r e n t. next ; c u r r e n t. next. p r e v i o u s = c u r r e n t. p r e v i o u s ; στην αρχή της λίστας: insertfirst στο τέλος της λίστας: insertlast μετά από κόμβο με συγκεκριμένο κλειδί: inserta er ( key) Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 35 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 36 / 46
Εισαγωγή στην Αρχή Διπλά Συνδ/νης Λίστας Εισαγωγή στο Τέλος Διπλά Συνδ/νης Λίστας D o u b l y L i n k e d L i s t N o d e newnode= D o u b l y L i n k e d L i s t N o d e ( id, dd ) ; ( isempty ( ) ) l a s t = newnode ; { f i r s t. p r e v i o u s = newnode ; newnode. next = f i r s t ; D o u b l y L i n k e d L i s t N o d e newnode= D o u b l y L i n k e d L i s t N o d e ( id, dd ) ; ( isempty ( ) ) f i r s t = newnode ; { l a s t. next = newnode ; newnode. p r e v i o u s = l a s t ; l a s t = newnode ; f i r s t = newnode ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 37 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 38 / 46 Εισαγωγή μετά από κόμβο σε Διπλά Συνδ/νη Λίστα i n s e r t A f t e r ( key, id, O b j e c t dd ) { D o u b l y L i n k e d L i s t N o d e c u r r e n t = f i r s t ; ( c u r r e n t. i D a t a!= key ) { c u r r e n t = c u r r e n t. next ; ( c u r r e n t == ) ( ) ; D o u b l y L i n k e d L i s t N o d e newnode = D o u b l y L i n k e d L i s t N o d e ( id, dd ) ; ( c u r r e n t == l a s t ) { newnode. next = ; l a s t = newnode ; { newnode. next = c u r r e n t. next ; c u r r e n t. next. p r e v i o u s = newnode ; newnode. p r e v i o u s = c u r r e n t ; c u r r e n t. next = newnode ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 39 / 46 ( c u r r e n t == l a s t ) { newnode. next = ; l a s t = newnode ; { newnode. next = c u r r e n t. next ; c u r r e n t. next. p r e v i o u s = newnode ; newnode. p r e v i o u s = c u r r e n t ; c u r r e n t. next = newnode ; ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 40 / 46
Διαγραφή από την Αρχή Διπλά Συνδ/νης Λίστας Διαγραφή από το Τέλος Διπλά Συνδ/νης Λίστας ( f i r s t. next == ) l a s t = ; { f i r s t. next. p r e v i o u s = ; f i r s t = f i r s t. next ; Node d e l e t e L a s t ( ) { Node temp = l a s t ; ( f i r s t. next == ) f i r s t = ; l a s t. p r e v i o u s. next = ; l a s t = l a s t. p r e v i o u s ; temp ; Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 41 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 42 / 46 O b j e c t d e l e t e ( key ) { Node c u r r e n t = f i r s t ; ( c u r r e n t. i D a t a!= key ) { c u r r e n t = c u r r e n t. next ; ( c u r r e n t == ) ( ) ; ( c u r r e n t == f i r s t ) f i r s t = c u r r e n t. next ; c u r r e n t. p r e v i o u s. next = c u r r e n t. next ; ( c u r r e n t == l a s t ) l a s t = c u r r e n t. p r e v i o u s ; c u r r e n t. next. p r e v i o u s = c u r r e n t. p r e v i o u s ; ( c u r r e n t. ddata ) ; Κυκλικά Συνδεδεμένες Λίστες Ο τελευταίος κόμβος έχει ως επόμενο κόμβο τον πρώτο κόμβο. Έστω: h πρώτος κόμβος στη λίστα. t τελευταίος κόμβος στη λίστα. t.next = h; h.previous = t; Δε χρειάζεται κόμβος-κεφαλή (head) Δε χρειάζεται κόμβος-ουρά (tail) Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 43 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 44 / 46
Κυκλική Διπλά Συνδεδεμένη Λίστα Σύνοψη Οι λίστες σαν δομή μοιάζουν στις βασικές πράξεις με τους πίνακες. Υποστηρίζουν δυναμική δέσμευση μνήμης (αντίθετα με τους πίνακες). Υποστηρίζουν κάποιες διαγραφές σε O(1) χρόνο (αντίθετα με πίνακες). : Από αρχή / τέλος σε O(1) χρόνο. Ενδιαμέσως (μετά / πριν από κόμβο) σε O(n) χρόνο (λόγω ). f i r s t. p r e v i o u s = l a s t ; l a s t. next = f i r s t ; : σε O(n) χρόνο. μπορούμε να έχουμε τόσο καλή αναζήτηση σε μια λίστα (απλά / διπλά συνδεδεμένη / κυκλική) όσο σε έναν πίνακα? Πώς / Γιατί? Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 45 / 46 Ο. Τελέλης Πανεπιστήμιο Πειραιώς Δομές Δεδομένων 46 / 46