Skip to main content

Section 27.2 List Nodes

The alternate implementation of a list that we will explore is known as a linked list. A linked list is made up of a series of nodes. Each node contains a value and a pointer to the next node in the list. To make a list, we will chain together a series of nodes, with each node pointing to the next one.
A simple chain of nodes to store the list of values 12, 6, and 10 would look like this:
Three nodes. The first has data of 12 and points to the second node, which has data of 6 and points to the third node, which has data of 10 and points to nothing.πŸ”—
Figure 27.2.1. A basic Linked List

Insight 27.2.1.

In a memory diagram, each node is a box containing two parts: the data and the next pointer. The next pointer stores the address of the next node in the list, or nullptr if it is the last node.
We draw the pointer as an arrow from one node to the next, but in memory, it is just stored as a memory address.
Each node is a separate object in memory. And we make no assumptions that the nodes are stored contiguously, or even in any particular order, in memory. The only connection between the nodes, what makes them into a list, are the pointers from one node to the next.
A ListNode that was only capable of storing an integer value and a pointer to the next node could be defined like this:
struct ListNode {
    // Value stored in this node
    int data = 0;

    // Next node in list (nullptr == end of list)
    ListNode* next = nullptr;

    // Constructor
    ListNode(int value) {
        data = value;
    }
};
Note that each node (implemented as a struct to avoid public/private access control) has a constructor that initializes its value. The next pointer is initialized to nullptr by default. Which means the node is not connected to anything.

Note 27.2.2.

Structs in C++ can have constructors and member functions just like classes. The primary difference is that structs have public access by default.
To make a list node, we can use the constructor to create a new node with a given value. When we do so, we will always allocate the node on the heap using new, which means we need to use a pointer to keep track of where the node is stored. Here is how we could create three nodes to store the values 12, 6, and 10:
ListNode* node1 = new ListNode(12);
ListNode* node2 = new ListNode(6);
ListNode* node3 = new ListNode(10);

Warning 27.2.3.

If we wanted to, we could allocate a node on the stack instead of the heap. However, doing so would limit the node’s lifetime to the scope in which it was created, which is usually not what we want for linked lists.
We will always allocate nodes on the heap.
At this point, we have the stack based pointer variables node1, node2, and node3 that point to the three nodes on the heap:
Three List Node pointer variables on the stack. node1 points at a node with 12. node2 points at a node with 6. node3 points at a node with 10.πŸ”—
Figure 27.2.2. Three List Nodes
The nodes are not yet connected into a list. To do that, we need to set the next pointer of each node to point to the next node in the list. We can do that like this:
node1->next = node2;
node2->next = node3;
Note that node1 is not storing a node. It stores the memory address of a node. To access the next pointer of the node that node1 points to, we use the arrow operator to say β€œgo to the thing node1 points at and access the next member there”. We want to set node1->next to point to the node that has the value 6, which is the node that node2 points to. So we set node1->next = node2;, which says β€œgo to the node that node1 points to and set its next pointer to the memory address stored in node2”.

Warning 27.2.4.

It is critical to remember that a pointer always just stores a memory address.
When we do something like node1->next = node2;, we are not copying a node. We are only copying the memory address stored in node2 into the next pointer of the node that node1 points to.
After connecting the nodes, our linked list looks like this:
Three List Node pointer variables on the stack. node1 points at a node with 12. node2 points at a node with 6. node3 points at a node with 10. node1’s next pointer points the node with 6o, and node2’s next pointer points to the node with 10.πŸ”—
Figure 27.2.3. Three List Nodes
At this point, we have a very simply linked list made up of three nodes storing the list {12, 6, 10}. The list starts at the node that node1 points to. From there, we can follow the next pointers to get to the other nodes in the list. Here is a program that prints out the values in the list by following the pointers from one node to the next:
Notice that to access the data we are only relying on the node1 pointer variable. From there, we follow the next pointers to get to the other nodes in the list. node1->next->next->data says something like:
  • Go to the node that node1 points to...
  • ...then go to the node that its next pointer points to...
  • ...then go to the node that its next pointer points to...
  • ...then get the data value stored there.
If you trace these steps through FigureΒ 27.2.3, you can see that this gets us to the value 10.

Insight 27.2.5.

Every time we see -> we should be following an arrow to a new node in the diagram.
Do you notice something we have not taken care of yet? We allocated three nodes on the heap using new, but we have not deleted them. (The program above was set to run without the address sanitizer introduced in SectionΒ 22.7.) So our program has memory leaks. To fix that, we need to make sure to delete each node when we are done with it. Our program should end with something like:
    ...
    delete node1;  // delete the node that node1 points to
    delete node2;  // delete the node that node2 points to
    delete node3;  // delete the node that node3 points to
}  //end of main
Because a linked list allocates each node as a separate allocation on the heap, we need to also delete each node individually.

Checkpoint 27.2.1.

What best describes the contents of node1 in the snippet below?
ListNode* node1 = new ListNode(12);
  • The memory address of a ListNode
  • A ListNode
  • An integer
  • The memory address of an integer

Checkpoint 27.2.2.

Checkpoint 27.2.3.

Given this code:
ListNode* a = new ListNode(10);
ListNode* b = new ListNode(20);
ListNode* c = new ListNode(30);
c->next = b;
b->next = a;
How could we access the value 10? Select all options that would work.
  • a->data
  • b->next->data
  • c->next->next->data
  • b->data
  • a->next->data
  • c->next->data
  • b->next->next->data
You have attempted of activities on this page.