0% found this document useful (0 votes)
8 views

Unit 2

Uploaded by

ramesh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

Unit 2

Uploaded by

ramesh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 130

Linked Lists

What is a Linked List?


• A linked list is a data structure used for storing
collections of data.
• A linked list has the following properties.
– Successive elements are connected by pointers
– The last element points to NULL
– Can grow or shrink in size during execution of a program
– Can be made just as long as required (until systems
memory exhausts)
– Does not waste memory space (but takes some extra
memory for pointers). It allocates memory as list grows.
Example of Single Linked List
Linked Lists ADT

• The following operations make linked


lists an ADT:
• Main Linked Lists Operations
– Insert: inserts an element into the list
– Delete: removes and returns the specified
position element from the list
• Auxiliary Linked Lists Operations
– Delete List: removes all elements of the list
(disposes the list)
– Count: returns the number of elements in
the list
– Find nth node from the end of the list
Why Linked Lists?

• Both linked lists and arrays are used to store


collections of data, and since both are used
for the same purpose, we need to
differentiate their usage.
• That means in which cases arrays are suitable
and in which cases linked lists are suitable.
Arrays Overview

• One memory block is allocated for the entire


array to hold the elements of the array. The
array elements can be accessed in constant
time by using the index of the particular
element as the subscript.
Why Constant Time for Accessing Array
Elements?
• To access an array element, the address of an element
is computed as an offset from the base address of the
array and one multiplication is needed to compute
what is supposed to be added to the base address to
get the memory address of the element. First the size
of an element of that data type is calculated and then
it is multiplied with the index of the element to get the
value to be added to the base address.
• This process takes one multiplication and one addition.
Since these two operations take constant time, we can
say the array access can be performed in constant
time.
Advantages of Arrays

• Simple and easy to use


• Faster access to the elements
(constant access)
First Disadvantage of Arrays

• Preallocates all needed memory up


front and wastes memory space for
indices in the array that are empty.
– Fixed size: The size of the array is
static (specify the array size before
using it).
Second Disadvantage of Arrays
– One block allocation: To allocate the
array itself at the beginning,
sometimes it may not be possible to
get the memory for the complete array
(if the array size is big).
• Eg: A[10000]
Third Disadvantage of Arrays
– Complex position-based insertion: To
insert an element at a given position, we
may need to shift the existing elements.
– This will create a position for us to insert
the new element at the desired position.
– If the position at which we want to add an
element is at the beginning, then the
shifting operation is more expensive.
– A[20]
Dynamic Arrays

• Dynamic array (also called as growable array,


resizable array, dynamic table, or array list) is a
random access, variable-size list data structure
that allows elements to be added or removed.
• One simple way of implementing dynamic arrays
is to initially start with some fixed size array.
• As soon as that array becomes full, create the
new array double the size of the original array.
• Similarly, reduce the array size to half if the
elements in the array are less than half.
Advantages of Linked Lists
• Linked lists have both advantages and
disadvantages.
• Issues in arrays:
– To create an array, we must allocate memory for a
certain number of elements.
– To add more elements to the array when full, we must
create a new array and copy the old array into the
new array.
– This can take a lot of time.
– We can prevent this by allocating lots of space initially
but then we might allocate more than we need and
waste memory.
Advantages of Linked Lists

• The advantage of linked lists is that they can


be expanded in constant time.
– With a linked list, we can start with space for just
one allocated element and add on new elements
easily without the need to do any copying and
reallocating.
Issues with Linked Lists (Disadvantages)
• There are a number of issues with linked lists.
– The main disadvantage of linked lists is access time to individual
elements.
o Array is random-access, which means it takes O(1) to access
any element in the array.
o Linked lists take O(n) for access to an element in the list in
the worst case.
– Another advantage of arrays in access time is spacial locality in
memory. Arrays are defined as contiguous blocks of memory,
and so any array element will be physically near its neighbors.
This greatly benefits from modern CPU caching methods.
• Although the dynamic allocation of storage is a
great advantage, the overhead with storing and
retrieving data can make a big difference.
Sometimes linked lists are hard to manipulate.
If the last item is deleted, the last but one must then
have its pointer changed to hold a NULL reference.
This requires that the list is traversed to find the last
but one link, and its pointer set to a NULL reference.
• Finally, linked lists waste memory in terms of
extra reference points.
Comparison of Linked Lists with
Arrays & Dynamic Arrays
Singly Linked Lists
• Generally “linked list” means a singly linked
list.
• This list consists of a number of nodes
• Each node has a next pointer to the following
element.
• The link of the last node in the list is NULL,
which indicates the end of the list.
• Following is a type declaration for a linked list
of integers:
Basic Operations on a List

