CDS Unit 2
CDS Unit 2
DATA STRUCTURES
Data Structure can be defined as the group of data elements which provides an efficient way
of storing and organizing data in the computer so that it can be used efficiently.
Some examples of Data Structures are arrays, Linked List, Stack, Queue, etc. Data
Structures are widely used in almost every aspect of Computer Science i.e. Operating System,
Compiler Design, Artificial intelligence, Graphics and many more.
Data Structures are the main part of many computer science algorithms as they enable the
programmers to handle the data in an efficient way. It plays a vital role in enhancing the
performance of a software or a program as the main function of the software is to store and
retrieve the user's data as fast as possible.
Basic Terminology
Data structures are the building blocks of any program or the software. Choosing the appropriate
data structure for a program is the most difficult task for a programmer. Following terminology is
used as far as data structures are concerned.
Data: Data can be defined as an elementary value or the collection of values, for example,
student's name and its id are the data about the student.
Group Items: Data items which have subordinate data items are called Group item, for example,
name of a student can have first name and the last name.
Record: Record can be defined as the collection of various data items, for example, if we talk
about the student entity, then its name, address, course and marks can be grouped together to form
the record for the student.
File: A File is a collection of various records of one type of entity, for example, if there are 60
employees in the office then there will be 20 records in the related file where each record contains
the data about each employee.
Attribute and Entity: An entity represents the class of certain objects. it contains various
attributes. Each attribute represents the particular property of that entity.
Field: Field is a single elementary unit of information representing the attribute of an entity.
Arrays: An array is a collection of similar type of data items and each data item is called an
element of the array. The data type of the element may be any valid data type like char, int, float
or double.
The elements of array share the same variable name but each one carries a different index number
known as subscript. The array can be one dimensional, two dimensional or multidimensional.
The individual elements of the array age are:
age[0], age[1], age[2], age[3],................age[98], age[99].
What is a Stack?
A Stack is a linear data structure that follows the LIFO (Last-In-First-Out) principle.
Stack has one end, whereas the Queue has two ends (front and rear). It contains only one
pointer top pointer pointing to the topmost element of the stack.
Whenever an element is added in the stack, it is added on the top of the stack, and the
element can be deleted only from the stack.
In other words, a stack can be defined as a container in which insertion and deletion can be
done from the one end known as the top of the stack.
Some key points related to stack
o It is called as stack because it behaves like a real-world stack, files of books, etc.
o A Stack is an abstract data type with a pre-defined capacity, which means that it can
store the elements of a limited size.
o It is a data structure that follows some order to insert and delete the elements, and that
order can be LIFO or FILO.
Working of Stack
Stack works on the LIFO pattern. As we can observe in the below figure there are five
memory blocks in the stack; therefore, the size of the stack is 5.
Suppose we want to store the elements in a stack and let's assume that stack is empty. We
have taken the stack of size 5 as shown below in which we are pushing the elements one by one
until the stack becomes full.
Since our stack is full as the size of the stack is 5. In the above cases, we can observe that it goes
from the top to the bottom when we were entering the new element in the stack. The stack gets
filled up from the bottom to the top.
When we perform the delete operation on the stack, there is only one way for entry and exit as the
other end is closed. It follows the LIFO pattern, which means that the value entered first will be
removed last. In the above case, the value 5 is entered first, so it will be removed only after the
deletion of all the other elements.
Standard Stack Operations
The following are some common operations implemented on the stack:
o push(): When we insert an element in a stack then the operation is known as a push. If
the stack is full then the overflow condition occurs.
o pop(): When we delete an element from the stack, the operation is known as a pop. If the
stack is empty means that no element exists in the stack, this state is known as an underflow state.
o isEmpty(): It determines whether the stack is empty or not.
o isFull(): It determines whether the stack is full or not.'
o peek(): It returns the element at the given position.
o count(): It returns the total number of elements available in a stack.
o change(): It changes the element at the given position.
o display(): It prints all the elements available in the stack.
PUSH operation
The steps involved in the PUSH operation is given below:
o Before inserting an element in a stack, we check whether the stack is full.
o If we try to insert the element in a stack, and the stack is full, then the overflow condition
occurs.
o When we initialize a stack, we set the value of top as -1 or NULL to check that the stack
is empty.
o When the new element is pushed in a stack, first, the value of the top gets incremented,
i.e., top=top+1, and the element will be placed at the new position of the top.
o The elements will be inserted until we reach the max size of the stack.
POP operation
The steps involved in the POP operation is given below:
o Before deleting the element from the stack, we check whether the stack is empty.
o If we try to delete the element from the empty stack, then the underflow condition
occurs.
o If the stack is not empty, we first access the element which is pointed by the top
o Once the pop operation is performed, the top is decremented by 1, i.e., top=top-1.
Applications of Stack
The following are the applications of the stack:
o Balancing of symbols: Stack is used for balancing a symbol. For example, we have the
following program:
1. int main()
2. {
3. cout<<"Hello";
4. cout<<"javaTpoint";
5. }
As we know, each program has an opening and closing braces; when the opening braces come, we
push the braces in a stack, and when the closing braces appear, we pop the opening braces from
the stack. Therefore, the net value comes out to be zero. If any symbol is left in the stack, it means
that some syntax occurs in a program.
o String reversal: Stack is also used for reversing a string. For example, we want to reverse
a "javaTpoint"string,so we can achieve this with the
of a stack. First, we push all the characters of the string in a stack
until we reach the null character. After pushing all the characters, we start taking out the
character one by one until we reach the bottom of the stack.
o UNDO/REDO: It can also be used for performing UNDO/REDO operations.
For example, we have an editor in which we write 'a', then 'b', and then 'c'; therefore, the text
written in an editor is abc. So, there are three states, a, ab, and abc, which are stored in a stack.
There would be two stacks in which one stack shows UNDO state, and
the other shows
REDO state. If we want to perform UNDO operation, and
want to achieve 'ab' state, then we implement pop operation.
o Recursion: The recursion means that the function is calling itself again. To maintain the
previous states, the compiler creates a system stack in which all the previous records of the
function are maintained.
o DFS(Depth First Search): This search is implemented on a Graph, and Graph uses the
stack data structure.
o Backtracking: Suppose we have to create a path to solve a maze problem. If we are
moving in a particular path, and we realize that we come on the wrong way. In order to come at
the beginning of the path to create a new path, we have to use the stack data structure.
o Expression conversion: Stack can also be used for expression conversion. This is one of
the most important applications of stack. The list of the expression conversion is given below:
o Infix to prefix
o Infix to postfix
o Prefix to infix
o Prefix to postfix Postfix to infix
o Memory management: The stack manages the memory. The memory is assigned in the
contiguous memory blocks. The memory is known as stack memory as all the variables are
assigned in a function call stack memory. The memory size assigned to the program is known to
the compiler. When the function is created, all its variables are assigned in the stack memory.
When the function completed its execution, all the variables assigned in the stack are released.
In array implementation, the stack is formed by using the array. All the operations regarding
the stack are performed using arrays. Lets see how each operation can be implemented on the
stack using array data structure.
Adding an element into the top of the stack is referred to as push operation. Push operation
involves following two steps.
1. Increment the variable Top so that it can now refere to the next memory location.
2. Add element at the position of incremented top. This is referred to as adding new element
at the top of the stack.
Stack is overflown when we try to insert an element into a completely filled stack therefore, our
main function must always avoid stack overflow condition.
Algorithm:
1. #include <stdio.h>
2. int stack[100],i,j,choice=0,n,top=-1;
3. void push();
4. void pop();
5. void show();
6. void main ()
7. {
9. printf("Enter the number of elements in the stack ");
10. scanf("%d",&n);
11. printf("*********Stack operations using array*********"); 12.
13. printf("\n \n");
14. while(choice != 4)
15. {
16. printf("Chose one from the below options...\n");
17. printf("\n1.Push\n2.Pop\n3.Show\n4.Exit");
18. printf("\n Enter your choice \n");
19. scanf("%d",&choice);
20. switch(choice)
21. {
22. case 1:
23. {
24. push();
25. break;
26. }
27. case 2:
28. {
29. pop();
30. break;
31. }
32. case 3:
33. {
34. show();
35. break;
36. }
37. case 4:
38. {
39. printf("Exiting.........");
40. break;
41. }
42. default:
43. {
44. printf("Please Enter valid choice ");
45. }
46. };
47. }
48. } 49.
50. void push ()
51. {
52. int val;
53. if (top == n )
54. printf("\n Overflow");
55. else
56. {
57. printf("Enter the value?");
58. scanf("%d",&val);
59. top = top +1;
60. stack[top] = val;
61. }
62. }
64. void pop ()
65. {
66. if(top == -1)
67. printf("Underflow");
68. else
69. top = top -1;
70. }
71. void show()
72. {
73. for (i=top;i>=0;i--)
74. {
75. printf("%d\n",stack[i]);
76. }
77. if(top == -1)
78. {
79. printf("Stack is empty");
80. }
81. }
STACK IMPLEMENTATION USING ARRAYS
#include<stdio.h>
#define CAPACITY 5
int stack[CAPACITY],top=-1;
void push(int);
void pop();
void peek();
void display();
int main()
{
printf("Stack Implemenation:\n");
While(1)
{
int option;
printf("Choose option: 1)PUSH 2)POP 3)PEEK 4)DISPLAY 5)EXIT \n Enter option:");
scanf("%d",&option);
switch(option)
{
case 1 : push();
case 2 : pop();
case 3 : peek();
case 4 : display();
case 5 : exit();
default
printf("invalid option\n");
}
return 0;
}
}
void push(int ele)
{
if(isFull())
{
printf("Stack is Full\n");
}
else
{
top++;
stack[top]->ele;
printf("%d inserted\n",ele);
}
}
int isFull()
{
if(top==capcity-1)
{
return 1;
}
else
{
return 0;
}
}
int pop()
{
int ele;
if(isEmpty())
{
return 0;
}
else
{
ele->Sstack[top];
top--;
}
return top;
}
int isEmpty()
{
if(top==-1)
{
return 1;
}
else
{
return 0;
}
}
int peek()
{
if(isEmpty())
{
return 0;
}
else
{
return stack[top];
}
}
void display()
{
int i;
if(isEmpty())
{
printf("No elements\n");
else
{
printf("Stack Elements are:\n");
for(i=0;i<=top;i++)
{
printf("%d",stack[i]);
}
}
}
Arithmetic Expression Evaluation:
The stack organization is very effective in evaluating arithmetic expressions. Expressions are
usually represented in what is known as Infix notation, in which each operator is written
between two operands (i.e., A + B). With this notation, we must distinguish between ( A + B )*C
and A + ( B * C ) by using either parentheses or some operator-precedence convention. Thus, the
order of operators and operands in an arithmetic expression does not uniquely determine the
order in which the operations are to be performed.
1. Polish notation (prefix notation) –
It refers to the notation in which the operator is placed before its two operands. Here no
parentheses are required, i.e.,
+AB
2. Reverse Polish notation(postfix notation) –
It refers to the analogous notation in which the operator is placed after its two operands. Again,
no parentheses is required in Reverse Polish notation, i.e.,
AB+
Stack-organized computers are better suited for post-fix notation than the traditional infix
notation. Thus, the infix notation must be converted to the postfix notation. The conversion from
infix notation to postfix notation must take into consideration the operational hierarchy.
main ( ){
char a[50], ch;
int i,op1,op2,res,x; void push (int);
int pop( );
int eval (char, int, int);
printf("enter a postfix expression:");
gets (a);
Output:
When the above program is executed, it produces the following result −
Run 1:
enter a postfix expression:45+ evaluated value = 9
Run 2:
enter a postfix expression: 3 5 2 * + evaluated value = 13
Convert Infix to Postfix Expression
Infix expressions are readable and solvable by humans. We can easily distinguish the order of
operators, and also can use the parenthesis to solve that part first during solving mathematical
expressions. The computer cannot differentiate the operators and parenthesis easily, that’s why
postfix conversion is needed.
To convert infix expression to postfix expression, we will use the stack data structure. By scanning
the infix expression from left to right, when we will get any operand, simply add them to the
postfix form, and for the operator and parenthesis, add them in the stack maintaining the
precedence of them.
Algorithm to convert Infix To Postfix
Let, X is an arithmetic expression written in infix notation. This algorithm finds the equivalent
postfix expression Y.
1. Push “(“onto Stack, and add “)” to the end of X.
2. Scan X from left to right and repeat Step 3 to 6 for each element of X until the Stack is
empty.
3. If an operand is encountered, add it to Y.
4. If a left parenthesis is encountered, push it onto Stack.
5. If an operator is encountered ,then:
1. Repeatedly pop from Stack and add to Y each operator (on the top of Stack) which has
the same precedence as or higher precedence than operator.
2. Add operator to Stack. [End of If]
6. If a right parenthesis is encountered ,then:
1. Repeatedly pop from Stack and add to Y each operator (on the top of Stack) until a left
parenthesis is encountered.
2. Remove the left Parenthesis. [End of If]
[End of If]
7. END.
abc*+
Output Test Case 2:
Enter the expression : (a+b)*c+(d-a)
ab+c*da-+
Applications of Queue
Due to the fact that queue performs actions on first in first out basis which is quite fair for the
ordering of actions. There are various applications of queues discussed as below.
1. Queues are widely used as waiting lists for a single shared resource like printer, disk,
CPU.
2. Queues are used in asynchronous transfer of data (where data is not being transferred at
the same rate between two processes) for eg. pipes, file IO, sockets.
3. Queues are used as buffers in most of the applications like MP3 media player, CD player,
etc.
4. Queue are used to maintain the play list in media players in order to add and remove the
songs from the play- list.
5. Queues are used in operating systems for handling interrupts.
Types of Queue
There are four different types of queue that are listed as follows -
In Linear Queue, an insertion takes place from one end while the deletion occurs from another
end. The end at which the insertion takes place is known as the rear end, and the end at which the
deletion takes place is known as front end. It strictly follows the FIFO rule.
The major drawback of using a linear Queue is that insertion is done only from the rear end. If the
first three elements are deleted from the Queue, we cannot insert more elements even though the
space is available in a Linear Queue. In this case, the linear Queue shows the overflow condition
as the rear is pointing to the last element of the Queue.
Circular Queue
In Circular Queue, all the nodes are represented as circular. It is similar to the linear Queue except
that the last element of the queue is connected to the first element. It is also known as Ring Buffer,
as all the ends are connected to another end. The representation of circular queue is shown in the
below image -
The drawback that occurs in a linear queue is overcome by using the circular queue. If the empty
space is available in a circular queue, the new element can be added in an empty space by simply
incrementing the value of rear. The main advantage of using the circular queue is better memory
utilization.
Priority Queue
It is a special type of queue in which the elements are arranged based on the priority. It is a special
type of queue data structure in which every element has a priority associated with it. Suppose
some elements occur with the same priority, they will be arranged according to the FIFO principle.
The representation of priority queue is shown in the below image -
Insertion in priority queue takes place based on the arrival, while deletion in the priority queue
occurs based on the priority. Priority queue is mainly used to implement the CPU scheduling
algorithms.
There are two types of priority queue that are discussed as follows -
In Deque or Double Ended Queue, insertion and deletion can be done from both ends of the queue
either from the front or rear. It means that we can insert and delete elements from both front and
rear ends of the queue. Deque can be used as a palindrome checker means that if we read the string
from both ends, then the string would be the same.
Deque can be used both as stack and queue as it allows the insertion and deletion operations on
both ends. Deque can be considered as stack because stack follows the LIFO (Last In First Out)
principle in which insertion and deletion both can be performed only from one end. And in deque,
it is possible to perform both insertion and deletion from one end, and Deque does not follow the
FIFO principle.
Queue operations may involve initializing or defining the queue, utilizing it, and then completely
erasing it from the memory. Here we shall try to understand the basic operations associated with
queues −
enqueue() − add (store) an item to the queue.
dequeue() − remove (access) an item from the queue.
Few more functions are required to make the above-mentioned queue operation efficient. These
are −
peek() − Gets the element at the front of the queue without removing it.
isfull() − Checks if the queue is full.
isempty() − Checks if the queue is empty.
In queue, we always dequeue (or access) data, pointed by front pointer and while enqueing (or
storing) data in the queue we take help of rear pointer.
Let's first learn about supportive functions of a queue −
peek()
This function helps to see the data at the front of the queue. The algorithm of peek() function is as
follows −
Algorithm
begin procedure peek return queue[front]
end procedure
Implementation of peek() function in C programming language −
Example
int peek() {
return queue[front];
}
isfull()
As we are using single dimension array to implement queue, we just check for the rear pointer to
reach at MAXSIZE to determine that the queue is full. In case we maintain the queue in a circular
linked-list, the algorithm will differ. Algorithm of isfull() function −
Algorithm
end procedure
bool isfull() {
if(rear == MAXSIZE - 1)
return true;
else
return false;
}
isempty()
Algorithm of isempty() function −
Algorithm
return true
begin procedureelse
isempty
return false
if front is lessendif
than MIN OR front is greater than rear
end procedure
If the value of front is less than MIN or 0, it tells that the queue is not yet initialized, hence
empty. Here's the C programming code −
Example
bool isempty() {
if(front < 0 || front > rear)
return true;
else
return false;
}
Enqueue Operation
Queues maintain two data pointers, front and rear. Therefore, its operations are
comparatively difficult to implement than that of stacks.
The following steps should be taken to enqueue (insert) data into a queue −
Step 1 − Check if the queue is full.
Step 2 − If the queue is full, produce overflow error and exit.
Step 3 − If the queue is not full, increment rear pointer to point the next empty space.
Step 4 − Add data element to the queue location, where the rear is pointing.
Step 5 − return success.
Implementation of enqueue() in C programming language −
Dequeue Operation
Accessing data from the queue is a process of two tasks − access the data where front is pointing
and remove the data after access. The following steps are taken to perform dequeue operation −
Step 1 − Check if the queue is empty.
Step 2 − If the queue is empty, produce underflow error and exit.
Step 3 − If the queue is not empty, access the data where front is pointing.
Step 4 − Increment front pointer to point to the next available data element.
Step 5 − Return success.
We can easily represent queue by using linear arrays. There are two variables i.e. front and rear,
that are implemented in the case of every queue. Front and rear variables point to the position
from where insertions and deletions are performed in a queue. Initially, the value of front and
queue is -1 which represents an empty queue. Array representation of a queue containing 5
elements along with the respective values of front and rear, is shown in the following figure.
The above figure shows the queue of characters forming the English word "HELLO". Since, No
deletion is performed in the queue till now, therefore the value of front remains -1 . However, the
value of rear increases by one every time an insertion is performed in the queue. After inserting an
element into the queue shown in the above figure, the queue will look something like following.
The value of rear will become 5 while the value of front remains same.
After deleting an element, the value of front will increase from -1 to 0. however, the queue will
look something like following.
If the item is to be inserted as the first element in the list, in that case set the value of front and
rear to 0 and insert the element at the rear end.
Otherwise keep increasing the value of rear and insert each element one by one having rear as the
index.
Algorithm
C implementation:
1. void insert (int queue[], int max, int front, int rear, int item) 2. {
3. if (rear + 1 == max) 4. {
5. printf("overflow"); 6. }
7. else
8. {
9. if(front == -1 && rear == -1)
10. {
11. front = 0;
12. rear = 0;
13. }
14. else
15. {
16. rear = rear + 1;
17. }
18. queue[rear]=item;
19. }
20. }
Algorithm to delete an element from the queue(Dequeue):
If, the value of front is -1 or value of front is greater than rear , write an underflow message and
exit. Otherwise, keep increasing the value of front and return the item stored at the front end of the
queue at each time.
Algorithm
C implementation
int delete (int queue[], int max, int front, int rear)
1. {
2. int y;
3. if (front == -1 || front > rear)
5. {
6. printf("underflow"); 7. }
8. else
9. {
10. y = queue[front];
11. if(front == rear)
12. {
13. front = rear = -1;
14. else
15. front = front + 1; 16.
17. }
18. return y;
19. }
20. }
1. #include<stdio.h>
2. #include<stdlib.h>
3. #define maxsize 5
4. void insert();
5. void delete();
6. void display();
7. int front = -1, rear = -1;
8. int queue[maxsize];
9. void main ()
10. {
11. int choice;
12. while(choice != 4)
13. {
14. printf("\n*************************Main Menu*****************************\
n");
15. printf("\
n=================================================================\n");