Last time, we went over the linked list and the functions defined for it in SLL.cpp. The program listTest.cpp is a simple driver to exercise the list class. While you're running it, you can use the debugger to examine the contents of any element in your list. The command "Show as C string" (Command-Shift-S) in the data menu will help you see what's going on by showing the names you put in the list as you typed them in.

So, you can vary the implementation somewhat, but the important things to know are:

Doubly Linked Lists

Doubly linked lists solve the problem of remove taking O(n) time.

Now we know each element's predecessor as well as its successor. The cost is maintaining twice as many pointers. You can see in DLL.h that the element class now has next and previous pointers. DLL.cpp is fairly similar to SLL.cpp. Only insert and remove change. The other functions are still fine.

insert

Let's say we plan to insert Stewart Copeland after John Flansburgh. First we set current to John Flansburgh (remember that we insert after the current element) Second, we create a new element and maintain a pointer x to that element. The next thing to do is get a pointer to the successor of the current element.

Now, update the previous pointer in the element that is going to become x's successor to point to x.

Change the new element's next pointer to point to the successor. Then change the current element's next to point to the new element.

Update the new element's previous pointer, and set the new element to be the current element, and you're done. The procedure for the end cases (inserting before the head and after the tail) are just a little more complicated, so it may be worth your while to go through the code and see how they differ.

remove

Now the good part -- remove is now O(1) time. Of course, we have to take care to change the previous pointers, but that's a small price to pay for reducing an O(n)-time operation to an O(1)-time one. So, let's say we want to remove John Flansburgh

First, we look at the element before the current one and change its next pointer to point to where current next points.

Then, fix the previous pointer of current's successor to point to current's predecessor.

Finally, save a pointer to current's predecessor, delete current, and set current to be the predecessor.

Once again, it may be worth your while to see how the special cases of removing the head and tail differ. Or will it? WIBNIF we could get rid of those nasty exceptions, and just remove or add anything anywhere the same way?

Circular Doubly Linked List with Sentinel

Okay, so we can. Here's how: You know how all the easy cases were when the element in question was in the middle? We just change the list so that all the elements are in the middle. We do that by making the list a circle.

The element on the far left is the sentinel, a distinguished element used to close the circle. Any circular DLL has exactly one sentinel, no matter how many other elements there are. Because the circular list has no NULL pointers, there are no special cases.

sentinelDLL.h defines elements just the same as in DLL.h, but in the list, the head and tail are replaced by the sentinel.

sentinelDLL.cpp holds the new functions. The constructor is changed. The result of the constructor now looks like this:

The destructor

insert and remove

other functions

isEmpty has now changed to simply return whether or not the sentinel points to itself. find and print now use sentinel-style loops. headOf sets current to the sentinel, and tailOf sets current to sentinel->previous.

To Index Previous Next