Professional Documents
Culture Documents
Data Structure Lec04
Data Structure Lec04
Lecture No. 04
___________________________________________________________________
Data Structures
Lecture No. 04
Reading Material
Data Structures and algorithm analysis in C++
Chapter. 3
3.2.3, 3.2.4, 3.2.5
Summary
Page 1 of 14
delete currentNode;
currentNode = lastCurrentNode->getNext();
size--;
}
};
currentNode
headNode
6
Size = 5
lastCurrentNode
Suppose that the currentNode is pointing at the location that contains the value 6. A
request for the removal of the node is made. Resultantly, the node pointed by
currentNode should be removed. For this purpose, at first, the next pointer of the node
with value 2 (the node pointed by the lastCurrentNode pointer), that is before the
node with value 6, bypasses the node with value 6. It is, now pointing to the node with
value 8. The code of the first step is as:
lastCurrentNode->setNext(currentNode->getNext());
What does the statement currentNode->getNext() do? The currentNode is pointing to
the node with value 6 while the next of this node is pointing to the node with value 8.
That is the next pointer of node with value 6 contains the address of the node with
value 8. The statement lastCurrentNode->setNext(currentNode->getNext()) will set
the next pointer of the node pointed by the lastCurrentNode to the node with value 8.
So the next pointer of the node with value 2 is pointing to the node with value 8.
currentNode
headNode
Step1
2
Size = 5
lastCurrentNode
You see that the next pointer of the node having data element 2 contains the address
of the node having data element 8. The node with value 6 has been disconnected from
the chain while the node with value 2 is connected to the node with the value 8.
The code of the next step is:
delete currentNode;
Page 2 of 14
headNode
Size = 5
Step2
lastCurrentNode
In the next step, we have moved the currentNode to point the next node. The code is:
currentNode = lastCurrentNode->getNext();
In the fourth step, the size of the list has been reduced by 1 after the deletion of one
node i.e.
size--;
Step3
currentNode
Step1
headNode
Step4
Size = 4
Step2
lastCurrentNode
The next method is length() that simply returns the size of the list. The code is as
follows:
// returns the size of the list
int length()
{
return size;
};
The private data members of the list are:
private:
int size;
Page 3 of 14
add
For the addition purposes, we simply insert the new node after the current node.
So add is a one-step operation. We insert a new node after the current node in the
chain. For this, we have to change two or three pointers while changing the values
of some pointer variables. However, there is no need of traversing too much in the
list. In case of an array, if we have to add an element in the centre of the array, the
space for it is created at first. For this, all the elements that are after the current
pointer in the array, should be shifted one place to the right. Suppose if we have to
insert the element in the start of the array, all the elements to the right one spot are
shifted. However, for the link list, it is not something relevant. In link lists, we can
create a new node very easily where the current pointer is pointing. We have to
adjust two or three pointers. Its cost, in terms of CPU time or computing time, is
not much as compared to the one with the use of arrays.
remove
Page 5 of 14
find
The worst-case in find is that we may have to search the entire list. In find, we
have to search some particular element say x. If found, the currentNode pointer is
moved at that node. As there is no order in the list, we have to start search from
the beginning of the list. We have to check the value of each node and compare it
with x (value to be searched). If found, it returns true and points the currentNode
pointer at that node otherwise return false. Suppose that x is not in the list, in this
case, we have to search the list from start to end and return false. This is the worst
case scenario. Though time gets wasted, yet we find the answer that x is not in the
list. If we compare this with array, it will be the same. We dont know whether x is
in the array or not. So we have to search the complete array. In case of finding it,
we will remember that position and will return true. What is the average case? x
can be found at the first position , in the middle or at the end of the list. So on
average, we have to search half of the list.
back
In the back method, we move the current pointer one position back. Moving the
current pointer back, one requires traversing the list from the start until the node
whose next pointer points to current node. Our link list is singly linked list i.e. we
can move in one direction from start towards end. Suppose our currentNode
pointer and lastCurrentNode are somewhere in the middle of the list. Now we
want to move one node back. If we have the pointer of lastCurrentNode, it will be
easy. We will assign the value of lastCurrentNode to currentNode. But how can
we move the lastCurrentNode one step back. We dont have the pointer of
previous node. So the solution for this is to go at the start of the list and traverse
the list till the time you reach the node before the lastCurrentNode is pointing.
That will be the node whose next pointer contains the value lastCurrentNode. If
the currentNode and the lastCurrentNode are at the end of the list, we have to
traverse the whole list. Therefore back operation is not a one step operation. We
not only need a loop here but also require time.
Doubly-linked List
If you look at single link list, the chain is seen formed in a way that every node has a
field next that point to the next node. This continues till the last node where we set the
next to NULL i.e. the end of the list. There is a headNode pointer that points to the
start of the list. We have seen that moving forward is easy in single link list but going
back is difficult. For moving backward, we have to go at the start of the list and begin
from there. Do you need a list in which one has to move back or forward or at the start
or in the end very often? If so, we have to use double link list.
In doubly-link list, a programmer uses two pointers in the node, i.e. one to point to
Page 6 of 14
prev
element
next
First part is prev i.e. the pointer pointing to the previous node, second part is element,
containing the data to be inserted in the list. The third part is next pointer that points to
the next node of the list. The objective of prev is to store the address of the previous
node.
Lets discuss the code of the node of the doubly-link list. This node factory will create
nodes, each having two pointers. The interface methods are same as used in singly
link list. The additional methods are getPrev and setPrev. The method getPrev returns
the address of the previous node. Thus its return type is Node*. The setPrev method
sets the prev pointer. If we have to assign some address to prev pointer, we will call
this method. Following is the code of the doubly-linked list node.
/* this is the doubly-linked list class, it uses the next and prev pointers */
class Node {
public:
int get() { return object; }; // returns the value of the element
void set(int object) { this->object = object; }; // set the value of the element
Node* getNext() { return nextNode; }; // get the address of the next node
void setNext(Node* nextNode)
// set the address of the next node
{ this->nextNode = nextNode; };
Node* getPrev() { return prevNode; }; // get the address of the prev node
void setPrev(Node* prevNode)
// set the address of the prev node
{ this->prevNode = prevNode; };
private:
int object;
// it stores the actual value of the element
Node* nextNode;
// this points to the next node
Node* prevNode;
// this points to the previous node
};
Most of the methods are same as those in singly linked list. A new pointer prevNode is
added and the methods to get and set its value i.e. getPrev and setPrev. Now we will
use this node factory to create nodes.
You have to be very cautious while adding or removing a node in a doubly linked list.
The order in which pointers are reorganized is important. Lets have a pictorial view
of doubly-link list. The diagram can help us understand where the prevNode and
nextNode are pointing.
Page 7 of 14
head
size=5
current
This is a doubly link list. The arrows pointing towards right side are representing
nextNode while those pointing towards left side are representing prevNode. Suppose
we are at the last node i.e. the node with value 1. In case of going back, we usually
take the help of prevNode pointer. So we can go to the previous node i.e. the node
with value 7 and then to the node with value 8 and so on. In this way, we can traverse
the list from the end to start. We can move forward or backward in doubly-link list
very easily. We have developed this facility for the users to move in the list easily.
Lets discuss other methods of the doubly-linked list. Suppose we have created a new
node from the factory with value 9. We will request the node factory to create a new
object using new keyword. The newly created node contains three fields i.e. object,
prevNode and nextNode. We will store 9 into object and connect this new node in the
chain. Lets see how the pointers are manipulated to do that. Consider the above
diagram, the current is pointing at the node with value 6. The new node will be
inserted between the node with value 6 and the one with value 8.
In the first step, we assign the address of the node with value 8 to the nextNode of the
new node.
newNode->setNext( current->getNext() );
current
head
newNode
size=5
In the next step, a programmer points the prevNode of the newNode to the node with
value 6.
newNode->setprev( current );
Page 8 of 14
head
size=5
2
newNode
In the third step, we will set the previous node with value 8 to point to the newNode.
(current->getNext())->setPrev(newNode);
current
head
size=5
newNode
Now the prevNode of the node with value 8 is pointing to the node with value 9.
In the fourth step, the nextNode of the node with value 6 is pointing to the newNode
i.e. the node with value 9. Point the current to the newNode and add one to the size of
the list.
current->setNext( newNode );
current = newNode;
size++;
head
6
4
2
newNode
size=6
3
1
current
Now the newNode has been inserted between node with value 6 and node with value
8.
Page 9 of 14
Circularly-linked lists
Lets talk about circularly linked list. The next field in the last node in a singly-linked
list is set to NULL. The same is the case in the doubly-linked list. Moving along a
singly-linked list has to be done in a watchful manner. Doubly-linked lists have two
NULL pointers i.e. prev in the first node and next in the last node. A way around this
potential hazard is to link the last node with the first node in the list to create a
circularly-linked list.
The next method in the singly-linked list or doubly-linked list moves the current
pointer to the next node and every time it checks whether the next pointer is NULL or
not. Similarly the back method in the double-linked list has to be employed carefully
if the current is pointing the first node. In this case, the prev pointer is pointing to
NULL. If we do not take care of this, the current will be pointing to NULL. So if we
try to access the NULL pointer, it will result in an error. To avoid this, we can make a
circularly linked list.
We have a list with five elements. We have connected the last node with the first node.
It means that the next of the last node is pointing towards the first node.
current
head
size=5
size=5
2
7
1
You have noticed that there is no such node whose next field is NULL. What is the
benefit of this? If you use the next or back methods that move the current pointer, it
will never point to NULL. It may be the case that you keep on circulating in the list.
To avoid this, we get help from the head node. If we move the head node in the
circularly linked list, it will not be certain to say where it was pointing in the start. Its
advantages depend on its use. If we do not have to move too much in the list and have
Page 10 of 14
Josephus Problem
Now we will see an example where circular link list is very useful. This is Josephus
Problem. Consider there are 10 persons. They would like to choose a leader. The way
they decide is that all 10 sit in a circle. They start a count with person 1 and go in
clockwise direction and skip 3. Person 4 reached is eliminated. The count starts with
the fifth and the next person to go is the fourth in count. Eventually, a single person
remains.
You might ask why someone has to choose a leader in this way. There are some
historical stories attached to it. This problem is also studied in mathematics. Lets see
its pictorial view.
N=10, M=3
4
10
9
We have ten numbers representing the ten persons who are in a circle. The value of M
shows the count. As the value of M is three, the count will be three. N represents the
number of persons. Now we start counting clockwise. After counting up to three, we
have the number four. The number four is eliminated and put in the eliminated
column.
Page 11 of 14
N=10, M=3
10
9
After eliminating the number four, we will start our counting from number five.
Counting up to three, we have number eight which is eliminated and so on.
Eliminated
N=10, M=3
4
3
5
8
6
10
9
N=10, M=3
5
8
2
7
3
10
9
1
6
Page 12 of 14
Page 13 of 14
Page 14 of 14