ΕΝΟΤΗΤΑ 6 ΛΙΣΤΕΣ ΠΑΡΑΛΕΙΨΗΣ (SKIP LISTS)
Ταχεία Αναζήτηση Σε πίνακα: δυαδική αναζήτηση (binary search) σε ταξινοµηµένο πίνακα O(log n) Σε δένδρο: αναζήτηση σε ισοζυγισµένο δένδρο O(log n) Σε λίστα: Μπορούµε να επιτύχουµε αναζήτηση σε O(log n) χρόνο? λίστες παράλειψης (skip lists) (αναφέρονται και ως λίστες αναπηδήσεως) (Pugh, 1990) 2
Λίστες Παράλειψης (Skip Lists) Τέλεια Οργανωµένη Λίστα Παράλειψης Έχουµε επίπεδα δεικτών: ανά 1 στοιχείο, ανά 2, ανά 4, κ.ο.κ. Οι δείκτες που συνδέουν όλα τα στοιχεία στη σειρά είναι οι δείκτες επιπέδου 0. Οι δείκτες που συνδέουν τα (k * 2 i ) οστά στοιχεία (k 1) είναι οι δείκτες επιπέδου i. Ένας κόµβος κεφαλή δείχνει στον 1ο κόµβο κάθε επιπέδου. Χρειάζονται το πολύ 1+logn δείκτες ανά στοιχείο για µια λίστα παράλειψης µε n στοιχεία. Τα περισσότερα στοιχεία δεν χρειάζεται να κρατούν τόσους πολλούς δείκτες: Μόνο τα µισά στοιχεία χρειάζονται έναν ακόµη δείκτη, και από αυτά µόνο τα µισά έναν ακόµη, κ.ο.κ. Χώρος n + n/2 + n/4 + + 1 2 n 3
Λίστες Παράλειψης Αλγόριθµος Αναζήτησης κλειδιού Κ Ξεκινώντας µε τους δείκτες του υψηλότερου επιπέδου: Ακολούθησε δείκτες µέχρι να βρεθεί ένα στοιχείο µε κλειδί t το οποίο είναι Κ. Αν t = Κ, τότε το κλειδί Κ βρέθηκε! αλλιώς (δηλ., t > Κ), οπισθοδροµούµε στο προηγούµενο στοιχείο και επαναλαµβάνουµε ακολουθώντας δείκτες του επόµενου χαµηλότερου επιπέδου. Αν εκτελώντας την παραπάνω διαδικασία στο επίπέδο 0, βρούµε κλειδί t > Κ, τότε το κλειδί Κ δεν υπάρχει στη λίστα παράλειψης. Πως γίνεται η οπισθοδρόµηση; Σε µια τέλεια οργανωµένη λίστα παράλειψης, ποια θα ήταν η πολυπλοκότητα του αλγορίθµου; Είναι οι τέλεια οργανωµένες λίστες παράλειψης πρακτικές στην περίπτωση που έχουµε εισαγωγές και εξαγωγές στοιχείων; 4
Υλοποίηση Λειτουργίας Αναζήτησης Ν: µέγιστο δυνατό πλήθος στοιχείων της λίστας MaxLevel = logn : µέγιστο δυνατό επίπεδο Κάθε κόµβος µιας λίστας παράλειψης έχει πεδία: o Key o Info o έναν πίνακα Forward από δείκτες. Η λίστα παράλειψης είναι δοµή µε πεδία: o Header: δείκτης σε dummy node (που περιέχει πίνακα Forward από MaxLevel δείκτες). Ο δείκτης Header->Forward[j] δείχνει στον πρώτο κόµβο στο επιπέδου j. o Level: ακέραιος (=το τρέχον µέγιστο επίπεδο) Αρχικά, Level = 0, και όλοι οι δείκτες NULL. function SkipListLookUp(key K, slist_ptr L): item_ptr /* επιστρέφει δείκτη στον κόµβο µε κλειδί Κ εάν υπάρχει στη λίστα παράλειψης, διαφορετικά NULL */ P = L->Header; for j from L->Level downto 0 do while ((P->Forward[j])->Key < K) do P = P->Forward[j]; P = P->Forward[0]; if (P->Key == K) return P; else return NULL; 5
Λίστες Παράλειψης Μη-τέλεια οργανωµένες λίστες παράλειψης εισάγουµε τυχαιότητα στην κατασκευή Βασική Ιδέα Για κάθε επίπεδο: έχουµε «περίπου» το ίδιο πλήθος κόµβων όπως και στην τέλεια οργανωµένη λίστα (δηλ., το επίπεδο i+1 έχει περίπου τους µισούς κόµβους από ότι το επίπεδο i), αλλά οι κόµβοι αυτοί είναι διασκορπισµένοι τυχαία µέσα στη λίστα του επιπέδου. Η εισαγωγή γίνεται ως εξής: βρίσκουµε την κατάλληλη θέση εισαγωγής στη λίστα του επιπέδου 0 (που περιέχει όλα τα στοιχεία), επιλέγουµε το επίπεδου του κόµβου µε τυχαίο τρόπο, αλλά µε βάση τον κανόνα: «Για κάθε επίπεδο j, η πιθανότητα να επιλεγεί το j ως επίπεδο του κόµβου είναι διπλάσια από την πιθανότητα να επιλεγεί το j+1». συνδέουµε τον κόµβο στις λίστες των επιπέδων. 6
Υλοποίηση Λειτουργίας Εισαγωγής function RandomLevel(): integer /* τυχαίο επίπεδο µεταξύ 0 και MaxLevel */ v = 0; while (Random() < ½ && v < MaxLevel) do v = v+1; return v; procedure SkipListInsert(key K, info I, slist_ptr L) /* εισάγει πληροφορία Ι µε κλειδί Κ στη λίστα παράλειψης L */ P = L->Header; for j from L->Level downto 0 while ((P->Forward[j])->Key < K) do P = P->Forward[j]; Update[j] = P; /* πίνακας προηγούµενων κόµβων*/ P = P->Forward[0]; if (P->Key == K) P->Info = I; /* ενηµέρωση */ else NewLevel = RandomLevel(); if (NewLevel > L->Level) for j from L->Level+1 to NewLevel do Update[j] = L->Header; L->Level = NewLevel; /* κατασκευή νέου κόµβου & σύνδεση */ P = new_struct(skiplistnode); P->Key = K; P->Info = I; for i from 0 to NewLevel /* σύνδεση */ P->Forward[i] = (Update[i])->Forward[i] ; (Update[i])->Forward[i] = P; 7