Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

ISBN13: 9781449646752 C++ Plus Data Structures 5th Edition Chapter 6 Question 12

ID: 673986 • Letter: I

Question

ISBN13: 9781449646752 C++ Plus Data Structures 5th Edition

Chapter 6

Question 12

Of the three variations of linked lists (circular, with head and trailer nodes, and doubly linked), which would be most appropriate for each of the following applications?

A. You want to search a list for a key and return the keys of the two elements that come before it and the keys of the two elements that come after it.

B. A text file contains integer elements, one per line, sorted from smallest to largest. You must read the values from the file and create a sorted linked list containing the values.

C. A list is short and frequently becomes empty. You want a list that is optimal for inserting an element into the empty list and deleting the last element from the list.

Explanation / Answer

Special Linked Lists

Introduction ( to Special Linked Lists):

In this lecture we will cover some standard variations of simple linear-linked

lists: circular, header, trailer, and doubly-linked lists. We will discuss the

tradeoffs between using these lists and simple linear-linked lists.

Circular Lists:

Standard linearly-linked list are used more often than any of these variants,

but it is interesting to see what tradeoffs the variants allow. The web reading

discusses these special lists in more detail. It would be an excellent idea to

hand simulate the methods there to become more familiar with how these special

lists are processed.

In "circular lists", the "rear" node doesn't store null in its .next field,

but instead has its .next field refer to the "front" node in the list. I put

"rear" and "front" in quotes because in a circular list, there really is not

an obvious front or rear node (unless we have a special reference into the

list named front or rear).

There are a few applications for circular lists. One allows us to represent

and efficiently process a queue by having a single reference to the "rear"

value in the queue (instead of one to the front and one to the rear). Note that

in a circular list, the node one beyond the rear is the front, so both the

front and rear nodes in a queue are easily reached from the rear node (and all

"action" in a queue is at the front/remove or end/add).

If rear refers to the last node an a queue represented by a circular list,

here is code to add to the rear of the queue. Draw a picture of an empty

list, a list with one node, and a list with 3-5 nodes and simulate this

code on those lists to see that it is correct.

if (rear != null)

    rear = rear.next = new LN(someValue,rear.next);

else

    rear = new LN(someValue,null);

    rear.next = rear;               //Make it a 1 node circular list

}

   

If rear refers to the last node an a queue represented by a circular list,

here is code to remove the front of the queue. Draw a picture of an empty

list, a list with one node, and a list with 3-5 nodes and simulate this

code on those lists to see that it is correct.

if (rear != null) {

    int frontValue = rear.next.value;

    if (rear.next == rear)             //Just 1 node in the list?

      rear = null;

    else

      rear.next = rear.next.next;

}

If we had to represent a huge number of queues, most of which are empty,

using a class that stored one reference to the rear of a circular list will

save space when compared to using a class that stores two references to the

front and rear of a linear-linked list.

Header Lists:

In "header lists", every list has a special (valueless) header node at the

front. So, an "emtpy header list" would have one list node on it. All "real"

nodes in the list come after the header. By using a header node, special cases

in the code are reduced. For example, I wrote the following code for adding a

value to the end of a linked list, in a class with front and rear instance

variables.

    if (front == null)

       front = rear = new ListNode(e,null);

    else

       rear = rear.next = new ListNode(e,null);

If the linked list were a header list, if it were empty the front and rear

would both be set to the header node (whose .next is set to null in the

constructor). We could remove the special case above (front is never null) and

simplify the code to the if-less

    rear = rear.next = new ListNode(e,null);

In a header list, one never changes front: it always refers to the header.

So every node we manipulate in the list has a previous node referring to it.

This simplifies the code for adding and removing nodes, although for other

operations (like traversal) we must remember to skip the header node.

Trailer Lists:

In "trailer lists", every list has a special (valueless) trailer node at the

end. So, an "emtpy trailer list" would have one list node on it. All "real"

nodes in the list come before the trailer. By using a trailer node, we can

remove a node that we have a reference to, without having a reference to the

node before it!

I'll show this "trick" in class; hand simulate the following code again to see

how it works (have r refer to the first, last and middle node in a trailer

list). Note that the "last" node IN a trailer list is the one before the

trailer node. This code works correctly only if the node r refers to is

followed by another node (which is guaranteed for a trailer list; and r -the

node to be removed- will never refer to the trailer itself).

    r.value = r.next.value;

    r.next = r.next.next;

Doubly-Linked Lists:

In "doubly linked lists", each node refers to the one after it and the one

before it. So, we can traverse the list in both directions: we can reach any

node from any other node. The cost is an increase of space needed to store a

doubly-linked list: twice as many references in each node (2 instead of 1) and

the requirement to change twice as many references when we mutate the list,

e.g., when adding/removing values.

For example, when we add a node to a doubly linked list, we need to make

the .next of the one before it refer to the new node, and the .previous of

the one after it refer to the new node. And in the new node itself, we need

to set its .next as well as its .previous instance variables

If the node added is at the front or rear of a doubly linked list, there are

all sorts of special cases to handle. By having both a header and a trailer

node in a doubly linked list, we can simplify this code and remove all the

special cases. But now, even an empty list has two nodes: the header/trailer.

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote