Data Structures 2
Data Structures 2
Data: Collection of raw facts and figures. When arranged in a proper format, it gives information
Information : Processed data which conveys useful content is called information. It gives useful meaning and
helps us to make some decisions.
Algorithm : An algorithm is a finite set of instructions or logic, written in order, to accomplish a certain
predefined task. Algorithm is not the complete code or program, it is just the core logic(solution) of a
problem, which can be expressed either as an informal high level description as pseudocode or using
a flowchart.
Data and Information - Data Structure Types - Concept of Data Types - Abstract Data Types- Pointers -
Structures - Unions - Arrays - Multidimensional Arrays.
Data: Collection of raw facts and figures. When arranged in a proper format, it gives information
Information : Processed data which conveys useful content is called information. It gives useful meaning and
helps us to make some decisions.
Algorithm : An algorithm is a finite set of instructions or logic, written in order, to accomplish a certain
predefined task. Algorithm is not the complete code or program, it is just the core logic(solution) of a
problem, which can be expressed either as an informal high level description as pseudocode or using
a flowchart.
Properties of an Algorithm
1. Finiteness: - an algorithm terminates after a finite numbers of steps.
2. Definiteness: - each step in algorithm is unambiguous. This means that the action specified by the step
cannot be interpreted (explain the meaning of) in multiple ways & can be performed without any
confusion.
3. Input:- an algorithm accepts zero or more inputs
4. Output:- it produces at least one output.
5. Effectiveness:- it consists of basic instructions that are realizable. This means that the instructions can
be performed by using the given inputs in a finite amount of time.
An algorithm is said to be efficient and fast, if it takes less time to execute and consumes less memory space.
The performance of an algorithm is measured on the basis of following properties :
1. Time Complexity
2. Space Complexity
Space Complexity
It is the amount of memory space required by the algorithm, during the course of its execution. Space
complexity must be taken seriously for multi-user systems and in situations where limited memory is
available.
Time Complexity
Time complexity of an algorithm signifies the total time required by the program to run to completion. The
time complexity of algorithms is most commonly expressed using the big O notation.
Time Complexity is most commonly estimated by counting the number of elementary functions performed by
the algorithm. And since the algorithm's performance may vary with different types of input data, hence for an
algorithm we usually use the worst-case Time complexity of an algorithm because that is the maximum time
taken for any input size.
The most common metric for calculating time complexity is Big O notation. This removes all constant
factors so that the running time can be estimated in relation to N, as N approaches infinity. In general it is a
simple statement
statement;
Time Complexity will be Constant. The running time of the statement will not change in relation to N.
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
Page 2 of 31
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
for (i=o; i < N; i++)
{ statement; }
The time complexity for the above algorithm will be Linear. The running time of the loop is directly
proportional to N. When N doubles, so does the running time.
for (i=o; i < N; i++)
{ for (j=o; j < N; j++)
{ statement; }
}
This time, the time complexity for the above code will be Quadratic. The running time of the two loops is
proportional to the square of N. When N doubles, the running time increases by N * N.
While (low <= high)
{ mid=(low+high)/2);
if (target < list[mid])
high=mid-1;
else if (target > list[mid])
low=mid+1;
else break; }
This is an algorithm to break a set of numbers into halves, to search a particular field. This algorithm will have
a Logarithmic Time Complexity. The running time of the algorithm is proportional to the number of times N
can be divided by 2(N is high-low here). This is because the algorithm divides the working area in half with
each iteration.
This is the slgorithm for Quick Sort. In Quick Sort, we divide the list into halves every time, but we repeat the
iteration N times(where N is the size of list). Hence time complexity will be N*log( N ). The running time
consists of N loops (iterative or recursive) that are logarithmic, thus the algorithm is a combination of linear
and logarithmic.
NOTE : In general, doing something with every item in one dimension is linear, doing something with every
item in two dimensions is quadratic, and dividing the working area in half is logarithmic.
Algorithm Analysis :
Time complexity and Space complexity are used to analyze the algorithm in the data structure. There are
various ways of solving a problem and there exists different algorithms which can be designed to solve the
problem.
Consequently, analysis of algorithms focuses on the computation of space and time complexity. Here are
various types of time complexities which can be analyzed for the algorithm:
• Best case time complexity: The best case time complexity of an algorithm is a measure of the minimum
time that the algorithm will require for an input of size 'n.' The running time of many algorithms varies not
only for the inputs of different sizes but also for the different inputs of the same size.
• Worst case time Complexity: The worst case time complexity of an algorithm is a measure of the
minimum time that the algorithm will require for an input of size 'n.' Therefore, if various algorithms for
sorting are taken into account and say 'n,' input data items are supplied in reverse order for a sorting
algorithm, then the algorithm will require n2 operations to perform the sort which will correspond to the
worst case time complexity of the algorithm.
Asymptotic Notations
Following are the commonly used asymptotic notations to calculate the running time complexity of an
algorithm.
• Ο Notation
• Ω Notation
• θ Notation
Big Oh Notation, Ο
The notation Ο(n) is the formal way to express the upper bound of an algorithm's running time. It measures
the worst case time complexity or the longest amount of time an algorithm can possibly take to complete.
Omega Notation, Ω
The notation Ω(n) is the formal way to express the lower bound of an algorithm's running time. It measures
the best case time complexity or the best amount of time an algorithm can possibly take to complete.
Theta Notation, θ
The notation θ(n) is the formal way to express both the lower bound and the upper bound of an algorithm's
running time.
EXAMPLES :
Big O Notation: N
function(n) {
For(var a = 0; i <= n; i++) { // It's N because it's just a single loop
// Do stuff
}
}
Big O Notation: N2
function(n, b) {
For(var a = 0; a <= n; a++) {
For(var c = 0; i <= b; c++) { // It's N squared because it's two nested loops
// Do stuff
}
}
}
Big O Notation: 2N
function(n, b) {
For(var a = 0; a <= n; a++) {
// Do stuff
}
For(var c = 0; i <= b; c++) { // It's 2N the loops are outside each other
// Do stuff
}
}
Big O Notation: NLogN
for(int i = 0; i < n; i++) //this loop is executed n times, so O(n)
{
for(int j = n; j > 0; j/=2) //this loop is executed O(log n) times
}
}
EXAMPLE
for (int i = 1; i <=m; i += c) {
// some O(1) expressions
}
for (int i = 1; i <=n; i += c) {
// some O(1) expressions
}
Time complexity of above code is O(m) + O(n) which is O(m+n)
If m == n, the time complexity becomes O(2n) which is O(n).
Data Structure is a way of collecting and organizing data in such a way that we can perform operations on
these data in an effective way. Data Structures is about rendering data elements in terms of some relationship,
for better organization and storage.
Linear – Linear data structures, values are arranged in linear fashion. Arrays, linked lists, stacks and queues
are examples of linear data structures in which the values are stored in a sequence.
Non linear – In this the values are not arranged in order. Examples trees, graphs and sets.
Homogenous – Values of the same type of data are stored. Example : Arrays
Non Nomogenous / Heterogenous – Values of different types are grouped. Example : Structures, Classes
Static – The value of the static variable remains in the memory throughout the program. Value of static variable
persisits.
Dynamic – Size and memory locations can be changed during program executions. Example – Pointers.
Data Types in C :
C programming language which has the ability to divide the data into different types. The type of a variable
determine the what kind of values it may take on. The various data types are
• Simple Data type
→ Integer, Real, float, Char
• Structured Data type
→Array, Strings
• User Defined Data type
→Enum, Structures, Unions
Four standard data types. They are int, float, char and double.
Structures
A structure is a user defined data type that groups logically related data items of different data types into a
single unit. All the elements of a structure are stored at contiguous memory locations. A variable of structure
type can store multiple data items of different data types under the one name. As the data of employee in
company that is name, Employee ID, salary, address, phone number is stored in structure data type.
Defining a structure
The syntax of defining a structure is
struct <struct_name>
{
<data_type> <variable_name>;
<data_type> <variable_name>;
……..
<data_type> <variable_name>;
};
Example
The structure of Employee is declared as
struct employee
{
int emp_id;
char name[20];
float salary;
The structure members cannot be directly accessed in the expression. They are accessed by using the name of
structure variable followed by a dot and then the name of member variable. Example e1.emp_id, e1.name,
e1.salary, e1.age. The data within the structure is stored and printed by this method using scanf and printf
statement in c program
Structure Assignment
The value of one structure variable is assigned to another variable of same type using assignment statement.
If the e1 and e2 are structure variables of type employee then the statement
e1 = e2;
assign value of structure variable e2 to e1. The value of each member of e2 is assigned to corresponding
members of e1
Array of Structures
C language allows to create an array of variables of structure. The array of structure is used to store the large
number of similar records. For example to store the record of 100 employees then array of structure is used.
The method to define and access the array element of array of structure is similar to other array.
Example Struct employee e1[100];
Access them as e[i].emp_id, e[i].salary
Declaring Arrays
• When declaring arrays, specify
– Name
– Type of array
– Number of elements
arrayType arrayName[ numberOfElements ];
Examples:
int c[ 10 ];
float myArray[ 3284 ];
Initializers int n[ 5 ] = { 1, 2, 3, 4, 5 };
– If not enough initializers, rightmost elements become 0
int n[ 5 ] = { 0 } All elements 0
If too many a syntax error is produced syntax error
C arrays have no bounds checking
If size omitted, initializers determine it
int n[ ] = { 1, 2, 3, 4, 5 }; 5 initializers, therefore 5 element array
STACK
Stack is a LIFO (last in first out) structure. It is an ordered list of the same type of elements. A stack is a
linear list where all insertions and deletions are permitted only at one end of the list. When elements are
added to stack it grow at one end. Similarly, when elements are deleted from a stack, it shrinks at the same
end.
Applications of Stack
1. Expression Evaluation
Stack is used to evaluate prefix, postfix and infix expressions.
2. Expression Conversion
An expression can be represented in prefix, postfix or infix notation. Stack can be used to convert one
form of expression to another.
3. Syntax Parsing
Many compilers use a stack for parsing the syntax of expressions, program blocks etc. before
translating into low level code.
4. Backtracking
Suppose we are finding a path for solving maze problem. We choose a path and after following it we
realize that it is wrong. Now we need to go back to the beginning of the path to start with new path.
This can be done with the help of stack.
PROGRAM
#include<stdio.h>
#include<process.h>
#include<stdlib.h>
#define MAX 5 //Maximum number of elements that can be stored
int top=-1;
stack[MAX];
void push();
void pop();
void display();
void main()
{
int ch;
switch(ch)
{
case 1: push();
break;
case 2: pop();
break;
case 3: display();
break;
case 4: exit(0);
void push()
{
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
Page 9 of 31
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
int val;
if(top==MAX-1)
printf("\nStack is full!!");
else
{
printf("\nEnter element to push:");
scanf("%d",&val);
top=top+1;
stack[top]=val;
}
}
void pop()
{
if(top==-1)
printf("\nStack is empty!!");
else
{
printf("\nDeleted element is %d",stack[top]);
top=top-1;
}
}
void display()
{
int i;
if(top==-1)
printf("\nStack is empty!!");
else
{
printf("\nStack is...\n");
for(i=top;i>=0;--i)
printf("%d\n",stack[i]);
}
}
QUEUES
A queue is a useful data structure in programming. It is similar to the ticket queue outside a cinema hall,
where the first person entering the queue is the first person who gets the ticket.
Queue follows the First In First Out(FIFO) rule - the item that goes in first is the item that comes out first
Applications of Queues
1) When a resource is shared among multiple consumers. Examples include CPU scheduling, Disk
Scheduling.
2) When data is transferred asynchronously (data not necessarily received at same rate as sent) between
two processes. Examples include IO Buffers, pipes, file IO, etc.
3) data packets waiting to be transmitted over the Internet.
4) There are various queues quietly doing their job in your computer's (or the network's) operating system.
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
Page 10 of 31
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
5) There's a printer queue where print jobs wait for the printer to be available
Program
#include<stdio.h>
#include<conio.h>
#define n 5
void main()
{
int queue[n],ch=1,front=0,rear=0,i,j=1,x=n;
clrscr();
printf("Queue using Array");
printf("\n1.Insertion \n2.Deletion \n3.Display \n4.Exit");
while(ch)
{
printf("\nEnter the Choice:");
scanf("%d",&ch);
switch(ch)
{
case 1:
if(rear==x)
printf("\n Queue is Full");
else
{
printf("\n Enter no %d:",j++);
scanf("%d",&queue[rear++]);
}
break;
case 2:
if(front==rear)
{
printf("\n Queue is empty");
}
else
{
printf("\n Deleted Element is %d",queue[front++]);
x++;
}
break;
case 3:
printf("\n Queue Elements are:\n ");
if(front==rear)
printf("\n Queue is Empty");
else
{
for(i=front; i<rear; i++)
{
printf("%d",queue[i]);
printf("\n");
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
Page 11 of 31
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
}
break;
case 4:
exit(0);
default:
printf("Wrong Choice: please see the options");
}
}
}
getch();
}
A Stack Data Structure works on Last In First A Queue Data Structure works on First In
Out (LIFO) principle. First Out (FIFO) principle.
A Stack requires only one reference pointer. A Queue requires two reference pointers.
A Stack contains TOP as its reference for data A Queue contains REAR and FRONT as its
processing. reference for data processing.
The data items are inserted and deleted from The data items in a queue are inserted and
the same end. deleted from different ends.
The element to be inserted first is removed The element to be inserted first is removed
last. first.
Algorithm
1. Scan the infix expression from left to right.
2. If the scanned character is an operand, output it.
3. Else,
…..3.1 If the precedence of the scanned operator is greater than the precedence of the operator in the stack(or
the stack is empty or the stack contains a ‘(‘ ), push it.
…..3.2 Else, Pop all the operators from the stack which are greater than or equal to in precedence than that of
the scanned operator. After doing that Push the scanned operator to the stack. (If you encounter parenthesis
while popping then stop there and push the scanned operator in the stack.)
4. If the scanned character is an ‘(‘, push it to the stack.
5. If the scanned character is an ‘)’, pop the stack and and output it until a ‘(‘ is encountered, and discard both
the parenthesis.
6. Repeat steps 2-6 until infix expression is scanned.
7. Print the output
8. Pop and output from the stack until it is not empty.
Dynamic Memory Allocation
Allow the program to allocate some variables (notably arrays), during the program, based on variables in
program (dynamically)
char pop()
{
if(top == -1)
return -1;
else
return stack[top--];
}
main()
{
char exp[20];
char *e, x;
printf("Enter the expression :: ");
scanf("%s",exp);
e = exp;
while(*e != '\0')
{
if(isalnum(*e))
printf("%c",*e);
else if(*e == '(')
push(*e);
else if(*e == ')')
{
while((x = pop()) != '(')
printf("%c", x);
}
else
{
while(priority(stack[top]) >= priority(*e))
printf("%c",pop());
push(*e);
}
e++;
}
while(top != -1)
{
printf("%c",pop());
}
}
As Postfix expression is without parenthesis and can be evaluated as two operands and an operator at a time,
this becomes easier for the compiler and the computer to handle.
Algorithm
1) Add ) to postfix expression.
2) Read postfix expression Left to Right until ) encountered and repeat steps 3 and 4
3) If operand is encountered, push it onto Stack
4) If operator is encountered, Pop two elements
i) A ->Top element
ii) B->Next to Top element
iii) Evaluate B operator A and Push the result onto Stack
5) Set result=pop
6) END
EXAMPLE :
Expression: 456*+
Program
#include<stdio.h>
#include<ctype.h>
# define MAXSTACK 100 /* for max size of stack */
# define POSTFIXSIZE 100 /* define max number of charcters in postfix expression */
/* declare stack and its top pointer to be used during postfix expression
evaluation*/
int stack[MAXSTACK];
int top = -1 ; /* because array index in C begins at 0 */
/* can be do this initialization somewhere else */
int i ;
char ch;
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
Page 16 of 31
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
int val;
int A, B ;
case '/':
val = B / A;
break;
case '+':
val = B + A;
break;
case '-':
val = B - A;
break;
}
void main()
{ int i ;
/* declare character array to store postfix expression */
Pointers
A pointer is a reference to another variable (memory location) in a program
– Used to change variables inside a function (reference parameters)
– Used to remember a particular member of a group (such as an array)
– Used in dynamic (on-the-fly) memory allocation (especially of arrays)
– Used in building complex data structures (linked lists, stacks, queues, trees, etc.)
– Variables are allocated at addresses in computer memory (address depends on computer/operating
system)
– Name of the variable is a reference to that memory address
– A pointer variable contains a representation of an address of another variable (P is a pointer variable in
the following
Uses of Pointers
• Working with memory locations that regular variables don’t give you access to
• Working with strings and arrays
• Creating new variables in memory while the program is running
• Creating arbitrarily-sized lists of values in memory
int* pc;
int c;
c=22;
printf("Address of c:%u\n",&c);
printf("Value of c:%d\n\n",c);
pc=&c;
printf("Address of pointer pc:%u\n",pc);
printf("Content of pointer pc:%d\n\n",*pc);
c=11;
printf("Address of pointer pc:%u\n",pc);
printf("Content of pointer pc:%d\n\n",*pc);
*pc=2;
printf("Address of c:%u\n",&c);
printf("Value of c:%d\n\n",c);
Output
Address of c: 2686784
Value of c: 22
Address of c: 2686784
Value of c: 2
*pc = &c; // Wrong! *pc is the value pointed by address whereas, &c is an address
*pc = c; // Correct! *pc is the value pointed by address and, c is also a value.
void swap(int * n1, int * n2) // pointer n1 and n2 points to the address of num1 and num2 respectively
{ int temp;
temp = *n1;
*n1 = *n2;
*n2 = temp;
}
void main()
{ int num1 = 5, num2 = 10;
swap( &num1, &num2); // address of num1 and num2 is passed to the swap function
printf("Number1 = %d\n", num1);
printf("Number2 = %d", num2);
}
Output
Number1 = 10
Number2 = 5
void main()
{ int num1 = 5, num2 = 10;
swap( num1, num2); // vlues of num1 and num2 is passed to the swap function
printf("Number1 = %d\n", num1);
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
Page 20 of 31
DATA STRUCTURES NOTES FOR II SEM B.Tech – ‘B’ SECTION
printf("Number2 = %d", num2);
}
Output
Number1 = 5
Number2 = 10
malloc() Allocates requested size of bytes and returns a pointer first byte of allocated space
calloc() Allocates space for an array elements, initializes to zero and then returns a pointer to memory
malloc()
The name malloc stands for "memory allocation".
The function malloc() reserves a block of memory of specified size and return a pointer of type void which
can be casted into pointer of any form.
Syntax of malloc()
Here, ptr is pointer of cast-type. The malloc() function returns a pointer to an area of memory with size of
byte size. If the space is insufficient, allocation fails and returns NULL pointer.
This statement will allocate either 200 or 400 according to size of int 2 or 4 bytes respectively and the pointer
points to the address of first byte of memory.
free()
Dynamically allocated memory created with either calloc() or malloc() doesn't get freed on its own. You
must explicitly use free() to release the space.
syntax of free()
free(ptr);
This statement frees the space allocated in the memory pointed by ptr.
Arrays can be used to store linear data of similar types, but arrays have following limitations.
1) The size of the arrays is fixed: So we must know the upper limit on the number of elements in advance.
Also, generally, the allocated memory is equal to the upper limit irrespective of the usage.
2) Inserting a new element in an array of elements is expensive, because room has to be created for the new
elements and to create room existing elements have to shifted.
1) Dynamic size
2) Ease of insertion/deletion
Drawbacks:
1) Random access is not allowed. We have to access elements sequentially starting from the first node. So we
cannot do binary search with linked lists.
2) Extra memory space for a pointer is required with each element of the list.
Representation in C:
A linked list is represented by a pointer to the first node of the linked list. The first node is called head. If the
linked list is empty, then value of head is NULL.
Each node in a list consists of at least two parts:
1) data
2) pointer to the next node
A linked list is a data structure that consists of sequence of nodes. Each node is composed of two fields: data
field and reference field which is a pointer that points to the next node in the sequence.
snode * getnode()
{ snode * newnode;
newnode = (struct snode *) malloc(sizeof(struct snode))
newnode->link=null;
return newnode;
}
Code
void insert_beg(snode* list)
{ int x;
snode* newnode;
newnode=getnode();
printf(“Enter the data to be inserted”);
scanf(“%d”,x);
newnode->data=x;
if (list==null)
list=newnode;
else
{ newnode->link=list;
list=newnode; }
}
Code
void insert_end(snode* list)
{ int x;
Code
void insert_after(snode* list)
{ int x,y;
snode* newnode, * trav;
if (list==null)
printf(“ERROR – List Empty)
else
{ printf(“Enter value of node after which insertion to take place”);
Scanf(“%d”, &y);
trav=list;
while (trav != null || trav->data !=y)
trav=trav->link; // jump till last node or till node with value y found
if (trav==null) // no node with value y
printf(“no node with value y after which insertion to be done”)
else // node with value y found and pointed by trav
{ newnode=getnode();
printf(“Enter the data to be inserted”);
scanf(“%d”,x);
newnode->link=trav->link; //attach the new node after trav, the node with value y
trav->link=newnode;
}
}
} // function ends
Code
void delete-middle(snode* list)
{ int y;
snode* temp;
if (list==null)
printf(“ERROR – Empty list, Deletion not done”);
if (list!==null)
{
printf(“enter the node value to be deleted”);
scanf(“%d”,&y)
if (list->data==y)
{ temp=trav;
list=list->link; // happened to be the first node
free(temp); // return the deleted node to memory
}
else // list has many node, search using trav, but keep track of prev node
{ while (trav != null && trav->data !=y)
{ prev=trav; // prev keeps track of previous node
trav=trav->link; // jump till last node or till node with value y found
}
if (trav==null)
printf(“ERROR – no such node with value given”);
else
{
temp=trav // node to be deleted with value
prev->link=trav->link //disconnect the node
free(temp); // return the deleted node to memory
}
}
} // function ends
Code
void traverse(snode* list)
{
if (list==null)
snode * getnode()
{ snode * newnode;
newnode = (struct snode *) malloc(sizeof(struct snode))
newnode->flink=null;
newnode->blink=null;
return newnode;
}
Code
void insert_beg(dnode* list)
{ int x;
dnode* newnode;
newnode=getnode();
printf(“Enter the data to be inserted”);
scanf(“%d”,x);
newnode->data=x;
if (list==null)
list=newnode;
Code
void insert_end(dnode* list)
{ int x;
dnode* newnode, * trav;
newnode=getnode();
printf(“Enter the data to be inserted”);
scanf(“%d”,x);
newnode->data=x;
if (list==null)
list=newnode;
else
{ trav=list;
while (trav->flink != null)
trav=trav->flink; // jump till last node
trav->flink=newnode; // attach new node to last node
newnode->blink=trav //attach newnode backward link to point to trav
}
}
Code
void insert_after(dnode* list)
{ int x,y;
dnode* newnode, * trav;
Code
void delete-middle(dnode* list)
{ int y;
dnode* temp;
if (list==null)
printf(“ERROR – Empty list, Deletion not done”);
if (list!==null)
{
printf(“enter the node value to be deleted”);
scanf(“%d”,&y)
if (list->data==y)
{ temp=trav;
list=list->flink; // happened to be the first node
list->blink=null;
free(temp); // return the deleted node to memory
}
else // list has many node, search using trav, but keep track of prev node
{ while (trav!= null && trav->data !=y)
trav=trav->flink; // jump till last node or till node with value y found
if (trav==null)
Code
void traverse(dnode* list)
{
if (list==null)
printf(“EMPTY LIST);
else
{ trav=list;
printf(“The contents of the List :”)
while (trav != null) // traverse till end of list, printing each node value
{ printf(“node value %d “, trav->data);
trav=trav->flink; // jump to next node
}
}
}