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

DS Unit 2

It is very useful to reasearch more on this topic is make good and bright future

Uploaded by

harinikadevi9
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)
13 views

DS Unit 2

It is very useful to reasearch more on this topic is make good and bright future

Uploaded by

harinikadevi9
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/ 33

UNIT II LINEAR STRUCTURES

List ADT – array-based implementations – linked list implementations – singly linked lists –
circularly linked lists – doubly linked lists – applications of lists – Stack ADT – Queue ADT –
double ended queues

Data Structure

Organizing, managing and storing data is important as it enables easier access and efficient
modifications. Data Structures allows you to organize your data in such a way that enables you
to store collections of data, relate them and perform operations on them accordingly.
A data structure is classified into two categories:
● Linear data structure
● Non-linear data structure

What is the Linear data structure?


A linear data structure is a structure in which the elements are stored sequentially, and
the elements are connected to the previous and the next element.

The types of linear data structures are Array, Queue, Stack, Linked List.

LIST ADT

Python’s list structure is a mutable sequence container that can change size as items are added
or removed. It is an abstract data type that is implemented using an array structure to store the
items contained in the list.

Creating a Python List

Suppose we create a list containing several values:

pyList = [ 4, 12, 2, 34, 17 ]

When the list() constructor is called, an array structure is created to store the items contained
in the list. The array is initially created bigger than needed, leaving capacity for future
expansion. The values stored in the list comprise a subarray in which only a contiguous subset
of the array elements are actually used.
Appending Items

pyList.append( 50 )

If there is room in the array, the item is stored in the next available slot of the array and the
length _eld is incremented by one. The result of appending 50 to pyList is illustrated in Figure
2.3.

What happens when the array becomes full and there are no free elements in which to add a
new list item? For example, consider the following list operations:

pyList.append( 18 )
pyList.append( 64 )
pyList.append( 6 )

After the second statement is executed, the array becomes full and there is no available space
to add more values

By definition, a list can contain any number of items and never becomes full. Thus, when the
third statement is executed, the array will have to be expanded to make room for value 6. From
the discussion in the previous section, we know an array cannot change size once it has been
created. To allow for the expansion of the list, the following steps have to be performed:
Extending A List
A list can be appended to a second list using the extend() method as shown in the following
example:
pyListA = [ 34, 12 ]
pyListB = [ 4, 6, 31, 9 ]
pyListA.extend( pyListB )

Inserting Items
An item can be inserted anywhere within the list using the insert() method. In the following
example
pyList.insert( 3, 79 )
we insert the value 79 at index position 3. Since there is already an item at that position, we
must make room for the new item by shifting all of the items down one position starting with
the item at index position 3. After shifting the items, the value 79 is then inserted at position 3
Removing Items
An item can be removed from any position within the list using the pop() method. Consider the
following code segment, which removes both the first and last items from the sample list:
pyList.pop( 0 ) # remove the first item
pyList.pop() # remove the last item

List Slice

Slicing is an operation that creates a new list consisting of a contiguous subset of elements
from the original list.

aSlice = theVector[2:3]
Complexity of List Operations

ARRAY-BASED IMPLEMENTATIONS OF LIST

Python list class provides a highly optimized implementation of dynamic arrays,

If an element is appended to a list at a time when the underlying array is full, we perform the
following steps:
• Allocate a new array B with larger capacity.
• Set B[i] = A[i], for i Æ 0, ...,n1, where n denotes current number of items.
• Set A = B, that is, we henceforth use B as the array supporting the list.
• Insert the new element in the new array.

Implement List ADT Using Python Arrays

import ctypes
class DynamicArray(object):
def __init__(self):
self.n = 0 # Count actual elements (Default is 0)
self.capacity = 1 # Default Capacity
self.A = self.make_array(self.capacity)

def __len__(self):
return self.n

def __getitem__(self, k):


if not 0 <= k <self.n:
# Check it k index is in bounds of array
return IndexError('K is out of bounds !')

return self.A[k] # Retrieve from the array at index k

def append(self, ele):


if self.n == self.capacity:
# Double capacity if not enough room
self._resize(2 * self.capacity)

self.A[self.n] = ele # Set self.n index to element


self.n += 1

def insertAt(self,item,index):
if index<0 or index>self.n:
print("please enter appropriate index..")
return

if self.n==self.capacity:
self._resize(2*self.capacity)

for i in range(self.n-1,index-1,-1):
self.A[i+1]=self.A[i]

self.A[index]=item
self.n+=1

def delete(self):
if self.n==0:
print("Array is empty deletion not Possible")
return

self.A[self.n-1]=0
self.n-=1

def removeAt(self,index):
if self.n==0:
print("Array is empty deletion not Possible")
return
if index<0 or index>=self.n:
return IndexError("Index out of bound....deletion not possible")

if index==self.n-1:
self.A[index]=0
self.n-=1
return

for i in range(index,self.n-1):
self.A[i]=self.A[i+1]

self.A[self.n-1]=0
self.n-=1

def _resize(self, new_cap):


B = self.make_array(new_cap) # New bigger array

for k in range(self.n): # Reference all existing values


B[k] = self.A[k]

self.A = B # Call A the new bigger array


self.capacity = new_cap # Reset the capacity

def make_array(self, new_cap):


return (new_cap * ctypes.py_object)()
# Instantiate
arr = DynamicArray() # Append new element
arr.append(1)
len(arr) # Append new element
arr.append(2) # Check length
len(arr) # Index
arr[0]
arr. insertAt(5)
arr.removeAt(5)

LINKED LIST IMPLEMENTATIONS

LIST (can be implemented by 2 ways)

Array Linked List

Linked lists and arrays are similar since they both store collections of data. Array is the most
common data structure used to store collections of elements. Arrays are convenient to declare
and provide the easy syntax to access any element by its index number. Once the array is set
up, access to any element is convenient and fast.
The disadvantages of arrays are:

• The size of the array is fixed. Most often this size is specified at compile time. This makes
the programmers to allocate arrays, which seems "large enough" than required.
• Inserting new elements at the front is potentially expensive because existing elements need
to be shifted over to make room.
• Deleting an element from an array is not possible. Linked lists have their own strengths and
weaknesses, but they happen to be strong where arrays are weak.
Generally array's allocates the memory for all its elements in one block whereas linked lists
use an entirely different strategy. Linked lists allocate memory for each element separately and
only when necessary.

Linked List Concepts:


A linked list is a non-sequential collection of data items. It is a dynamic data structure. For
every data item in a linked list, there is an associated pointer that would give the memory
location of the next data item in the linked list. The data items in the linked list are not in
consecutive memory locations. They may be anywhere, but the accessing of these data items
is easier as each data item contains the address of the next data item.

Advantages of linked lists:


Linked lists have many advantages. Some of the very important advantages are:
1. Linked lists are dynamic data structures. i.e., they can grow or shrink during the execution
of a program.
2. Linked lists have efficient memory utilization. Here, memory is not preallocated. Memory
is allocated whenever it is required and it is de-allocated (removed) when it is no longer needed.
3. Insertion and Deletions are easier and efficient. Linked lists provide flexibility in inserting a
data item at a specified position and deletion of the data item from the given position.
4. Many complex applications can be easily carried out with linked lists.

Disadvantages of linked lists:


1. It consumes more space because every node requires a additional pointer to store address of
the next node.
2. Searching a particular element in list is difficult and also time consuming.

Types of Linked Lists:


Basically we can put linked lists into the following four items:
1. Single Linked List.
2. Double Linked List.
3. Circular Linked List.

SINGLY LINKED LISTS

A linked list allocates space for each element separately in its own block of memory called a
"node". Each node contains two fields; a "data" field to store whatever element, and a "next"
field which is a pointer used to link to the next node.
The basic operations in a single linked list are:
● Creation.
● Insertion.
● Deletion.
● Traversing.
● Searching

Creating a node for Single Linked List: Creating a singly linked list starts with creating a
node. Sufficient memory has to be allocated for creating a node. The information is stored in
the memory.

Create a New node


New.Value=Element
New.Next = None

Insertion of a Node:
One of the most primitive operations that can be done in a singly linked list is the insertion of
a node. Memory is to be allocated for the new node (in a similar way that is done while creating
a list) before reading the data. The new node will contain empty data field and empty next field.
The data field of the new node is then stored with the information read from the user. The next
field of the new node is assigned to NULL. The new node can then be inserted at three different
places namely:
• Inserting a node at the beginning.
• Inserting a node at the end.
• Inserting a node at intermediate position.

Inserting a node at the beginning.


Inserting a node at the end:

Inserting a node into the single linked list at a specified intermediate position other than
beginning and end.

Algorithm -
Create a New node
New.value = element
If position < size of list
Then,
If size of list = 0
Then,
Head -> New node
Else If position = 0
Then,
New.next -> Head
Head -> New
Else If position < size
Then,
Temp = Head
For i in range of 0 to position – 1
/*position - 1 is used to reach the element after which the new node is to be
inserted */
Do,
Temp -> Temp.next // traversal using next pointer
New.next -> Temp.next
Temp.next -> New
Increment count
Else
Invalid insertion // position greater than size

Deletion of a node:
Another primitive operation that can be done in a singly linked list is the deletion of a node.
Memory is to be released for the node to be deleted. A node can be deleted from the list from
three different places namely.
• Deleting a node at the beginning.
• Deleting a node at the end.
• Deleting a node at intermediate position.

Deleting a node at the beginning:

Algorithm -
If list is not Empty and position < size
Then,
temp = head
for i in range of 0 to position - 1
Do,
temp -> temp.next
res = temp.next.value
temp.next -> temp.next.next
Decrement count
return res
else
return 0 or a delimiter to specify List is empty
Traversal and displaying a list (Left to Right):
To display the information, you have to traverse (move) a linked list, node by node from the
first node, until the end of the list is reached.

Traversing a list involves the following steps:


• Assign the address of start pointer to a temp pointer.
• Display the information from the data field of each node. The function traverse () is used for
traversing and displaying the information stored in the list from left to right.

Algorithm –
If list is not Empty and position < size
Then,
i=0
temp -> head
for i in range 0f 0 to position
Do,
If temp -> temp.next
Return temp.next.value
Else
Return 0

Searching Operation
The pointer traverse across the list until the element is found or end is reached.

If list is not Empty and position < size


Then,
i=0
temp -> head
While temp.next is not null //end is not reached
Do,
If temp.value == element
Then,
Return i
Else,
temp -> temp.next
increment i
Else
Return -1

DOUBLY LINKED LISTS

A double linked list is a two-way list in which all nodes will have two links. This helps in
accessing both successor node and predecessor node from the given node position. It provides
bidirectional traversing. Each node contains three fields:
● Left link.
● Data.
● Right link.
The left link points to the predecessor node and the right link points to the successor node.
The data field stores the required data. Many applications require searching forward and
backward through nodes of a list. For example searching for a name in a telephone directory
would need forward and backward scanning thru a region of the whole list.
The basic operations in a double linked list are:
● Creation.
● Insertion.
● Deletion.
● Traversing.

The beginning of the double linked list is stored in a "start" pointer which points to the first
node. The first node‟s left link and last node‟s right link is set to NULL.

Creating and inserting a node for Double Linked List:

Creating a double linked list starts with creating a node. Sufficient memory has to be allocated
for creating a node. The information is stored in the memory.
Double Linked List with 3 nodes:

Inserting a node at the end:


Insert Algorithm
Create a New Node
New.value=element
If position<size of list
Then,
If size of list=0
Then,
Head -> New Node
Else if position=0
Then,
New.Next->Head
Head.Prev->New
Head->New
Else
Temp=Head
For i in range of 0 to position
Do
Temp -> Temp.next
Temp1 = temp.prev
New.next -> Temp
New.prev -> Temp1
Temp1.next -> New
Temp.prev -> New
Increment Count
Else
Invalid Insertion

Delete Node Operation:


Deleting a node at Intermediate position:

Delete Algorithm:
If list is not empty and position<size
Then
Temp=head
For i in range 0 to position
Do
Temp-> temp.next
Temp1=temp.previous
Res=temp.value
Temp=temp.next
Temp1.next->temp
Temp1.previous->temp1
Decrement count
Return res
Else
Return 0

CIRCULARLY LINKED LISTS


It is just a single linked list in which the link field of the last node points back to the address of
the first node. A circular linked list has no beginning and no end. In circular linked list no null
pointers are used, hence all pointers contain valid address.
Creating a circular single Linked List with „n‟ number of nodes:
The basic operations in a circular single linked list are:
• Creation.
• Insertion.
• Deletion.
• Traversing.

Algorithm for Insert a node


APPLICATIONS OF LISTS

Dynamic Memory Allocation: As we know, we can dynamically allocate memory in a linked


list, so it can be very helpful when we don’t know the number of elements we are going to use.
Implementing advanced data structures: We can implement data structures like stacks and
queues with the help of a linked list.
Manipulating polynomials: We can do polynomials manipulation with the help of a linked
list by storing the constants in the nodes of the linked list.
Arithmetic operations on long integers: As integers have a limit, we cannot perform
arithmetic operations on long integers. But, if we use a linked list to represent the long integers,
we can easily perform the operations.
Graph Adjacency list Representation: Linked List helps in storing the adjacent vertices of a
graph in adjacency list representation.
In web browsers, you might have seen that we can always access the previous and next URL
using the back and forward button. Access to previous and next URL searched is possible
because they are linked using a linked list.
The songs in the Music Player are linked to the next and the previous song. We can play songs
either from the starting or the end of the list.
In an Image Viewer, the next and the previous images are linked; hence they can be accessed
by the previous and the next button.

STACK ADT

Stack is an Abstract data structure (ADT) works on the principle Last In First Out (LIFO).
The last element add to the stack is the first element to be delete. Insertion and deletion can
be takes place at one end called TOP. It looks like one side closed tube.
● The add operation of the stack is called push operation
● The delete operation is called as pop operation.
● Push operation on a full stack causes stack overflow.
● Pop operation on an empty stack causes stack underflow.
● SP is a pointer, which is used to access the top element of the stack.
● If you push elements that are added at the top of the stack;
● In the same way when we pop the elements, the element at the top of the stack is
deleted.

Insertion into Stack - Push


Algorithm

If stack is not full


Then
Assign the new element to a[size]
Update top pointer to a[size]
Size++

Removal from Stack - Pop

Algorithm

If stack is not empty


Then,
Temp=value at Top
Stack[size-1]=0
Size—
Update Top to stack[size-1]
Return Temp
Else
Return 0

Reading from Stack - Top


Algorithm
If stack is not empty
Then
Return value at Top
Else
Return 0

Search in a Stack

Algorithm -
found_flag =0
While found_flag is not 1 or Original_stack is not empty
Do,
If(Original_stack.top() is equal to required element)
Then
Found_flag = 1
Else
Temporary_stack.push(Original_stack.pop())
While Temporary_stack is not empty
Do,
Original_stack.push(Temporary_stack.pop())
Return found_flag
Sorting a Stack
Algorithm –
While Original_stack is not empty
Do,
Temp = Original_stack.pop()
If(Temp is less than sorted_stack.top()
Then
Sorted_stack.push(Temp)
Else
While sorted_stack.top() is less than Temp or Sorted_stack is not empty()
Do, Original_stack.push(sorted_stack.pop())
Sorted_stack.push(Temp)

Implementation of Stack using Pointers

Stack Applications:
1. Stack is used by compilers to check for balancing of parentheses, brackets and braces.
2. Stack is used to evaluate a postfix/prefix expression.
3. Stack is used to convert an infix expression into postfix/prefix form.
4. In recursion, all intermediate arguments and return values are stored on the processor‟s
stack.
5. During a function call the return address and arguments are pushed onto a stack and on
return they are popped off.
6. Depth first search uses a stack data structure to find an element from a graph

In-fix- to Postfix Transformation:


Procedure:
Procedure to convert from infix expression to postfix expression is as follows:
1. Scan the infix expression from left to right.
2. a) If the scanned symbol is left parenthesis, push it onto the stack.
b) If the scanned symbol is an operand, then place directly in the postfix expression (output).
c) If the symbol scanned is a right parenthesis, then go on popping all the items from the stack
and place them in the postfix expression till we get the matching left parenthesis.
d) If the scanned symbol is an operator, then go on removing all the operators from the stack
and place them in the postfix expression, if and only if the precedence of the operator which is
on the top of the stack is greater than (or equal) to the precedence of the scanned operator and
push the scanned operator onto the stack otherwise, push the scanned operator onto the stack.

