Linked List Basic
Prerequisites
- C
Content
- What is Linked List?
- Linked List v.s. Array
- Visualizing Linked List
- Basic examples (in C)
What is Linked List?
Linked lists use dynamically allocated memories as data storage, and associate these storages with pointers.
Well, you may just think of linked lists as arrays that are resizable, easily rearrangible, with non-consecutive slots.
Linked List v.s. Array
Everyone likes comparisons. So the properties of linked list & array here -
Linked List | Array |
---|---|
* Dynamic size | Fixed size |
* Cheaper insertion | Expensive insertion |
No random access | * Random Access |
--- | * Better cache performance |
Extra memory space | * --- |
We must know how many elements to allow when defining an array; linked lists allowes more flexibility.
To insert an element into array, we probably need to move a whole bunch of elements backwards for the newcomer to fit. For linked lists, it's just about switching between some pointers and that's it.
Memories are allocated consecutively for arrays, which mean calculating
arr[5]
,arr[10]
,arr[1000]
are all about adding offsets toarr[0]
, which is quite cheap. But for linked lists, the memories are dynamically allocated on heap and do not have this luxury.Moreover for consecutive memories, the cache performance is better, since a whole bunch of array elements that fit into the cache size will be fetched all at once.
Linked lists requires more memory space than arrays due to the fact that pointers are required.
Visualizing Linked List
OK, so what's the picture of linked lists? It all starts with a pointer (*list
), followed by a list of nodes (node_i
):
[] -> [_] -> [_] -> ... -> [_] -> NULL
*list node_1 node_2 ... node_N
Hold on, some structures need to be defined for lists and nodes before you can easily mingle with the crazy pointers.
struct Node {
int value; // holding the value of the node; can be any type or have multiple value fields
struct Node *next; // pointing to the next node in list
};
typedef struct Node Node;
struct SinglyLinkedList {
Node *head; // pointing to the first node in list
int size; // count of nodes in list; optional field
};
typedef struct SinglyLinkedList SinglyLinkedList;
Haven't seen
typedef
before?
typedef
provides an easy way to define types so that you don't have to type in so many words when declaring a variable with that data type.
typedef [actual-data-type] [a-convenient-name]
So in C, after defiing a struct (e.g. Node), everytime you wanna declare a Node, you type
struct Node n
. Now I know how to aliasstruct Node
intoNode
bytypedef
!
typedef struct Node Node;
Then we're ready to use these structs and build a linked list.
Basic Examples (in C)
Initialization
SinglyLinkedList *list = NULL;
Node *n = NULL;
Node *temp_n = NULL;
list = malloc(sizeof(SinglyLinkedList));
list->head = NULL;
list->size = 0;
malloc
stands for memory allocation, which is similar to thenew
keyword in C++.
You pass in the size of memory you want to allocate, so for aSinglyLinkedList
struct, itssizeof(SinglyLinkedList)
.
Adding a node to list
/* [] -> NULL */
/* *list */
// create a node
n = malloc(sizeof(Node));
n->value = 1; // give the node a value
n->next = NULL; // the node is not linked by other nodes yet
// add it to the head of the list
list->head = n;
list->size++;
/* [] -> [1] -> NULL */
/* *list ↑ */
/* *n */
// remove it from the list
list->head = NULL;
list->size--;
/* [] -> NULL [1] -> NULL */
/* *list ↑ */
/* *n */
// destroy it by releasing its memory
free(n);
/* [] -> NULL */
/* *list */
Always free your malloc'ed memories to avoid memory leak.
Adding nodes to list
// declare pointer temp_n to point to the head of the empty list
temp_n = list->head;
for (int i = 0; i < 1000; i++) {
// create new node
n = malloc(sizeof(Node));
n->value = i;
n->next = NULL;
// add to the head of the list
if (list->head == NULL) { // if list empty, let head point to n
list->head = n;
} else { // else let the currently pointed node link to n (by assigning *next to point to n)
temp_n->next = n;
}
temp_n = n;
list->size++;
}
/* [] -> [0] -> [1] -> [2] -> ... -> [999] -> NULL */
/* *list ↑ -> ↑ -> ↑ ... -> ↑ */
/* *temp_n *temp_n *temp_n *temp_n */
Use a temp node pointer to move around the list.
List traversal
int sum = 0;
temp_n = list->head;
while (temp_n != NULL) {
printf("value: %d\n", temp_n->value);
sum += temp_n->value;
temp_n = temp_n->next; // move forward the cursor
}
printf("sum: %d\n", sum);
Insert a node into list
/* [] -> [0] -> [1] -> [2] -> ... -> [999] -> NULL */
/* *list */
int index = 5; // inserting into index 5 of list
n = malloc(sizeof(Node));
n->value = 87;
n->next = NULL;
// traverse through the list to find index; assume we don't check if index is valid
temp_n = list->head;
for (int i = 0; i < index-1; i++) {
temp_n = temp_n->next;
}
/* [] -> ... -> [4] -> [5] -> ... -> NULL */
/* *list ↑ */
/* *temp_n */
// insert by moving around pointers
n->next = temp_n->next;
temp_n->next = n;
/* *n -> [87] */
/* ↓ */
/* [] -> ... -> [4] -> [5] -> ... -> NULL */
/* *list ↑ */
/* *temp_n */
/* *n -> [87] */
/* ⬈ ↓ */
/* [] -> ... -> [4] [5] -> ... -> NULL */
/* *list ↑ */
/* *temp_n */
list->size++;
Remove a node from list
index = 5; // removing node at index 5 of list
// traverse through the list to find index; assume we don't check if index is valid
temp_n = list->head;
for (int i = 0; i < index-1; i++) {
temp_n = temp_n->next;
}
/* [] -> ... -> [4] -> [87] -> ... -> NULL */
/* *list ↑ */
/* *temp_n */
// remove by moving around pointers; notice that we're removing temp_n->next instead of temp->n
n = temp_n->next; // n = node to remove
temp_n->next = n->next; // connect the previous node and the next node
/* *n */
/* ↓ */
/* [] -> ... -> [4] -> [87] -> ... -> NULL */
/* *list ↑ */
/* *temp_n */
/* *n */
/* ↓ */
/* [] -> ... -> [4] [87] -> [5] -> ... -> NULL */
/* *list ↑ \_____________⬈ */
/* *temp_n */
free(n); // release memory
list->size--;
Destroy the whole list before program ends
while (list->head != NULL) {
temp_n = list->head;
list->head = list->head->next;
free(temp_n);
}
// finally, free list as well
free(list);
Summary
Linked lists are the most basic data structure to know and deal with, and its application is wide, such as creating trees. Make sure you can construct a valid linked list before you move on to the more complicated pointer-related data structure!
Source code can be found at GitHub.