DS Unit-1
DS Unit-1
DATA STRUCTURES
Basically data are represented by data values held temporarily within a program’s data area or recorded
permanently on a file. So, the simple data is represented by data type. Data structure is often confused with
data types. Data type is a collection of finite set of permitted values, and a set operations on those values,
whereas, Data structure is a study about the organizing the related data to enable us to work efficiently with
data, exploring the relationships within the data. A data type can be defined as follows.
Data type = Permitted Data Values + Operations
Often the different data values are related to each other. To enable programs to make use of these relationships,
these data values must be in an organized form. So the organized collection of data is called a data structure.
Data Structure = Organized Data + Allowed Operations
Definition:
Data may be organized in many different ways; the logical or mathematical model of a particular
organization of data is called a data structure. The data structure can also be defined as methods to store the
information in a computer.
Study of Data Structures study covers the following
» Amount of memory require to store
» Amount of time require to process
» Representation of data in memory
» Operations performs on data
Need of Data Structures:
As applications are getting complex and data rich, common problems that applications face now-a-days are
» Data Search: As data grows, search will become slower.
» Processor speed: Processor speed although being very high, falls limited if the data grows to billion
records.
» Multiple requests: As thousands of users can search data simultaneously on a web server, even the fast
server fails while searching the data.
Advantages of Data Structures
» Efficiency: The efficiency of any program depends upon the chosen data structure and thus, the
efficiency of a particular program depends upon the specific selected data structure.
» Reusability: Data structures are reusable, i.e., once we have implemented a particular data structure, we
can use it at any other place. Implementation of data structures can be compiled into libraries which can
be used by different clients.
» Abstraction: Data Structure also provides an Abstraction feature which helps a user to use data structure
through an interface without knowing the details of implementation.
Applications of Data Structures:
» Ticket booking applications like book my show, abhibus etc., uses a two-dimensional array
» Navigation of Webpages in a Web Browser uses Stack Data structure
» Tokens in a Bank uses Queue Data Structure.
» Songs in a play list uses Single or Double Linked list or Circular Linked List
» File Explorer of Computer or Mobile uses Tree Data Structure.
» Navigation Systems like Google maps uses Graph data structure to find the path between two cities.
Department of Computer Science, SSBN Degree College, ATP 1
Data Structures Unit - I
Data Structures
In second case the linear relationship b/w the elements represented by means of pointers or links. These are
linked lists.
STACK
A stack is an ordered collection of items into which new items may be inserted and from which items
may be deleted at one end, called the top of the stack.
(OR)
The stack is the list of elements in which an element may be inserted or deleted only at the end called
only the top of the stack. This implies that the elements are removed from the stack in the reverse order of
insertion. Sometimes these are also known as LIFO lists. The stack work in the principle of last-in first-out [LIFO].
Unlike that of the array, the definition of array itself provides for the insertion and deletion of items. So,
the stack can be considered as a dynamic, constantly changing object.
Examples of such a data structure are a stack of books, bolls in a tube etc. here an item can be remove or inserted
only from the top of any of the stacks. This implies that the last item to be added to the stack is the first item to
be removed.
On a stack only two operations are performed they are insertion and deletion. These are known as push and
pop respectively.
Primitive Operations:
The two changes, which can be made to a stack, are given special name. When an item is added to a
stack, it is pushed onto the stack, and when an item is removed, it is popped from the stack.
» Push: Add an item to a stack, moving the stack pointer (top) up to accommodate the new item.
» Pop: Removes the most recently pushed item from the stack, moving the stack pointer (top) down.
To use a stack efficiently we need to check status of stack as well. For the same purpose, the following
functionality is added to stacks −
» peek − get the top data element of the stack, without removing it.
» isFull − check if stack is full.
» isEmpty − check if stack is empty.
Example:
The below figure shows the general model that there is some element that is at the top of the stack, and
it is the only element that is visible.
Push Pop
G Top
F Top F F
E E E Top
D D D
C C C
B B B
A A A
In this example, a stack contains the elements A, B, C, D, E, and F where F is on the top of the stack. When a new
element G is added (push), it is placed on the top of the stack and the top is now pointing to the element G.
When an item is deleted (pop) form the stack, that deleted element is G. This is because the deletion can be
made only from the top.
Representation of stack:
Stacks may be represented inside the memory in two ways.
1. Sequential Representation
2. Linked Representation
Sequential Representation:
A Stack can be represented using a single dimensional array. Here an integer variable is used called top
which hold the top elements position.
Linked Representation:
A Stack can be represented using single linked lists. Here a pointer is maintained called top used to point
the top most element.
Stack Exceptions
Stack Overflow:
Occurs when a push operation is performed on a Stack which is already full.
Condition: if top>=MAX-1 → stack overflow
Stack Underflow:
Occurs when a pop operation is performed on a empty Stack.
Condition: if top==-1 → stack underflow
Overflow, however, is not a condition that is applicable to a stack as an abstract data structure.
Abstractly, it is always possible to push an element onto a stack. A stack is just an ordered set, and there is no
limit to the number of elements that such a set can contain. The possibility of overflow is introduced when a
stack is implemented by an array with only a finite number of elements, thereby prohibiting the growth of the
stack beyond that number.
Implementing the Push & Pop operations:
Algorithms:
Push: This procedure inserts an element item to the top of the stack which is represented by an arrays & variable
top denoting the top element in the stack.
Push(S, Size, item)
Step 1: check for overflow condition
if top>= Size -1
Print ”Stack overflow”;
return;
Step 2: increment top
top=top+1
Step 3: insert the element
S[top]=item
Step 4: return
Pop: This procedure deletes the top element of the stack and assigns it to the variable item.
Pop(S,item)
Step 1: check for stack underflow
if(top==-1)
Print ”Stack underflow”
return
Step 2: assign top element to x
item=S[top];
Step 3: decrement top
top=top-1
Step 4: return
Stack as an abstract type:
class Stack
{
long stk[];
int top;
public Stack(int size)
{
stk=new long[size];
top=-1;
}
public boolean isFull()
{
return (top>=stk.length-1);
}
public boolean isEmpty()
{
return (top==-1);
}
public void push(long item)
{
if(isFull())
{
System.out.println("Stack is Full");
return;
}
stk[++top]=item;
}
public void pop()
{
if(isEmpty())
{
System.out.println("Stack is empty");
return;
}
long item=stk[top--];
System.out.println("Popped element from Stack is :"+item);
}
public void display()
{
System.out.print("Stack :[ ");
for(int i=top;i>=0;i--)
System.out.print(stk[i]+" ");
System.out.println("]");
}
}
Applications of Stack:
» Conversion of expressions.
» Evaluating expressions
» Implementing function calls
» Maintaining the UNDO list for an application
» Checking the nesting of parentheses in an expression
Postponed Decision: - This is one of the applications of stack. Stacks are frequently used to indicate the order
of processing of data when some steps in processing must be postponed until some other conditions are
fulfilled.
D top
C
B
main
This can be explained by taking a main program which calls B and that calls C, which is internally calling
D functions. In this situation while processing main A, we required to move to B which is required in A. Then B
places A on to a stack and begin execution of B while processing B if C is required we move to C after placing B
on to a stack above A. Similarly, we place C over B and start processing D.
After completing D we can process only C from the stack which calls B is on the top of the stock. Then
we take B and after completing B we can go to the main program A. here at each stage the stack automatically
maintained the order that is require to complete the processing.
Example: Infix, Postfix and Prefix
Basic Definitions & Examples:
An arithmetic expression can be represented in three forms. They are
Infix Postfix Prefix
• Infix Notation: In an expression if the relative position of the operator is between the two operands then it
is known as Infix notation.
Examples: A+B, A+(B*C), (A+B)*C, (A+B)*(C-D)
• Postfix Notation: In this notation the operator follows the two operands.
Examples: AB+, ABC*+, AB+C*, AB+CD-*
• Prefix Notation: In this notation the operator follows the two operator precedes the two operands.
Examples: +AB, +A*BC, *+ABC, *+AB-CD
Modification must be made to this algorithm to accommodate parentheses? When an opening parenthesis is
read, it must be pushed onto the stack. This can be done by establishing the convention that prcd(op,’(‘) equals
FALSE, for any operator symbol op other than a right parenthesis. In addition, we define prcd(‘(‘,op) to be FALSE
for any operator symbol op. This ensures that an operator symbol appearing after a left parenthesis is pushed
onto the stack.
When a closing parenthesis is read, all operators up to the first opening parenthesis must be popped
from the stack into the postfix string. This can be done by defining prcd(op, ‘)’) as TRUE for all operators op
other than a left parenthesis.
prcd(‘(‘,op) = FALSE for any operator op
prcd(op,’(‘) = FALSE for any operator op rather than ‘)’
prcd(op,’)’) = TRUE for any operator op other than ‘(‘
prcd(‘)’,op) = undefined for any operator op (an attempt to compare the two indicates an error)
Example 2: (A + B) * C
Symb Postfix string OpStack
( (
A A (
+ A (+
B AB (+
) AB+
* AB+ *
C AB + C *
AB + C*
In this example, when the right parenthesis is encountered the stack is popped until a left parenthesis is
encountered, at which point both parentheses are discarded. By using parentheses to force an order of
precedence different than the default, the order of appearance of the operators in the postfix string is different
than in example 1.
Example 3: ((A – (B + C)) * D) $ (E + F)
Evaluating postfix Expression: While executing the infix expression computer first it converts the expression to
postfix and then evaluates. The easiest way to evaluate postfix expression is to use a stack. When a number is
seen it is pushed on to the stack, when an operator is seen the top 2 elements are popped from the stack and
the operator is obtained and the result is pushed on to the stack.
Ex: Evaluation of the following postfix expression involves the following steps.
Postfix: 6 5 2 3 + 8* + 3 + *
Step 1: Since the first 4 are operands they are pushed on to stack 6->5->2->3->top,
Step 2: next + is read, so 3 and 2 are popped from the stack and added result 5 is placed or pushed into the stack
6 -> 5 -> 5 ->top;
Step 3: next 8 is pushed into the stack. Then stack is
6 -> 5 -> 5 -> 8 ->top;
step 4: now * is read, so 8 and 5 are popped and multiplied and the result 40 is pushed on to the stack.
Then the stack is…
6 -> 5 -> 40 ->top;
step 5:Again the operator + is read. So 40 & 5 are popped added and result 45 is pushed on to the stack.
Then the stack is..
6->45->top;
step 6: Next 3 read and is pushed into the stock. Then the stack is…
6-> 45 ->3->top;
step 7: Next + is read so 3 & 45 are added and the result 48 is pushed into the stack. Then the stack is
6->48->top
finally, the * is read so, 48 and 6 are multiplied and the result 288 is pushed which is the result of the expression.
QUEUE
A queue is a linear list in which items may be added at one end and items may be removed only at the
other end. In queues the two ends are called front and rear. The end in which items may be added is called rear
and the other end in which deletions are done are called front.
Queues are also called first in first out lists, since the order in which elements enter a queue is the order
in which they live. This is contrast to stacks, which works on the principle of LIFO. There are so many examples,
in day to day life for queues. Coming to the computers the time saving system in which programs with the same
priority form a view while waiting for execution.
A B C
Front Rear
A B C D E
Front Rear
C D E
Front Rear
The basic operations insertion and deletion can be performed on queue. Here, when the array value
exceeds the size of view then the overflow occurs and an attempt to remove on element from any empty queue
can give an underflow.
Conditions of a queue:
1. front: The front pointer is used to delete an element from queue
2. rear: Rear pointer is used to insert an element in the queue
3. if rear= Size and front=0 (Size is capacity of queue) then queue full
4. if front=-1: queue is empty
5. if front=rear: then queue has only one element
Representation of Queues:
Queues may be represented inside the memory mainly in 2 ways by means of one way is linear arrays.
When a queue is maintained by a linear array Q it needs two pointer variables f and r to store the location of
front element and to store the location of rear element respectively.
When deleting an element f is incremented by 1 and whenever an element is inserted r is incremented
by 1. If Q size is n, after n insertions the near element of the queue will occupy Q(n). This happens even though
the queue itself may not containing many elements i.e. a shown below.
If we want to insert an element into ‘Q’ front rear the queue at this stage i.e. when n=r it is not possible.
One way to overcome this is to simply move the entire queue to the beginning of the array and changing front
and rear. But this process is very expensive so, the method is to assume queue as circular.
Circular Queue:
In the case of straight queue as the elements are deleted from the queue the remaining element doesn’t
shift forward. Hence, lot of memory space is wasted. When the elements are added up to the last location of
the queue no more elements can be added even though free memory locations are presented in the beginning
of the queue. To overcome, this circular queues are used. If the queue is circular q[1] comes after q[n] in the
array, i.e as soon as all reaches n, instead of increasing r to n+1 be reset r=0. Similarly if f=n and an element is
deleted be reset front=0.
Operations on Queue:
» Insert or Enqueue : adding an element at the rear end of the Circular Queue.
» Delete or Dequeue: removing an element from the fronty end of the Circular Queue.
Exceptions:
» Queue Overflow: Occurs when an insert operation is performed on a Circular Queue which is already full
Condition: (front == 0 && rear == MAX-1 ) || (front == rear +1 )
» Queue Underflow: Occurs when a delete operation is performed on an empty Circular Queue.
Condition: front == -1
Note: when front=rear then queue has only one element
}
Priority Queue:
A priority queue is a collection of elements such that each element has been assigned a priority and such
that the order in which elements are deleted and protest comes under the following rules.
1. An element of higher priority is processed before any element of lower priority
2. Two elements with same priority are processed according to the order in they were added to the queue.
The best example for priority queue is time sharing system. Programs of higher priority processed first
and the programs with same priority form straight queue.
Array implementation of a Priority Queue
A stack and a queue can be implemented in an array so that each insertion or deletion involves
accessing only a single element of the array. Unfortunately, this is not possible for a priority queue.
Suppose that the n elements of a priority queue pq are maintained in positions 0 to n-1 of an array
pq.items of size maxpq, and suppose that pq.rear equals the first empty array position, n. Then pqinsert(pq,x)
would seem to be a fairly straightforward operation:
If (pq.rear >= maxpq)
{
Print (“Priority Queue overflow”)
exit(1)
}
pq.items[pq.rear] = x;
pq.rear++;
Note that under this insertion method the elements of the priority queue are not kept ordered in the array.
As long as only insertions take place, this implementation works well. Suppose, however, that we
attempt the operation pqmindelete(pq) on an ascending priority queue. This raises two issues. First, to locate
the smallest element, every element of the array from pq.items[0] through pq.items[pq.rear-1] must be
examined. Therefore a deletion requires accessing every element of the priority queue. Priority queue
deletion under this implementation requires both searching for the element to be deleted and removal of an
element in the middle of an array.
1. A special “empty” indicator can be placed into a deleted position. This indicator can be a value that is
invalid as an element (for example, --1 in a priority queue of nonnegative numbers), or a separate field
can be contained in each array element to indicate whether it is empty. Insertion proceeds as before,
but when pq.rear reaches maxpq the array elements are compacted into the front of the array
disadvantages to this approach. First, the search process to locate the maximum or minimum element
must examine all the deleted array positions in addition to the actual priority queue elements. If many
items have been deleted but no compaction has yet taken place, the deletion operation accesses many
more array elements than exist in the priority queue. Second, once in awhile insertion requires
accessing every single position of the array, as it runs out of room and begins compaction.
2. The deletion operation labels a position empty as in the previous solution, but insertion is modified to
insert a new item in the first “empty” position. Insertion then involves accessing every array element
up to the first one that has been deleted. This decreased efficiency of insertion is a major drawback to
this solution.
3. Each deletion can compact the array by shifting all elements past the deleted element by one position
and then decrementing pq.rear by 1. Insertion remains unchanged . On the average, half of all priority
queue elements are shifted for each deletion , so that deletion becomes quite inefficient. A slightly
better alternative is to shift either all preceding elements forward or all succeeding elements
backward, depending on which group is smaller. This would require maintaining both front and rear
indicators and treating the array as a circular structure, as we did for the queue.
4. Instead of maintaining the priority queue as an unordered array, maintain it is an ordered, circular
array as follows;
class pqueue
{
private int items [];
private int front, rear;
public pqueue(int size)
{
Items=new int[size];
front=rear=-1;
}
}
pqueue pq=new pqueue(10);
pq.front is the position of the smallest element, pq.rear is 1 greater than the position of the largest. Deletion
involves merely increasing pq.front (for the ascending queue) or decreasing pq.rear (for a descending queue).
However, insertion requires locating the proper position of the new element and shifting the preceding or
succeeding elements (again, the technique of shifting whichever group is smaller is helpful). This method
moves the work of searching and shifting from the deletion operation to the insertion