Convert the following infix expression A + B * C – D / E * H into its equivalent postfix


expression.

Evaluating Arithmetic Expressions:


Procedure:
The postfix expression is evaluated easily by the use of a stack.
● When a number is seen, it is pushed onto the stack;
● When an operator is seen, the operator is applied to the two numbers that are popped
from the stack and the result is pushed onto the stack.

Evaluate the postfix expression: 6 5 2 3 + 8 * + 3 + *

Complexity:
All the 3 basic operations of the stack are of constant complexity O(1). Some Complex
operations like Searching and Sorting will have a complexity of O(n).

Queue ADT

A queue is a data structure that is best described as "first in, first out". A queue is another
special kind of list, where items are inserted at one end called the rear and deleted at the other
end called the front.

Types:
Single Ended Queue
Circular Array Based Queue
Double Ended Queue (Dequeue)
Single Ended Queue
It is a one way queue, where the elements get inside in the rear end and move out from the
front end. Opposite movement is restricted.

Enqueue - The operation of adding an element into the queue, which happens at the rear end.

Algorithm- Enqueue

If Queue is empty
Then,
Assign data in queue[0]
Front ->queue[0]
Rear ->queue[1]
Increment size
Else
Assign data to rear
Rear++
Size++

Dequeue - The operation of removing an element into the queue, which happens at the front
end.
Algorithm:
If queue is not empty
Then,
Temp=value at front
Front++
Size - -
Return Temp
Else
Return 0

Front - Looks up what is the front element in the queue.


.

Algorithm

If queue is not empty


Then,
Return value at front
Else
Return 0

Search in a Queue - Search 3 in the given queue.


Algorithm –
Flag = -1
For i in range of 0 to n
Do,
check if Front is required element
Then,
Flag = 1
Dequeue(enqueue)
Return Flag

Sort a Queue
Algorithm –
Sorted_queue.enqueue(Original_queue.dequeue)
While original queue is not empty
Do,
temp = original_queue.dequeue
for i in range of 0 to sorted_queue.size
Do,
If sorted_queue.front is less than temp
Then,
sorted_queue.enqueue(sorted_queue.dequeue)
Else
Sorted_queue.enqueue(temp)
temp = sorted_queue.dequeue
sorted_queue.enqueue(temp)

Circular Array Based Queue

● The normal queue implemented with a size constraint.


● When the end is reached and if there is a vacant space in the front, then the new element
is placed in the front as the end of the queue is at a fixed location.

Enqueue in Circular Array Based Queue


Double Ended Queue
● This is a two way queue, where the elements get inside and come out in both rear and
front ends.
● The enqueue and dequeue operations are renamed accordingly.

Operations in DEQUE
1. Insert element at back (Push Back)
2. Insert element at front (Push Front)
3. Remove element at front (Pop Front)
4. Remove element at back (Pop Back)

Algorithm:
Insert element at front (Push Front)

If Queue is empty
Then,
Assign data in Q[0]
Front ->Q[0]
Rear -> Q[1]
Size++
Else
Front - -
Assign data to front
Size + +

Insert element at back (Push Back)


If Queue is empty
Then,
Assign data in Q[0]
Front ->Q[0]
Rear -> Q[1]
Size++
Else
Assign data to rear
Rear++
Size + +

Remove element at front (Pop Front)

If queue is not empty


Then
Temp=value at front
Front + +
Size - -
Return temp
Else
Return 0

Remove element at back (Pop Back)

If queue is not empty


Then
Temp=value at rear – 1
Rear - -
Size - -
Return temp
Else
Return 0
Remove_back () is an operation used to remove element from

You might also like