DS UNIT-3 Notes
DS UNIT-3 Notes
DS UNIT-3 Notes
UNIT – III
Data Structures –Definition, Linear Data Structures, Non-Linear Data Structures,
Stacks - Overview of Stack, Implementation of Stack (List), Applications of Stack
Queues: Overview of Queue, Implementation of Queue (List), Applications of Queues, Priority Queues
Linked Lists – Implementation of Singly Linked Lists, Doubly Linked Lists, Circular Linked Lists.
Implementation of Stack and Queue using Linked list.
A Data Structure delivers a structured set of variables related to each other in various ways. It forms
the basis of a programming tool that signifies the relationship between the data elements and allows
programmers to process the data efficiently.
A data structure that preserves a linear connection among its data elements is known as a Linear Data
Structure. The arrangement of the data is done linearly, where each element consists of the successors
and predecessors except the first and the last data element. However, it is not necessarily true in the
case of memory, as the arrangement may not be sequential.
Based on memory allocation, the Linear Data Structures are further classified into two types:
1. Static Data Structures: The data structures having a fixed size are known as Static Data
Structures. The memory for these data structures is allocated at the compiler time, and their size
cannot be changed by the user after being compiled; however, the data stored in them can be
altered.
The Array is the best example of the Static Data Structure as they have a fixed size, and its data
can be modified later.
2. Dynamic Data Structures: The data structures having a dynamic size are known as Dynamic
Data Structures. The memory of these data structures is allocated at the run time, and their size
varies during the run time of the code. Moreover, the user can change the size as well as the
data elements stored in these data structures at the run time of the code.
Linked Lists, Stacks, and Queues are common examples of dynamic data structures
The following is the list of Linear Data Structures that we generally use:
1. Arrays
An Array is a data structure used to collect multiple data elements of the same data type into one
variable. Instead of storing multiple values of the same data types in separate variable names, we could
store all of them together into one variable. This statement doesn't imply that we will have to unite all
the values of the same data type in any program into one array of that data type. But there will often be
times when some specific variables of the same data types are all related to one another in a way
appropriate for an array.
An Array is a list of elements where each element has a unique place in the list. The data elements of
the array share the same variable name; however, each carries a different index number called a
subscript. We can access any data element from the list with the help of its location in the list. Thus,
the key feature of the arrays to understand is that the data is stored in contiguous memory locations,
making it possible for the users to traverse through the data elements of the array using their respective
indexes.
Figure 3. An Array
a) One-Dimensional Array: An Array with only one row of data elements is known as a One-
Dimensional Array. It is stored in ascending storage location.
b) Two-Dimensional Array: An Array consisting of multiple rows and columns of data elements
is called a Two-Dimensional Array. It is also known as a Matrix.
c) Multidimensional Array: We can define Multidimensional Array as an Array of Arrays.
Multidimensional Arrays are not bounded to two indices or two dimensions as they can include
as many indices are per the need.
a) We can store a list of data elements belonging to the same data type.
b) Array acts as an auxiliary storage for other data structures.
c) The array also helps store data elements of a binary tree of the fixed count.
d) Array also acts as a storage of matrices.
2. Linked Lists
A Linked List is another example of a linear data structure used to store a collection of data elements
dynamically. Data elements in this data structure are represented by the Nodes, connected using links
or pointers. Each node contains two fields, the information field consists of the actual data, and the
pointer field consists of the address of the subsequent nodes in the list. The pointer of the last node of
the linked list consists of a null pointer, as it points to nothing. Unlike the Arrays, the user can
dynamically adjust the size of a Linked List as per the requirements.
a) Singly Linked List: A Singly Linked List is the most common type of Linked List. Each node
has data and a pointer field containing an address to the next node.
b) Doubly Linked List: A Doubly Linked List consists of an information field and two pointer
fields. The information field contains the data. The first pointer field contains an address of the
previous node, whereas another pointer field contains a reference to the next node. Thus, we
can go in both directions (backward as well as forward).
c) Circular Linked List: The Circular Linked List is similar to the Singly Linked List. The only
key difference is that the last node contains the address of the first node, forming a circular loop
in the Circular Linked List.
a) The Linked Lists help us implement stacks, queues, binary trees, and graphs of predefined size.
b) We can also implement Operating System's function for dynamic memory management.
c) Linked Lists also allow polynomial implementation for mathematical operations.
d) We can use Circular Linked List to implement Operating Systems or application functions that
Round Robin execution of tasks.
e) Circular Linked List is also helpful in a Slide Show where a user requires to go back to the first
slide after the last slide is presented.
f) Doubly Linked List is utilized to implement forward and backward buttons in a browser to
move forward and backward in the opened pages of a website.
STACK:
A stack is a linear data structure that stores items in a Last-In/First-Out (LIFO) or First-In/Last-
Out (FILO) manner. In stack, a new element is added at one end and an element is removed from that
end only. The insert and delete operations are often called push and pop
Push operation as demonstrated in below diagram will add an element at the top of stack. Refer
the below image for more understanding:
Pop operation as demonstrated in below diagram removes an element from the top of
the stack. Refer the below image for more understanding:
class Stack:
top=-1 #class variable
def __init__(self,size):
self.MAX=size
self.list1=[]
print("stack is created ")
def push_ele(self,x):
if Stack.top== self.MAX-1:
print("stack is full")
else:
Stack.top=Stack.top+1
print(Stack.top)
self.list1.insert( Stack.top , x) #insert element into the stack.
def pop_ele(self):
if Stack.top== -1:
print("stack is empty")
else:
m=Stack.top
print(m)
k=self.list1.pop( m ) #// to remove an item from the stack
Stack.top=Stack.top-1
return k
def display(self):
if Stack.top== -1:
print("stack is empty")
else:
print("elements in the stack are ")
print(self.list1)
ob=Stack(5)
ob.push_ele(25)
ob.push_ele(45)
ob.push_ele(65)
ob.push_ele(85)
ob.push_ele(95)
ob.push_ele(105)
print(Stack.top)
ob.display()
ob.push_ele(16)
ob.push_ele(12)
ob.display()
Output:
stack is created
0
1
2
3
4
stack is full
4
elements in the stack are
[25, 45, 65, 85, 95]
stack is full
stack is full
elements in the stack are
[25, 45, 65, 85, 95]
4
popped element is 95
3
popped element is 85
after deleting or popping the 2 items stack is
elements in the stack are
[25, 45, 65]
2
popped element is 65
1
popped element is 45
0
popped element is 25
stack is empty
popped element is None
QUEUE
Like a stack, the queue is a linear data structure that stores items in a First In First Out (FIFO) manner.
With a queue, the least recently added item is removed first. A good example of a queue is any queue
of consumers for a resource where the consumer that came first is served first.
Dequeue: Removes an item from the queue. The items are popped in the same order in
which they are pushed. If the queue is empty, then it is said to be an Underflow condition
– Time Complexity : O(1)
Front: Get the front item from queue – Time Complexity : O(1)
Rear: Get the last item from queue – Time Complexity : O(1)
Working of Queue
Enqueue Operation
Dequeue Operation
class Queue:
def __init__(self):
self.queue = []
# Add an element
def enqueue(self, item):
self.queue.append(item)
# Remove an element
def dequeue(self):
if len(self.queue) < 1:
return None
return self.queue.pop(0)
def size(self):
return len(self.queue)
q = Queue()
q.enqueue(10)
q.enqueue(20)
q.enqueue(30)
q.enqueue(40)
q.enqueue(50)
q.display()
q.dequeue()
print("After removing an element")
q.display()
OUTPUT:
Current Queue: [10, 20, 30, 40, 50]
Dequeued item: 10
Dequeued item: 20
Updated Queue: [30, 40, 50]
Before knowing about the types of a linked list, we should know what is linked list. So, to know about
the linked list, click on the link given below:
The singly linked list is a linear data structure in which each element of the list contains a pointer which
points to the next element in the list. Each element in the singly linked list is called a node. Each node
has two components: data and a pointer next which point to the next node in the list. The first node of
the list is called as head, and the last node of the list is called a tail. The last node of the list contains a
pointer to the null. Each node in the list can be accessed linearly by traversing through the list from
head to tail.
Consider the above example; node 1 is the head of the list and node 4 is the tail of the list. Each node
is connected in such a way that node 1 is pointing to node 2 which in turn pointing to node 3. Node 3
is again pointing to node 4. Node 4 is pointing to null as it is the last node of the list.
LL.printLL()
print("After inserting node at position 2")
LL.insert_position(5,45)
LL.printLL()
Output:
After creation the elents are
30
40
50
60
70
After inserting node at position 2
30
40
50
60
45
70
temp.next = new_Node
new_next=self.head
else:
self.head = new_Node
def remove_first_node(self):
if(self.head == None):
return
else:
temp=self.head
self.head = temp.next
self.temp=None
def remove_last_node(self):
prev=self.head
temp=self.head.next
while temp.next is not None:
temp=temp.next
prev=prev.next
prev.next=None
def remove_at_index(self, index):
if self.head == None:
return
temp = self.head
position = 0
if position == index:
self.remove_first_node()
else:
while(temp != None and position+1 != index):
position = position+1
temp = temp.next
if temp != None:
temp.next = temp.next.next
else:
print("Index not present")
# print method for the linked list
def printLL(self):
temp = self.head
while(temp):
print(temp.data)
temp = temp.next
# Singly Linked List with insertion and print methods
LL = LinkedList()
LL.insert(30)
LL.insert(40)
LL.insert(50)
LL.insert(60)
LL.insert(70)
print("After creation the elents are")
LL.printLL()
print("After deleting first element")
LL.remove_first_node()
LL.printLL()
#print("After deleting last element")
#LL.remove_last_node()
#LL.printLL()
print("After Deleting node at index 2")
LL.remove_at_index(2)
LL.printLL()
Output:
After creation the elents are
30
40
50
60
70
After deleting first element
40
50
60
70
After Deleting node at index 2
40
50
70
Doubly Linked List is a variation of the linked list. The linked list is a linear data structure which can
be described as the collection of nodes. Nodes are connected through pointers. Each node contains two
fields: data and pointer to the next field. The first node of the linked list is called the head, and the last
node of the list is called the tail of the list.
One of the limitations of the singly linked list is that it can be traversed in only one direction that is
forward. The doubly linked list has overcome this limitation by providing an additional pointer that
points to the previous node. With the help of the previous pointer, the doubly linked list can be traversed
in a backward direction thus making insertion and deletion operation easier to perform. So, a typical
node in the doubly linked list consists of three fields:
The above picture represents a doubly linked list in which each node has two pointers to point to
previous and next node respectively. Here, node 1 represents the head of the list. The previous pointer
of the head node will always point to NULL. Next pointer of node one will point to node 2. Node 5
represents the tail of the list whose previous pointer will point to node 4, and the next will point to
NULL
Illustration:
See the below illustration where E is being inserted at the beginning of the doubly linked
list
class DoublyLinkedList:
def __init__(self):
self.head = None;
self.tail = None;
def display(self):
Output
Nodes of doubly linked list:
10
20
30
40
50
The circular linked list is a kind of linked list. First thing first, the node is an element of the list, and it
has two parts that are, data and next. Data represents the data stored in the node, and next is the pointer
that will point to the next node. Head will point to the first element of the list, and tail will point to the
last element in the list. In the simple linked list, all the nodes will point to their next element and tail
will point to null.
The circular linked list is the collection of nodes in which tail node also point back to head node. The
diagram shown below depicts a circular linked list. Node A represents head and node D represents tail.
So, in this list, A is pointing to B, B is pointing to C and C is pointing to D but what makes it circular
is that node D is pointing back to node A.
Algorithm:
1. Define a Node class which represents a node in the list. It has two properties data and next
which will point to the next node.
2. Define another class for creating the circular linked list, and it has two nodes: head and tail. It
has two methods: add() and display() .
3. add() will add the node to the list:
4. It first checks whether the head is null, then it will insert the node as the head.
5. Both head and tail will point to the newly added node.
6. If the head is not null, the new node will be the new tail, and the new tail will point to the head
as it is a circular linked list.
Program:
1. #Represents the node of list.
2. class Node:
3. def __init__(self,data):
4. self.data = data;
5. self.next = None;
6.
7. class CreateList:
8. #Declaring head and tail pointer as null.
9. def __init__(self):
10. self.head = Node(None);
11. self.tail = Node(None);
52. cl.add(3);
53. cl.add(4);
54. #Displays all the nodes present in the list
55. cl.display();
Output:
# the node class is used to create an element holder and the respective reference pointers.
class Node:
def __init__(self, data=None, next=None):
self.data = data
self.next = next
class Stack:
def __init__(self):
self.top = None
def push(self, data):
if self.top is None:
self.top = Node(data, None)
return
self.top = Node(data, self.top)
def pop(self):
if self.top is None:
return
temp = self.top
if self.top is not None:
self.top = self.top.next
temp.next = None
return temp.data
def peek(self):
return self.top.data
def clearstack(self):
self.top = None
def emptystack(self):
if self.top is None:
return True
return False
def display(self):
itr = self.top
sstr = ' '
while itr:
sstr += str(itr.data) + '-->'
itr = itr.next
print(sstr)
if __name__ == "__main__":
stack = Stack()
stack.push(10)
stack.push(20)
stack.push(30)
stack.peek()
print("Display all the elements")
stack.display()
print("Delete top of the element")
stack.pop()
stack.display()
print("after iserting one element")
stack.push(40)
stack.display()
print("after clearstack")
stack.clearstack()
print("Stack is empty")
stack.display()
OUTPUT:
Display all the elements
30-->20-->10-->
Delete top of the element
20-->10-->
after iserting one element
40-->20-->10-->
after clearstack
Stack is empty
class Node:
class Queue:
def __init__(self):
self.head = None
self.last=None
def dequeue(self):
if self.head is None:
return None
else:
temp= self.head.data
self.head = self.head.next
self.head.prev=None
return temp
def first(self):
return self.head.data
def size(self):
temp=self.head
count=0
while temp is not None:
count=count+1
temp=temp.next
return count
def isEmpty(self):
if self.head is None:
return True
else:
return False
def printqueue(self):
if __name__=='__main__':
queue = Queue()
queue.enqueue(2)
queue.enqueue(4)
queue.enqueue(6)
queue.enqueue(8)
queue.printqueue()
queue.dequeue()
queue.dequeue()
print("\nqueue is empty:",queue.isEmpty())