LinkedList - revised
LinkedList - revised
1
Linked List
data link
• The address of the first node in the list is maintained in a pointer variable usually
called head, first, or list.
• The null pointer (nullptr or NULL, physical value 0 in C/C++) is used to denote
the end of the list, or not a valid address.
head 45 65 34 76
NULL
In typical C++ implementations, we define the node using struct and design the
functions using template such that the programs can be reused for different data
types.
template<class Type>
struct node
{
Type info;
node<Type>* link; // another commonly used var name is “next”
};
2
Example program codes that operate on a linked list
template<class Type>
int length(const node<Type>* list) // getter function
{
int len = 0;
const node<Type>* p = list; // traverse the list using p
3
// Insert a new element x at the front of the list.
// Assume Type = int in the following examples.
step 1:
head 45 65 34 76
NULL
(step 1)
p x
step 2:
head 45 65 34 76
NULL
(step 2)
p x
template<class Type>
void insertFront(node<Type>*& list, const Type& x)
{
// setter function, list is passed by reference
4
// Remove the element x (1st instance) from the linked list
// Program organization:
// 1. search for the node holding x with a while-loop
// 2. if x is found, remove the node
// (need to know its predecessor)
template<class Type>
bool remove(node<Type>*& list, const Type& x) // setter function
{
node<Type>* cur = list;
node<Type>* prev = nullptr;
// prev points to the predecessor of cur
5
Diagrams showing the deletion operation:
prev cur
head 45 65 34 76
NULL
prev cur
head 45 65 34 76
NULL
prev cur
head 45 65 34 76
NULL
head 45 65 76
NULL
6
Function to find the min data value in an unordered linked list
while (p != nullptr)
{
if (p->info < min)
min = p->info;
p = p->link;
}
return min;
}
Function to find the node holding the min data value in an unordered linked list
template<class Type>
node<Type>* getMinNode(node<Type>* list) // getter function
{
if (list == nullptr) // return nullptr if the list is empty
return nullptr;
while (p != nullptr)
{
if (p->info < min->info)
min = p;
p = p->link;
}
return min;
}
7
Function to remove the node with the min data value in an unordered linked list
while (p != nullptr)
{
if (p->info < min->info)
{
prev = q;
min = p;
}
q = p;
p = p->link;
}
if (prev != nullptr)
prev->link = min->link;
else // min is the 1st node
list = min->link; // list is updated, that’s why
// list is passed by reference
delete min;
return minValue;
}
8
Function to insert a node to an ordered linked list
template<class Type>
void insert(node<Type>*& list, node<Type>* p) // setter function
{
node<Type> *prev = nullptr;
node<Type> *cur = list;
Remark:
In this example, comparison of data objects is done using the operator>()
function of the given data type (i.e. based on natural order of the data type).
9
Sort a linked list using the insert function
template<class Type>
void insertionSort(node<Type>*& list)
{
// Sorting is based on natural order of the data type
node<Type>* p = list;
while (p != nullptr)
{
node<Type>* r = p->link;
insert(sortedList, p);
p = r;
}
list = sortedList;
}
Remark:
• Null-pointer exception is a common error in programs that manipulate linked list.
• A pointer must be properly initialized or tested for not equal to nullptr before you
can use it to access a data member or the next node.
10
Function to merge two ordered linked lists
template<class Type>
node<Type>* clone(const node<Type>* p)
{
node<Type>* r = new node<Type>;
r->info = p->info;
r->link = nullptr;
return r;
}
template<class Type>
node<Type>* mergeList(const node<Type > *A, const node<Type> *B)
{
const node<Type> *p = A; // traverse list A with p
const node<Type> *q = B; // traverse list B with q
header node
head 45 65 76
NULL
The data field of the header node is not used to store valid data. Some metadata may
be stored in the header node (if appropriate).
header node
2. Circular list:
- The link of the last node points back to the first node.
c_list
- The pointer variable c_list points to the last node in the list.
- We can access the first node in one step:
12
3. Circular list with header:
head
first node last node
65 34 76
4. Doubly-linked list:
template<class Type>
struct DNode
{
Type info;
DNode<Type>* next; //points to successor node
DNode<Type>* back; //points to predecessor node
}
With a doubly-linked list, we can traverse the list in the forward or backward
direction.
Remark: The list container in the C++ standard template library (STL), and the
LinkedList class in the Java JDK are implemented as doubly-linked list.
13
first last
45 65 76
NULL NULL
cur->next = p;
if (p->next != nullptr)
p->next->back = p->back;
else // p is the last node
{
last = p->back;
if (last != nullptr)
last->next = nullptr;
}
if (p->back != nullptr)
p->back->next = p->next;
else // p is the first node
{
first = p->next;
if (first != nullptr)
first->back = nullptr;
}
delete p;
14
5. Doubly-linked list with header node
head
first node last node
NULL 45 65 76
NULL
header node
45 65 76
first
head
Circular doubly-linked list with header is used in some operating system for
dynamic memory allocation and de-allocation.
The algorithm is called boundary-tag method.
15
The searching problem
Given a set of key-value pairs (where the keys are distinct), we want to find the
associated value for a given key.
Time complexity of the search and update operations (insertion or deletion of keys
to the data set):
Time complexity
Search algorithm search insert/delete
Sequential search (unordered list) O(N) O(1)
Binary search O(log N) O(N)
(ordered list maintained in an array)
Binary search tree (balanced) O(log N) O(log N)
Hash table, Map or Dictionary O(1) O(1)
(average case and best case)
16
General principle of Hash table
= { (K1, V1), (K2, V2), …, (Kn, Vn) } // data set with n records
h(key) is the hash function, and output value of h(key) is between 0 and M-1.
The hash table is an array of size M where M ≥ n.
Example: assume the keys are unsigned fixed-size integer, and M = 11 is a prime
number.
h(key) = key % M
= { (55, V1), (41, V2), (73, V3), (20, V4), (35, V5) }
Hash Table
index bucket (key, value)
0 (55, V1) 55 % 11 = 0
1
2 (35, V5) 35 % 11 = 2
3
4
5
6
7 (73, V3) 73 % 11 = 7
8 (41, V2) 41 % 11 = 8
9 (20, V4) 20 % 11 = 9
10
The load factor is defined as = n/M, where n is the number of elements, and M is
the size of the hash table.
17
Let U = domain of the keys.
M = size of the hash table.
The hash function h is a mapping h: U → {0, 1, 2, …, M−1}, where |U| >> M.
To search for a given key x, we use the hash function to compute the home address
home = h(x).
• If the home bucket is empty, then x does not exist in the set.
• The record stored in the home bucket is compared with x. If x matches the key
of the record, then the associated value is returned.
• If x does not match the key stored in the home bucket, then some additional
buckets may be probed depending on the collision resolution strategy used.
Collision
• A collision is said to have occurred when two distinct keys K1 and K2 are
hashed into the same bucket, i.e. h(K1) = h(K2).
18
Some simple hash functions
Bit-Extraction:
• Merely extract a few scattered bits from an element, putting those bits
together to form an address.
• A weakness of extraction is that the resulting location depends only on a small
subset of the bits of an element.
• A first principle in the design of hash functions is that the hash location should
be a function of every bit of the element.
Compression or folding:
• The key is divided into fixed-length segments, and then add them up as binary
numbers or take their exclusive-or.
• For example, keys are character strings, key = “THE”,
h(“THE”) = 0101 0100 xor 0100 1000 xor 0100 0101 = 0101 1001 = 89 d
• In general, if the keys are character string, then breaking the key into
individual chars is not a good idea!
Permutation of the same set of characters will have the same hash value, e.g.
h(“THE”) = h(“HET”)
Division:
• h(x) = x % M, where M is a prime number.
Multiplication:
• h(x) = M × fraction( × x)
• where = (5 – 1) / 2, or = 1 − (5 – 1) / 2
19
Collision resolution using separate chaining
• The hash table T is organized as an array of linear linked lists, one linked list
for each hash bucket.
• To lookup a key, we simply do a sequential search of the given linked list.
Example:
Suppose the hash function h(x) = x % 11.
Let = {17, 33, 28, 20, 45, 64, 56}
After inserting all the keys, the hash table looks as follows:
hashTable
0 33 NULL
1 45 56 NULL
2 NULL
3 NULL
4 NULL
5 NULL
6 17 28 NULL
7 NULL
8 NULL
9 20 64 NULL
10 NULL
Remarks:
- The hash table class in the Java JDK is implemented using separate chaining.
- Performance of the hash table depends on the load factor. Usually, the load
factor is maintained to be no more than 0.75 (threshold chosen by the Java
designer).
20
Orthogonal list: Linked representation of sparse matrix
Non-zero elements in a row (or column) are connected in a circular linked list with
header node.
𝟓𝟗 𝟎 𝟎 𝟎
Example: 𝑀 = [𝟕𝟏 𝟎 𝟗 𝟎]
𝟎 𝟎 𝟎 𝟎
𝟐 𝟎 𝟏 𝟔
4 4 -1 0 -1 2 -1 3
0 -1 0 0 59
1 -1 1 0 71 1 2 9
3 -1 3 0 2 3 2 1 3 3 6
21
• In this example, values of nRow and nCol of the sparse matrix are stored in the
dummy header node pointed at by M.
• The header of each circular list stores the respective row/column number.
p = M->down;
while (p != M && !foundRow)
{
if (p->row < i)
p = p->down;
else if (p->row == i) //found the row
foundRow = 1;
else
p = M; //terminate the loop
}
return 0; //matrix[i][j] == 0
}
22