• Traversing the list


• Inserting an item in the list
• Deleting an item from the list
Traversing the Linked List
• Let us assume that the head points to the first
node of the list.
• To traverse the list we do the following
 Follow the pointers.
Display the contents of the nodes (or count) as
they are traversed.
Stop when the next pointer points to NULL.
• The ListLength() function takes a linked list as
input and counts the number of nodes in the
list.
• The function given below can be used for
printing the list data with extra print function.
• Time Complexity: O(n), for scanning the list of
size n.
• Space Complexity: O(1), for creating a
temporary variable.
Singly Linked List Insertion
• Insertion into a singly-linked list has three cases:
• Inserting a new node before the head (at the
beginning)
• Inserting a new node after the tail (at the end of
the list)
• Inserting a new node at the middle of the list
(random location)
• Note: To insert an element in the linked list at
some position p, assume that after inserting the
element the position of this new node is p.
Inserting a Node in Singly Linked List
at the Beginning
• In this case, a new node is inserted before the
current head node. Only one next pointer
needs to be modified (new node’s next
pointer) and it can be done in two steps:
– Update the next pointer of new node, to point to
the current head.
Inserting a Node in Singly Linked List
at the Beginning(Diagram)
Inserting a Node in Singly Linked List
at the Beginning(Diagram)
Inserting a Node in Singly Linked List
at the Ending
• In this case, we need to modify two next
pointers
– Last nodes next pointer and
– new nodes next pointer.
• New node’s next pointer points to NULL.
Inserting a Node in Singly Linked List
at the Ending (Diagram)
Inserting a Node in Singly Linked List
at the Beginning(Diagram)
Inserting a Node in Singly Linked List
at the Middle
• Let us assume that we are given a position where
we want to insert the new node.
• In this case also, we need to modify two next
pointers.
• If we want to add an element at position 3 then we
stop at position 2. That means we traverse 2 nodes
and insert the new node.
• For simplicity let us assume that the second node is
called position node.
• The new node points to the next node of the
position where we want to add this node.
• Let us write the code for all three cases. We
must update the first element pointer in the
calling function, not just in the called function.
• For this reason we need to send a double
pointer. The following code inserts a node in
the singly linked list.
Code for Inserting a Node
• Note: We can implement the three variations
of the insert operation separately.
• Time Complexity: O(n), since, in the worst
case, we may need to insert the node at the
end of the list.
• Space Complexity: O(1), for creating one
temporary variable.
• Singly Linked List Deletion
• Similar to insertion, here we also have three
cases.
– Deleting the first node
– Deleting the last node
– Deleting an intermediate node.
Deleting the First Node in Singly
Linked List
• First node (current head node) is removed
from the list. It can be done in two steps:
– Create a temporary node which will point to the
same node as that of head.
• Now, move the head nodes pointer to the next
node and dispose of the temporary node.
Deleting the Last Node in Singly
Linked List
• In this case, the last node is removed from the
list. This operation is a bit trickier than removing
the first node, because the algorithm should find
a node, which is previous to the tail. It can be
done in three steps:
– Traverse the list and while traversing maintain the
previous node address also. By the time we reach the
end of the list, we will have two pointers, one pointing
to the tail node and the other pointing to the node
before the tail node.
• Update previous node’s next pointer with
NULL.
– Dispose of the tail node.
Deleting an Intermediate Node in
Singly Linked List
• In this case, the node to be removed is always
located between two nodes. Head and tail
links are not updated in this case. Such a
removal can be done in two steps:
– Similar to the previous case, maintain the
previous node while traversing the list. Once we
find the node to be deleted, change the previous
node’s next pointer to the next pointer of the
node to be deleted.
– Dispose of the current node to be deleted.
• Time Complexity: O(n). In the worst case, we
may need to delete the node at the end of the
list.
• Space Complexity: O(1), for one temporary
variable.
Deleting Singly Linked List
• This works by storing the current node in
some temporary variable and freeing the
current node.
• After freeing the current node, go to the next
node with a temporary variable and repeat
this process for all nodes.
Time & Space Complexity
• Time Complexity: O(n), for scanning the
complete list of size n.
• Space Complexity: O(1), for creating one
temporary variable.
Data Time Complexity Space
Struct Compl
ure eity

Average Worst Worst

Acces Searc Insert Deleti Acces Searc Insert Deleti


s h ion on s h ion on
Singly θ(n) θ(n) θ(1) θ(1) O(n) O(n) O(1) O(1) O(n)
Linke
d List
Doubly Linked Lists
• The advantage of a doubly linked list (also called
two – way linked list) is that given a node in the
list, we can navigate in both directions.
• A node in a singly linked list cannot be removed
unless we have the pointer to its predecessor.
– But in a doubly linked list, we can delete a node even
if we don’t have the previous node’s address (since
each node has a left pointer pointing to the previous
node and can move backward).
Disadvantages
• The primary disadvantages of doubly linked
lists are:
– Each node requires an extra pointer, requiring
more space.
– The insertion or deletion of a node takes a bit
longer (more pointer operations).
• Following is a type declaration for a doubly
linked list of integers:
Doubly Linked List Insertion
• Insertion into a doubly-linked list has three
cases (same as singly linked list):
– Inserting a new node before the head.
– Inserting a new node after the tail (at the end of
the list).
– Inserting a new node at the middle of the list.
Inserting a Node in Doubly Linked List
at the Beginning
• In this case, new node is inserted before the
head node.
– Previous and next pointers need to be modified
and it can be done in two steps:
• Update the right pointer of the new node to point to
the current head node (dotted link in below figure)
and also make left pointer of new node as NULL.
• Update head node’s left pointer to point to
the new node and make new node as head.
Inserting a Node in Doubly Linked List
at the Ending
• In this case, traverse the list till the end and
insert the new node.
– New node right pointer points to NULL
– left pointer points to the end of the list.
– Update right pointer of last node to point to new
node.
Inserting a Node in Doubly Linked List
at the Middle
• Traverse the list to the position node and insert
the new node.
– New node right pointer points to the next node of
the position node where we want to insert the new
node.
– New node left pointer points to the position node.
I Step
II Step
– Position node right pointer points to the new node
– The next node of position node left pointer points
to new node.
• Now, let us write the code for all of these
three cases.
– Update the first element pointer in the calling
function, not just in the called function.
– For this reason we need to send a double pointer.
– The following code inserts a node in the doubly
linked list
Code
Code
• Time Complexity: O(n). In the worst case, we
may need to insert the node at the end of the
list.
• Space Complexity: O(1), for creating one
temporary variable.
Doubly Linked List Deletion
• Similar to singly linked list deletion, here we
have three cases:
– Deleting the first node
– Deleting the last node
– Deleting an intermediate node
Deleting the First Node in Doubly
Linked List
• In this case, the first node (current head node)
is removed from the list. It can be done in two
steps:
– Create a temporary node which will point to the
same node as that of head.
– Now, move the head nodes pointer to the next
node and change the heads left pointer to NULL.
Then, dispose of the temporary node.
Deleting the Last Node in Doubly
Linked List
• This operation is a bit trickier than removing
the first node, because the algorithm should
find a node, which is previous to the tail first.
This can be done in three steps:
– Traverse the list and while traversing maintain the
previous node address also. By the time we reach
the end of the list, we will have two pointers, one
pointing to the tail and the other pointing to the
node before the tail.
– Update the next pointer of previous node to the
tail node with NULL.
– Dispose the tail node.
Deleting an Intermediate Node in
Doubly Linked List
• In this case, the node to be removed is always
located between two nodes, and the head and
tail links are not updated. The removal can be
done in two steps:
– Similar to the previous case, maintain the
previous node while also traversing the list. Upon
locating the node to be deleted, change the
previous node’s next pointer to the next node of
the node to be deleted.
– Dispose of the current node to be deleted.
• Time Complexity: O(n), for scanning the
complete list of size n.
• Space Complexity: O(1), for creating one
temporary variable.
Circular Linked Lists
• In singly linked lists and doubly linked lists, the
end of lists are indicated with NULL value.
• Circular linked lists do not have ends.
• In circular linked lists, each node has a
successor.
• In singly linked lists, there is no node with
NULL pointer in a circularly linked list.
Usage of circular linked lists
• When several processes are using the same
computer resource (CPU) for the same
amount of time, we have to assure that no
process accesses the resource before all other
processes do (round robin algorithm).
Declaration of node
• The following is a type declaration for a
circular linked list of integers:
• In a circular linked list, we access the elements
using the head node (similar to head node in
singly linked list and doubly linked lists).
• The circular list is accessible through the node
marked head.
• To count the nodes, the list has to be traversed
from the node marked head, with the help of a
dummy node current, and stop the counting
when current reaches the starting node head.
• If the list is empty, head will be NULL, and in that
case set count = 0.
• Otherwise, set the current pointer to the first
node, and keep on counting till the current
pointer reaches the starting node.
• Time Complexity: O(n), for scanning the
complete list of size n.
• Space Complexity: O(1), for creating one
temporary variable.
Printing the Contents of a Circular
Linked List
• We assume here that the list is being accessed by
its head node. Since all the nodes are arranged in
a circular fashion, the tail node of the list will be
the node previous to the head node.
• Let us assume we want to print the contents of
the nodes starting with the head node. Print its
contents, move to the next node and continue
printing till we reach the head node again.
• Time Complexity: O(n), for scanning the
complete list of size n.
• Space Complexity: O(1), for temporary
variable.
Inserting a Node at the End of a
Circular Linked List
• Let us add a node containing data, at the end
of a list (circular list) headed by head.
• The new node will be placed just after the tail
node (which is the last node of the list), which
means it will have to be inserted in between
the tail node and the first node.
– Create a new node and initially keep its next
pointer pointing to itself.
• Update the next pointer of the new node with
the head node and also traverse the list to the
tail. That means in a circular list we should
stop at the node whose next node is head.
• Update the next pointer of the previous node
to point to the new node and we get the list as
shown below.
Inserting a Node at the Front of a
Circular Linked List
• The only difference between inserting a node
at the beginning and at the end is that, after
inserting the new node, we just need to
update the pointer. The steps for doing this
are given below:
• Create a new node and initially keep its next
pointer pointing to itself.
• Update the next pointer of the new node with
the head node and also traverse the list until
the tail. That means in a circular list we should
stop at the node which is its previous node in
the list.
• Update the previous head node in the list to
point to the new node.
• Make the new node as the head.
• Time Complexity: O(n), for scanning the
complete list of size n.
• Space Complexity: O(1), for temporary
variable.
Deleting the Last Node in a Circular
Linked List
• The list has to be traversed to reach the last
but one node. This has to be named as the tail
node, and its next field has to point to the first
node. Consider the following list.
• To delete the last node 40, the list has to be
traversed till you reach 7. The next field of 7
has to be changed to point to 60, and this
node must be renamed pTail
• Traverse the list and find the tail node and its
previous node.
• Update the next pointer of tail node’s previous
node to point to head.
• Dispose of the tail node.
• Time Complexity: O(n), for scanning the
complete list of size n.
• Space Complexity: O(1), for a temporary
variable.
Deleting the First Node in a Circular
List
• The first node can be deleted by simply
replacing the next field of the tail node with
the next field of the first node.
• Find the tail node of the linked list by
traversing the list. Tail node is the previous
node to the head node which we want to
delete.
• Create a temporary node which will point to
the head. Also, update the tail nodes next
pointer to point to next node of head (as
shown below).
• Now, move the head pointer to next node.
Create a temporary node which will point to
head. Also, update the tail nodes next pointer
to point to next node of head (as shown
below).
Time Complexity and Applications
• Time Complexity: O(n), for scanning the
complete list of size n.
• Space Complexity: O(1), for a temporary
variable.
• Applications of Circular List
– Circular linked lists are used in managing the
computing resources of a computer. We can use
circular lists for implementing stacks and queues.

You might also like