Linked lists are our first data structure to explore.
We will cover:
Singly linked lists
Doubly linked lists
Circularly linked lists
This week we will look at linked lists:
Why to use linked lists?
How to use linked lists?
Different Types of linked list.
Arrays are useful structures provided by programming languages.
However, arrays have limitations:
Size must be known at compilation.
Data is separated in memory by the same distance.
These limitations make inserting an item difficult.
Linked lists overcome array limitations by linking data independently of memory location. Like a treasure hunt, each clue (data piece) points to the next location (address).
Each data piece stores the address of the next data piece.
A basic linked list structure:
Data
Pointer (address) to the next data element.
A linked structure is a collection of nodes.
Each node stores data.
Each node contains links (addresses) to other nodes.
Nodes can be located anywhere in memory.
Traversing the list involves using stored addresses to move between nodes, similar to following breadcrumbs.
A data structure that makes it easy to rearrange data without having to move data in memory. Think of it like rearranging a train by switching the links between cars instead of physically moving each car.
Example 1: Classroom of students in no order; reordering them alphabetically by seat number.
Solutions:
Change their seats (chaotic if many)
Leave students seated and make a list of seat numbers that corresponds to the alphabetical order of students (3,1,2)
Example 2: Rearranging students in size order (1,3,2)
Linked lists play a critical role in dynamically managing data in companies and governments.
Types:
Singly linked list: Enables movement in one direction (front to end). Like a one-way street.
Doubly linked list: Enables movement in both directions. Like a two-way street.
Programmers choose linked lists over arrays because linked lists can grow or shrink in size during runtime. Arrays are like a fixed-size bookshelf, while linked lists are like adding or removing train cars as needed.
Adding an entry:
In a linked list: simply assign reference to the new entry in the last entry.
Removing an entry:
In a linked list: simply remove the reference to it in the next element of the second-to-last entry.
Arrays vs Linked list for resizing:
Arrays: The OS tries to increase the array by using memory alongside the array, if unavailable, then the operating system finds another location large enough to hold elements of the array and new array elements and copies old elements.
Linked lists: the operating system changes references to the previous item and the next item on the list, often fewer steps than resizing an array.
Node: each entry in a linked list
Data: Associated with the current node.
Pointer to the previous node (if doubly linked).
Pointer to the next node.
typedef struct Node {
struct Node(int data) \
{
this->data = data;
previous = NULL; \
next = NULL; \
}
int data;
struct Node* previous;
struct Node* next;
} NODE;
The constructor initializes elements of the node when an instance of the node is created.
structure name creates instance
The last member of the structure is a pointer for storing the address of another structure of the same type.
#include <iostream>
#include <string>
using namespace std;
struct TeleType {
string name;
string phoneNo;
TeleType *nextaddr;
};
int main() {
TeleType tl = {"Acme, Sam", "(555) 898-2392"};
TeleType t2 = {"Dolan, Edith", "(555) 682-3104"};
TeleType t3 = {"Lanfrank, John", "(555) 718-4581"};
TeleType *first;
first = &tl; // store tl's address in first
tl.nextaddr = &t2; // store t2's address in tl.nextaddr
t2.nextaddr = &t3; // store t3's address in t2.nextaddr
t3.nextaddr = NULL; // store a NULL address in t3.nextaddr
cout << endl << first->name
<< endl << tl.nextaddr->name
<< endl << t2.nextaddr->name << endl;
return 0;
}
void display (TeleType *contents) {
while (contents != NULL) {
cout << endl << setiosflags (ios::left)
<< setw(30) << contents->name
<< setw(20) << contents->phoneNo;
contents = contents->nextaddr;
}
cout << endl;
return;
}
contents
is a pointer to a structure of type TeleType
The function displays until the end of the linked list.
Allocate memory for the new node.
Point the new node to its successor.
Point the new node’s predecessor to the new node.
Before add
list.head = 0
count = 0
After add
pNew->link = list.head
list.head = pNew
count = 1
Before add
list.head = 75
count = 1
After add
pNew->link = list.head
list.head = pNew
count = 2
Before add
list.head = 39
count = 2
After add
pNew->link = pPre->link
pPre->link = pNew
count = 3
Before add
list.head = 39
count = 3
After add
pNew->link = pPre->link
pPre->link = pNew
count = 4
algorithm insertNode ( ref list <metadata>, val pPre <node pointer> val dataIn <dataType>)
Inserts data into a new node in the linked list
Pre list is metadata structure to a valid list
pPre is pointer to data’s logical predecessor
dataIn contains data to be inserted
Post data have been inserted in sequence
Return true if successful, false if memory overflow
1 allocate (pNew)
2 if (memory overflow)
1 return false
3 end if
5 if (pPre nulll) Adding before first node or to empty list.
1 pNew->link = list.head
2 list.head = pNew
6 else Adding in middle or at end.
1 pNew->link = pPre->link
2 pPre->link = pNew
7 end if
8 list.count = list.count + 1
9 return true
end insertNode
nodeType *newNode;
newNode = new nodeType;
newNode->data = 123;
newNode->link = current->link;
current->link = newNode;
current=head;
while(current) {
if((current->data < newnode->data) && (current->next->data > newnode->data)) {
temp = current->next;
current->next = newnode;
newnode->next = temp;
break;
}
current = current->next;
}
The previous examples were singly linked lists.
Contains:
A pointer (P) to the first element.
Each element stores data and a pointer to the next element.
The final element points towards NULL
(0).
Data can be simple types or user-defined classes.
A node in a single linked list references the next node and not the previous node, although nothing stops you from creating a backward reference by using only the previous node reference
Singly Linked List:
Front pointer
Doubly Linked List:
Front pointer
Back pointer
Enables traversal in both directions.
void appendNode(int data) {
NODE* n = new NODE(data);
if(back == NULL) {
back = n;
front = n;
}
else {
back->next = n;
n->previous = back;
back = n;
}
}
back->next = n;
n->previous = back;
back = n;
Step 1: assigns the memory address of the new node to the next member of the back node
Step 2: assigns the memory address of the back node to the previous member of the new node. This links both nodes.
Step 3: replaces the memory address of the back node on the linked list with the memory address of the new node. This places the new node at the beginning of the linked list.
void displayNodes() {
cout << "Nodes:";
NODE* temp = front;
while(temp != NULL) {
cout << " " << temp->data;
temp = temp->next;
}
}
void displayNodesReverse() {
cout << "Nodes in reverse order:";
NODE* temp = back;
while(temp != NULL) {
cout << " " << temp->data;
temp = temp->previous;
}
}
Before delete:
list.head = 39
count = 3
After delete:
list.head = pLoc->link
count = 2
Before delete:
list.head = 39
count = 3
After delete:
pPre->link = pLoc->link
count = 2
algorithm deleteNode(
ref list <metadata>,
val pPre <node pointer>
val pLoc <node pointer>
ref dataOut <dataType>)
Deletes data from a linked list and returns it calling module
Pre list is a metadata structure to a valid list
pPre is a pointer to predecessor node
pLoc is a pointer to node to be deleted
dataOut is variable to receive deleted data
Post data have been deleted and t d t ll
1 dataOut = pLoc->data
2 if (pPre null) Deleting first node
1 list.head = pLoc->link
3 else Deleting other nodes
1 pPre->link = pLoc->link
4 end if
5 list.count = list.count - 1
6 recycle (pLoc)
7 return
end deleteNode
void destroyList() {
NODE* temp = back;
while(temp != NULL) {
NODE* temp2 = temp;
temp = temp->previous;
delete temp2;
}
back = NULL;
front = NULL;
}
This member function removes nodes from the linked list without removing the linked list itself.
The visualization of how nodes are removed step by step
After loop:
front=NULL
back=NULL
while(current!=NULL) {
if(current->data!=data) {
prev=current;
current = current->next;
}
else {
prev->next = current->next;
delete current;
break;
}
}
The final node points back to the first node. Like a snake eating its own tail.
Useful for tracking things continually in sequence.
The last node's link points to the first node
Difficulty in tail_delete
function calls.
Scanning the entire list to find the tail element.
Solution: Doubly Linked List.
Head
Tail
Data
Each node has pointers to both successor and predecessor.
Metadata:
Count
Position pointer for traversals
Rear pointer (optional)
Each node contains two pointers:
Backward pointer – points to its predecessor
Forward pointer – points to its successor
Inserting into null list or before first node:
Inserting between two nodes: