Linked Lists
3.2 Basic Operations on a Linked List
For illustrative purposes, we assume that we have a linked list of integers. We ignore, for the moment, how the list might be built.
3.2.1 Counting the Nodes in a Linked List
Perhaps the simplest operation is to count the number of nodes in a list. To illustrate, we write a function that, given a pointer to a linked list, returns the number of nodes in the list.
Before we write the function, let’s see how we can traverse the items in the list, starting from the first one.
Suppose top points to the head of the list. Consider the following code:
Node curr = top;
while (curr != null) curr = curr.next;
Initially, curr points to the first item, if any, in the list. If it is not null, the following statement is executed:
curr = curr.next;
This sets curr to point to “whatever the current node is pointing to,” in effect, the next node. For example, consider the following list:
Initially,
• curr points to (the node containing) 36. Since curr is not null, it is set to point to whatever 36 is pointing to, that is, (the node containing) 15.
• The while condition is tested again. Since curr is not null, curr = curr.next is executed, setting curr to point to whatever 15 is pointing to, that is, 52.
• The while condition is tested again. Since curr is not null, curr = curr.next is executed, setting curr to point to whatever 52 is pointing to, that is, 23.
• The while condition is tested again. Since curr is not null, curr = curr.next is executed, setting curr to point to whatever 23 is pointing to, that is, null.
• The while condition is tested again. Since curr is null, the while loop is no longer executed.
Note that each time curr is not null, we enter the while loop. But the number of times that curr is not null is exactly the same as the number of items in the list. So, in order to count the number of items in the list, we just have to count how many times the while body is executed.
To do this, we use a counter initialized to 0 and increment it by 1 inside the while loop. We can now write the function as follows (we call it length):
public static int length(Node top) { int n = 0;
Node curr = top;
while (curr != null) { n++;
curr = curr.next;
}
return n;
}
Note that if the list is empty, curr will be null the first time, and the while loop will not be executed. The function will return 0, the correct result.
Strictly speaking, the variable curr is not necessary. The function will work fine if we omit curr and replace curr by top in the function. At the end of the execution of the function, top will be null.
You may be worried that you have lost access to the list, but do not be. Remember that top in length is a copy of whatever variable (head, say) is pointing to the list in the calling function. Changing top has no effect whatsoever on head. When length returns, head is still pointing to the first item in the list.
3.2.2 Searching a Linked List
Another common operation is to search a linked list for a given item. For example, given the following list, we may want to search for the number 52:
top
36 15 52 23
top
36 15 52 23
Our search should be able to tell us that 52 is in the list. On the other hand, if we search for 25, our search should report that 25 is not in the list.
Suppose the number we are searching for is stored in the variable key. The search proceeds by comparing key with each number in the list, starting from the first one. If key matches with any item, we have found it. If we get to the end of the list and key does not match any item, we can conclude that key is not in the list.
We must write the logic so that the search ends if we find a match or we reach the end of the list. Put another way, the search continues if we have not reached the end of the list and we do not have a match. If curr points to some item in the list, we can express this logic as follows:
while (curr != null && key != curr.num) curr = curr.next;
Java guarantees that the operands of && are evaluated from left to right and evaluation ceases as soon as the truth value of the expression is known, in this case, as soon as one operand evaluates to false or the entire expression has been evaluated. We take advantage of this by writing the condition curr != null first. If curr is null, the && is immediately false, and the second condition key != curr.num is not evaluated.
If we wrote the following and curr happens to be null, our program will crash when it tries to retrieve curr.num:
while (key != curr.num && curr != null) curr = curr.next; //wrong
In effect, this asks for the number pointed to by curr, but if curr is null, it does not point to anything. We say we are trying to “dereference a null pointer,” which is an error.
Let’s write the search as a function that, given a pointer to the list and key, returns the node containing key if it is found. If it’s not found, the function returns null.
We assume the Node declaration from the previous section. Our function will return a value of type Node. Here it is:
public static Node search(Node top, int key) { while (top != null && key != top.num) top = top.next;
return top;
}
If key is not in the list, top will become null, and null will be returned. If key is in the list, the while loop is exited when key is equal to top.num; at this stage, top is pointing to the node containing key, and this value of top is returned.
3.2.3 Finding the Last Node in a Linked List
Sometimes, we need to find the pointer to the last node in a list. Recall that the last node in the list is distinguished by its next pointer being null. Here is a function that returns a pointer to the last node in a given list. If the list is empty, the function returns null.
public static Node getLast(Node top) { if (top == null) return null;
while (top.next != null) top = top.next;
return top;
}
We get to the while statement if top is not null. It therefore makes sense to ask about top.next. If this is not null, the loop is entered, and top is set to this non-null value. This ensures that the while condition is defined the next time it is executed. When top.next is null, top is pointing at the last node, and this value of top is returned.