Transcript Skip Lists

CS 261 - Spring 2008
Skip Lists
Skip Lists Advantages
• Ordinary linked lists and arrays have
fast (O(1)) addition, but slow search
• Sorted Vectors have fast search
(O(log n)) but slow insertion
• Skip Lists have it all - fast (O(log n))
addition, search and removal times!
• Cost - Slightly more complicated
Start with an ordered list
Lets begin with a simple ordered list, sentinel on
the left, single links.
Start of the struct definition
struct skiplist {
struct skiplink * sentinel;
int size;
};
Operations
• Add (Object newValue) - find correct
location, add the value
• Contains (Object testValue) - find
correct location, see if its what we want
• Remove (Object testValue) - find correct
location, possibly remove it
• See anything in common?
Slide Right
struct skiplink * slideRight (struct skiplink * current,
EleType testValue) {
while ((current->next != 0) &&
LT(current>next->value, testValue))
current = current->next;
return current;
}
Finds the link RIGHT BEFORE the place the value we
want should be, if it is there
Add (EleType newValue)
void add (struct skiplist* lst, EleType newValue) {
struct skiplink * current =
slideRight(lst->sentinel, newValue);
struct link * newLink = (struct link *)
malloc(sizeof(struct link));
assert (newLink != 0);
newLink->value = newValue;
newLink->next = current->next;
current->next = newLink;
lst->size++;
}
Contains (EleType testValue)
int contains (struct skip *lst, EleType testValue) {
struct skiplink * current =
slideRight (lst->leftSentinel, testValue);
if ((current->next != 0) &&
(EQ(testValue, current->next->value)))
return true;
return false;
} /* reminder, this is one level list, not real skiplist */
Remove (Object testValue)
void remove (struct skiplist *lst, EleType testValue) {
struct skiplink * current =
slideRight (lst->leftSentinel, testValue);
if ((current->next != 0) &&
(EQ(testValue, current->next->value))) {
current->next = current->next->next;
free(current->next);
lst->size--;
}
}
Problem with Sorted List
• What’s the use?
• Add is still O(n), so is contains, so is
remove.
• No better than an ordinary list.
• Major problem with list - sequential
access
Why no binary search?
What if we kept a link to middle of list?
But need to keep going
And going and going
In theory it would work…..
• In theory this would work
• Would give us nice O(log n) search
• But would be really hard to maintain as
values were inserted and removed
• What about if we relax the rules, and
don’t try to be so precise…..
The power of probability
• Instead of being precise, keep a
hierarchy of ordered lists with downward
pointers
• Each list has approximately half the
elements of the one below
• So, if the bottom list has n elements,
how many levels are there??
Picture of Skip List
Slightly bigger list
Contains (Object testValue)
• Starting at topmost sentinel, slide right
• If next value is it, return true
• If next value is not what we are looking
for, move down and slide right
• If we get to bottom without finding it,
return false
Notes about contains
• See how it makes a zig-zag from top to
bottom
• On average, slide only moves one or
two positions
• So basically proportional to height of
structure
• Which is????? O( ?? )
Remove Algorithm
• Start at topmost sentinal
• Loop as follows
– Slide right. If next element is the one we
want, remove it
– If no down element, reduce size
– Move down
Notice about Remove
• Only want to decrement size counter if
at bottom level
• Again, makes zig-zag motion to bottom,
is proportional to height
• So it is again O(log n)
Add (Object newElement)
• Now we come to addition - the most complex
operation.
• Intuitive idea - add to bottom row (remember
to increment count)
• Move upwards, flipping a coin
• As long as heads, add to next higher row
• At top, again flip, if heads make new row
Add Example
Insert the following: 9 14 7 3 20
Coin toss: T H T H H T H T (insert if heads)
The Power of Chance
• At each step we flip a coin
• So the exact structure is not predictable,
and will be different even if you add the
same elements in same order over
again
• But the gross structure is predictable,
because if you flip a coin N times as N
gets large you expect N/2 to be heads.
Notes on Add
• Again proportional to height of structure,
not number of nodes in list
• So also O(log n)
• So all three operations are O(log n) !
• By far the fastest overall Bag structure
we have seen!
•
(Downside - does use a lot of memory as values are repeated. But who
cares about memory? )
int skipListContains(struct skipList* slst, EleType d) {
struct skipLink *tmp = slst->topSentinel;
while (tmp != 0) {
tmp = slideRight(tmp, d);
if ((tmp->next != 0) && EQ(d, tmp->next>value))
return 1;
tmp = tmp->down;
}
return 0;
}
void skipListAdd(struct skipList * slst, EleType d) {
struct skipLink *downLink, *newLink;
downLink = skipLinkAdd(slideRight(slst>topSentinel,d),d);
if (downLink != 0 && skipFlip()) {
newLink = newSkipLink(d, 0, downLink);
slst->topSentinel = newSkipLink(0, newLink,
slst->topSentinel);
}
slst->size++;
}
struct skipLink* skipLinkAdd(struct skipLink * current, EleType d) {
struct skipLink *newLink, *downLink;
newLink = 0;
if (current->down == 0) {
newLink = newSkipLink(d, current->next, 0);
current->next = newLink;
}
else {
downLink = skipLinkAdd(slideRight(current->down, d), d);
if (downLink != 0 && skipFlip()) {
newLink = newSkipLink(d, current->next, downLink);
current->next = newLink;
}
}
return newLink;
}
struct skipLink* newSkipLink(EleType d, struct skipLink
* nlnk, struct skipLink* dlnk) {
struct skipLink * tmp = (struct skipLink *)
malloc(sizeof(struct skipLink));
assert(tmp != 0);
tmp->value = d;
tmp->next = nlnk;
tmp->down = dlnk;
return tmp;
}
int skipFlip() { return rand() % 2; }
void skipListRemove(struct skipList* slst, EleType d) {
struct skipLink *current, *tmp;
current = slst->topSentinel;
while (current != 0) {
current = slideRight(current, d);
if (current->next != 0 && EQ(d, current->next->value)) {
tmp = current->next;
current->next = current->next->next;
free(tmp);
if (current->down != 0)
slst->size--;
}
current = current->down;
}
}