Sunteți pe pagina 1din 67

DATA STRUCTURES

INTRODUCTION:
A data structure in Computer Science is a way of storing and organizing data in a computer‟s
memory or even disk storage so that it can be used efficiently. It is an organization of
mathematical and logical concepts of data. A well-designed data structure allows a variety of
critical operations to be performed, using as few resources, both execution time and memory
space, as possible. Data structures are implemented by a programming language by the data
types and the references and operations provide by that particular language.
DATA AND INFORMATION:
The term data comes from its singular form datum, which means a fact. The data is a fact about
people, places or some entities. In computers, data is simply the value assigned to a variable.
The term variable refers to the name of a memory location that can contain only one data at any
point of time. For example, consider the following statements:
Vijay is 16 years old.
Vijay is in the 12th standard.
Vijay got 80% marks in Mathematics.
Let „name‟, „age‟, „class‟, „marks‟ and „subject‟ be some defined variables. Now, let us assign a
value to each of these variables from the above statements.
Name = Vijay
Class = 12
Age = 16
Marks = 80
Subject = Mathematics
In the above example the values assigned to the five different variables are called data. We will
now see what is information with respect to computers. Information is defined as a set of
processed data that convey the relationship between data considered. Processing means to do
some operations or computations on the data of different variables to relate them so that these
data, when related, convey some meaning. Thus, information is as group of related data
conveying some meaning. In the examples above, when the data of the variables „name‟ and
„age‟ are related, we get the following information:
Vijay is 16 years old.
In the same example, when the data of the variables „name‟ and
„class‟ are related we get another information as
Vijay is in the 12th standard.
Further, when we relate the data of the variables, „name‟, „marks‟, and „subject‟, we get more
information that Vijay got 80% marks in Mathematics. The following figure shows how
information can be drawn from data.
Fig 1.1 : Relation between data and information

Basic types of Data Structures


As we discussed above, anything that can store data can be called as a data strucure, hence
Integer, Float, Boolean, Char etc, all are data structures. They are known as Primitive Data
Structures.
Then we also have some complex Data Structures, which are used to store large and connected
data. Some example of Abstract Data Structure are :

 Linked List
 Tree
 Graph
 Stack, Queue etc.
All these data structures allow us to perform different operations on data. We select these data
structures based on which type of operation is required. We will look into these data structures in
more details in our later lessons.

Basic Operations
The data in the data structures are processed by certain operations. The particular data
structure chosen largely depends on the frequency of the operation that needs to be performed
on the data structure.
 Traversing
 Searching
 Insertion
 Deletion
 Sorting

What is 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.
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
Its 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.
An algorithm generally requires space for following components :

 Instruction Space : Its the space required to store the executable version of the program.
This space is fixed, but varies depending upon the number of lines of code in the program.
 Data Space : Its the space required to store all the constants and variables value.
 Environment Space : Its the space required to store the environment information needed to
resume the suspended function.

Time Complexity
Time Complexity is a way to represent the amount of time needed by the program to run to
completion.
Queue
Queue is work on the principal of First-In-First-Out (FIFO), it means first entered item
remove first. Queue have two end front and rear, from front you can insert element and from
rear you can delete element.

Basic Operations
Queue operations may involve initializing or defining the queue, utilizing it and then completing
erasing it from memory. Here we shall try to understand 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 above mentioned queue operation efficient. These are

 peek() − get the element at front of the queue without removing it.
 isfull() − checks if queue is full.
 isempty() − checks if queue is empty.
In queue, we always dequeue (or access) data, pointed by front pointer and while enqueing
(or storing) data in queue we take help of rear pointer.
Let's first learn about supportive functions of a queue −

peek()
Like stacks, this function helps to see the data at the front of the queue. Algorithm of peek()
function −
begin procedure peek
return queue[front]
end procedure
Implementation of peek() function in C programming language −
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 queue is full. In case we maintain queue in a circular
linked-list, the algorithm will differ. Algorithm of isfull() function −
begin procedure isfull
if rear equals to MAXSIZE
return true
else
return false
endif
end procedure
Implementation of isfull() function in C programming language −
bool isfull() {
if(rear == MAXSIZE - 1)
return true;
else
return false;
}

isempty()
Algorithm of isempty() function −
begin procedure isempty
if front is less than MIN OR front is greater than rear
return true
else
return false
endif
end procedure
If value of front is less than MIN or 0, it tells that queue is not yet initialized, hence empty.
Here's the C programming code −
bool isempty() {
if(front < 0 || front > rear)
return true;
else
return false;
}

Enqueue Operation
As queue maintains two data pointers, front and rear, its operations are comparatively more
difficult to implement than stack.
The following steps should be taken to enqueue (insert) data into a queue −
 Step 1 − Check if queue is full.
 Step 2 − If queue is full, produce overflow error and exit.
 Step 3 − If queue is not full, increment rear pointer to point next empty space.
 Step 4 − Add data element to the queue location, where rear is pointing.
 Step 5 − return success.

Sometimes, we also check that if queue is initialized or not to handle any unforeseen situations.

Algorithm for enqueue operation


procedure enqueue(data)
if queue is full
return overflow
endif
rear ← rear + 1
queue[rear] ← data
return true
end procedure
Implementation of enqueue() in C programming language −
int enqueue(int data)
if(isfull())
return 0;
rear = rear + 1;
queue[rear] = data;
return 1;
end procedure

Dequeue Operation
Accessing data from queue is a process of two tasks − access the data wherefront is pointing
and remove the data after access. The following steps are taken to perform dequeue operation

 Step 1 − Check if queue is empty.
 Step 2 − If queue is empty, produce underflow error and exit.
 Step 3 − If queue is not empty, access data where front is pointing.
 Step 3 − Increment front pointer to point next available data element.
 Step 5 − return success.

Algorithm for dequeue operation −


procedure dequeue
if queue is empty
return underflow
end if
data = queue[front]
front ← front - 1
return true
end procedure
Implementation of dequeue() in C programming language −
int dequeue() {
if(isempty())
return 0;
int data = queue[front];
front = front + 1;
return data;
}
Example:
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
void insert();
void del();
void display();
struct circ
{
int cqueue[5];
};
struct circ cque;
int rear=0,front=-1;
void main()
{
while(1)
{
int num;
clrscr();
printf("1.Insertion\n2.Deletion\n3.Display\n0.Exit\n");
printf("\n\nSelect Option : ");
scanf("%d",&num);
switch(num)
{
case 1:
insert();
break;
case 2:
del();
break;
case 3:
display();
break;
case 0:
exit(0);
break;
default:
printf("\n\n Invalid Option ");
}
getch();
}
}
void insert()
{
int item;
printf("Element : ");
scanf("%d",&item);
if(front==(rear+1)%3)
{
printf("Queue is Full");
return;
}
if(front==-1)
{
rear=front=0;
}
else
{
rear=(rear+1)%3;
}
cque.cqueue[rear]=item;
printf("Successfully Insert");
}
void del()
{
int num;
if(front==-1)
{
printf("Queue Empty");
return;
}
else
{
num=cque.cqueue[front];
printf("Deleted item : %d",num);
}
if(front==rear)
{
front=-1;
}
else
front=(front+1)%3;
}
void display()
{
int i;
if(front==-1)
{
printf("Queue Empty");
return;
}
else
{
printf("\n\nItems : ");
for(i=front;i<=rear;i++)
{
printf(" %d",cque.cqueue[i]);
}
}
}

Data Structure - Stack

Stack
Stack is linear data structure. In stack addition of new data item and deletion of already
existing data item is done from only one end, known as top. Working of stack on the basis
of Last-in-First-out (LIFO) principal, it means last entered item remove first.

Real life example of stack


A most popular example of stack is plates in marriage party. Fresh plates are pushed onto to
the top and popped from the top.
Basic Operations
Stack operations may involve initializing the stack, using it and then de-initializing it. Apart from
these basic stuffs, a stack is used for the following two primary operations −
 push() − pushing (storing) an element on the stack.
 pop() − removing (accessing) an element from the stack.
When data is PUSHed onto stack?
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.
At all times, we maintain a pointer to the last PUSHed data on the stack. As this pointer always
represents the top of the stack, hence named top. The toppointer provides top value of the
stack without actually removing it.
First we should learn about procedures to support stack functions −

peek()
Algorithm of peek() function −
begin procedure peek
return stack[top]
end procedure
Implementation of peek() function in C programming language −
int peek() {
return stack[top];
}

isfull()
Algorithm of isfull() function −
begin procedure isfull
if top equals to MAXSIZE
return true
else
return false
endif
end procedure
Implementation of isfull() function in C programming language −
bool isfull() {
if(top == MAXSIZE)
return true;
else
return false;
}

isempty()
Algorithm of isempty() function −
begin procedure isempty
if top less than 1
return true
else
return false
endif
end procedure
Implementation of isempty() function in C programming language is slightly different. We
initialize top at -1, as index in array starts from 0. So we check if top is below zero or -1 to
determine if stack is empty. Here's the code −
bool isempty() {
if(top == -1)
return true;
else
return false;
}

PUSH Operation
The process of putting a new data element onto stack is known as PUSHOperation. Push
operation involves series of steps −
 Step 1 − Check if stack is full.
 Step 2 − If stack is full, produce error and exit.
 Step 3 − If stack is not full, increment top to point next empty space.
 Step 4 − Add data element to the stack location, where top is pointing.
 Step 5 − return success.

if linked-list is used to implement stack, then in step 3, we need to allocate space dynamically.

Algorithm for PUSH operation


A simple algorithm for Push operation can be derived as follows −
begin procedure push: stack, data
if stack is full
return null
endif
top ← top + 1
stack[top] ← data
end procedure
Implementation of this algorithm in C, is very easy. See the below code −
void push(int data) {
if(!isFull()) {
top = top + 1;
stack[top] = data;
}else {
printf("Could not insert data, Stack is full.\n");
}
}

Pop Operation
Accessing the content while removing it from stack, is known as pop operation. In array
implementation of pop() operation, data element is not actually removed, instead top is
decremented to a lower position in stack to point to next value. But in linked-list
implementation, pop() actually removes data element and deallocates memory space.
A POP operation may involve the following steps −
 Step 1 − Check if stack is empty.
 Step 2 − If stack is empty, produce error and exit.
 Step 3 − If stack is not empty, access the data element at which top is pointing.
 Step 4 − Decrease the value of top by 1.
 Step 5 − return success.

Algorithm for POP operation


A simple algorithm for Pop operation can be derived as follows −
begin procedure pop: stack
if stack is empty
return null
endif
data ← stack[top]
top ← top - 1
return data
end procedure
Implementation of this algorithm in C, is shown below −
int pop(int data) {
if(!isempty()) {
data = stack[top];
top = top - 1;
return data;
}else {
printf("Could not retrieve data, Stack is empty.\n");
}
}
Example:
#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#define size 5
void pop();
void push();
void display();
struct stack
{
int item;
int stack[size];
}s;
int top=-1;
void push()
{
if(top==size-1)
{
printf("\n stack is full");
}
else
{
top=top+1;
printf("\n\n Enter element in stack: ");
scanf("%d",&s.item);
s.stack[top]=s.item;
}
}
void pop()
{
if(top==0)
{
printf("\nStack is empty: ");
}
else
{
s.item=s.stack[top];
top=top-1;
printf("deleted data is: %d",s.item);
}
}
void display()
{
int i;
if(top==0)
{
printf("\n Stack is empty: ");
}
else
{
for(i=top;i>0;i--)
{
printf("\n%d",s.stack[i]);
}
}
}
void main()
{
char ch,c;
do
{
u:
clrscr();
printf("\n\n1: push");
printf("\n2: pop");
printf("\n3: display");
printf("\n4: exit");
printf("\nenter your choice: ");
scanf("%d",&ch);
switch(ch)
{
case 1:
up:
push();
break;
case 2:
pop();
break;
case 3:
display();
break;
case 4:
exit(0);
break;
default:
printf("\nwrong choice");
}
printf("\n\n Pushed an element (Y/N)");
c=getch();
if(c=='y'||c=='Y')
{
goto up;
}
else
{
goto u;
}
}
while(ch!=5);
}
Linked List
A linked-list is a sequence of data structures which are connected together via links.
Linked List is a sequence of links which contains items. Each link contains a connection to
another link. Linked list the second most used data structure after array. Following are
important terms to understand the concepts of Linked List.
 Link − Each Link of a linked list can store a data called an element.
 Next − Each Link of a linked list contain a link to next link called Next.
 LinkedList − A LinkedList contains the connection link to the first Link called First.
Advantages of linked list
 Dynamic Data Structure: The size of linked list increase and decrease during program
execution.
 No memory wastage: In linked list memory will be allocated at the time of program
execution so no memory wastage.
 Easily insert and delete data: In linked list you can insert any data at specific position
and also delete any data from specific position.
Dis-Advantages of linked list
 Need more memory: For store data in linked list you need more memory space, you need
memory space for both data and address part.

Linked List Representation


Linked list can be visualized as a chain of nodes, where every node points to the next node.

As per above shown illustration, following are the important points to be considered.
 LinkedList contains an link element called first.
 Each Link carries a data field(s) and a Link Field called next.
 Each Link is linked with its next link using its next link.
 Last Link carries a Link as null to mark the end of the list.

Types of Linked List


Following are the various flavours of linked list.
 Simple Linked List − Item Navigation is forward only.
 Doubly Linked List − Items can be navigated forward and backward way.
 Circular Linked List − Last item contains link of the first element as next and and first
element has link to last element as prev.

Basic Operations
Following are the basic operations supported by a list.
 Insertion − add an element at the beginning of the list.
 Deletion − delete an element at the beginning of the list.
 Display − displaying complete list.
 Search − search an element using given key.
 Delete − delete an element using given key.

Insertion Operation
Adding a new node in linked list is a more than one step activity. We Shall learn this with
diagrams here. First, create a node using the same structure and find the location where it has
to be inserted.

Imagine that we are inserting a node B (NewNode), between A (LeftNode) and C (RightNode).
Then point B.next to C

NewNode.next −> RightNode;

It should look like this −

Now the next of the node at left should point to the new node.

LeftNode.next −> NewNode;


This will put the new node in the middle of the two. The new list should look like this −

Similar steps should be taken if the node being inserted at the beginning of the list. While
putting it at the end, then the second last node of list should point to new node and the new
node will point to NULL.

Deletion Operation
Deletion is also a more than one step process. We shall learn with pictorial representation. First,
locate the target node to be removed, by using searching algorithms.

The left (previous) node of the target node now should point to the next node of the target node

LeftNode.next −> TargetNode.next;

This will remove the link that was pointing to target node. Now we shall remove to what target
node is pointing.
TargetNode.next −> NULL;

We need to use the deleted node we can keep that in memory otherwise we can simply
deallocate memory and wipe off the target node completely.

Reverse Operation
This operation is a thorough one. We need to make the last node be pointed by the head node
and reverse the whole linked list.

First, we traverse to the end of the list. It should be pointing to NULL. Now we shall make it to
point to its previous node −

We have to make sure that last node is not the lost node, so we'll have some temp node, which
looks like the head node pointing to the last node. Now we shall make our all our left side nodes
to point to their previous nodes one by one.
Except the node (first node) pointed by the head node, should point to their predecessor and
making them their new successor. The first node will point to NULL.

We'll make the head node to point the new first node by using temp node.

Data Structure - Doubly Linked List


Doubly Linked List is a variation of Linked list in which navigation is possible in both ways either
forward and backward easily as compared to Single Linked List. Following are important terms to
understand the concepts of doubly Linked List
 Link − Each Link of a linked list can store a data called an element.
 Next − Each Link of a linked list contain a link to next link called Next.
 Prev − Each Link of a linked list contain a link to previous link called Prev.
 LinkedList − A LinkedList contains the connection link to the first Link called First and to
the last link called Last.

Doubly Linked List Representation

As per above shown illustration, following are the important points to be considered.

 Doubly LinkedList contains an link element called first and last.


 Each Link carries a data field(s) and a Link Field called next.
 Each Link is linked with its next link using its next link.
 Each Link is linked with its previous link using its prev link.
 Last Link carries a Link as null to mark the end of the list.
Basic Operations
Following are the basic operations supported by an list.
 Insertion − add an element at the beginning of the list.
 Deletion − delete an element at the beginning of the list.
 Insert Last − add an element in the end of the list.
 Delete Last − delete an element from the end of the list.
 Insert After − add an element after an item of the list.
 Delete − delete an element from the list using key.
 Display forward − displaying complete list in forward manner.
 Display backward − displaying complete list in backward manner.

Insertion Operation
Following code demonstrate insertion operation at beginning in a doubly linked list.
//insert link at the first location
void insertFirst(int key, int data) {
//create a link
struct node *link = (struct node*) malloc(sizeof(struct node));
link->key = key;
link->data = data;
if(isEmpty()) {
//make it the last link
last = link;
}else {
//update first prev link
head->prev = link;
}
//point it to old first link
link->next = head;
//point first to new first link
head = link;
}

Deletion Operation
Following code demonstrate deletion operation at beginning in a doubly linked list.
//delete first item
struct node* deleteFirst() {
//save reference to first link
struct node *tempLink = head;
//if only one link
if(head->next == NULL) {
last = NULL;
}else {
head->next->prev = NULL;
}
head = head->next;
//return the deleted link
return tempLink;
}

Insertion at End Operation


Following code demonstrate insertion operation at last position in a doubly linked list.
//insert link at the last location
void insertLast(int key, int data) {
//create a link
struct node *link = (struct node*) malloc(sizeof(struct node));
link->key = key;
link->data = data;
if(isEmpty()) {
//make it the last link
last = link;
}else {
//make link a new last link
last->next = link;
//mark old last node as prev of new link
link->prev = last;
}
//point last to new last node
last = link;
}

Data Structure - Circular Linked List


Circular Linked List is a variation of Linked list in which first element points to last element and
last element points to first element. Both Singly Linked List and Doubly Linked List can be made
into as circular linked list.
Singly Linked List as Circular
In singly linked list, the next pointer of the last node points to the first node.

Doubly Linked List as Circular


In doubly linked list, the next pointer of the last node points to the first node and the previous
pointer of the first node points to the last node making the circular in both directions.

As per above shown illustrations, following are the important points to be considered.
 Last Link's next points to first link of the list in both cases of singly as well as doubly
linked list.
 First Link's prev points to the last of the list in case of doubly linked list.

Basic Operations
Following are the important operations supported by a circular list.
 insert − insert an element in the start of the list.
 delete − insert an element from the start of the list.
 display − display the list.

Insertion Operation
Following code demonstrate insertion operation at in a circular linked list based on single linked
list.
//insert link at the first location
void insertFirst(int key, int data) {
//create a link
struct node *link = (struct node*) malloc(sizeof(struct node));
link->key = key;
link->data= data;

if (isEmpty()) {
head = link;
head->next = head;
}else {
//point it to old first node
link->next = head;

//point first to new first node


head = link;
}

Deletion Operation
Following code demonstrate deletion operation at in a circular linked list based on single linked
list.
//delete first item
struct node * deleteFirst() {
//save reference to first link
struct node *tempLink = head;

if(head->next == head){
head = NULL;
return tempLink;
}

//mark next to first link as first


head = head->next;

//return the deleted link


return tempLink;
}

Display List Operation


Following code demonstrate display list operation in a circular linked list.
//display the list
void printList() {
struct node *ptr = head;
printf("\n[ ");
//start from the beginning
if(head != NULL) {
while(ptr->next != ptr) {
printf("(%d,%d) ",ptr->key,ptr->data);
ptr = ptr->next;
}
}
printf(" ]");
}
Example:
//Program by :
#include<stdio.h>
#include<conio.h>
#include<alloc.h>
//-------------------------------------------------
struct node
{
int data;
struct node *next;
}*start=NULL;
//------------------------------------------------------------

void creat()
{
char ch;
do
{
struct node *new_node,*current;
new_node=(struct node *)malloc(sizeof(struct node));
printf("\nEnter the data : ");
scanf("%d",&new_node->data);
new_node->next=NULL;
if(start==NULL)
{
start=new_node;
current=new_node;
}
else
{
current->next=new_node;
current=new_node;
}
printf("\nDo you want to creat another : ");
ch=getche();
}while(ch!='n');
}
//------------------------------------------------------------------
void display()
{
struct node *new_node;
printf("\nThe Linked List : n");
new_node=start;
while(new_node!=NULL)
{
printf("%d--->",new_node->data);
new_node=new_node->next;
}
printf("NULL");
}
//----------------------------------------------------
void main()
{
clrscr();
creat();
display();
}
//----------------------------------------------------
Introduction to Sorting
Sorting is nothing but storage of data in sorted order, it can be in ascending or descending order.
The term Sorting comes into picture with the term Searching. There are so many things in our
real life that we need to search, like a particular record in database, roll numbers in merit list, a
particular telephone number, any particular page in a book etc.
Sorting arranges data in a sequence which makes searching easier. Every record which is going
to be sorted will contain one key. Based on the key the record will be sorted. For example,
suppose we have a record of students, every such record will have the following data:

 Roll No.
 Name
 Age
 Class
Here Student roll no. can be taken as key for sorting the records in ascending or descending
order. Now suppose we have to search a Student with roll no. 15, we don't need to search the
complete record we will simply search between the Students with roll no. 10 to 20.

Sorting Efficiency
There are many techniques for sorting. Implementation of particular sorting technique depends
upon situation. Sorting techniques mainly depends on two parameters. First parameter is the
execution time of program, which means time taken for execution of program. Second is the
space, which means space taken by the program.

Types of Sorting Techniques


There are many types of Sorting techniques, differentiated by their efficiency and space
requirements. Following are some sorting techniques which we will be covering in next sections.

1. Bubble Sort
2. Insertion Sort
3. Selection Sort
4. Quick Sort
5. Merge Sort
6. Heap Sort

Bubble Sort Algorithm


Bubble sort is the simplest sorting algorithm. In this technique we follow given step to short
given elements in increasing order.
Steps to Sort data

 First compare First (previous) element with its next elements.

 If next element is grater than previous element just ignore it.

 If next element is smaller than previous element then interchange their positio
Example:

How bubble sort works?


We take an unsorted array for our example. Bubble sort take Ο(n 2) time so we're keeping short
and precise.

Bubble sort starts with very first two elements, comparing them to check which one is greater.

In this case, value 33 is greater than 14, so it is already in sorted locations. Next, we compare
33 with 27.

We find that 27 is smaller than 33 and these two values must be swapped.

The new array should look like this −

Next we compare 33 and 35. We find that both are in already sorted positions.

Then we move to next two values, 35 and 10.

We know than 10 is smaller 35. Hence they are not sorted.


We swap these values. We find that we reach at the end of the array. After one iteration the
array should look like this −

To be precise, we are now showing that how array should look like after each iteration. After
second iteration, it should look like this −

Notice that after each iteration, at least one value moves at the end.

And when there's no swap required, bubble sorts learns that array is completely sorted.

Complexity Analysis of Bubble Sorting


In Bubble Sort, n-1 comparisons will be done in 1st pass, n-2 in 2nd pass, n-3 in 3rd pass and so
on. So the total number of comparisons will be
(n-1)+(n-2)+(n-3)+.....+3+2+1
Sum = n(n-1)/2
i.e O(n2)
Hence the complexity of Bubble Sort is O(n2).
The main advantage of Bubble Sort is the simplicity of the algorithm.Space complexity for Bubble
Sort is O(1), because only single additional memory space is required for temp variable
Best-case Time Complexity will be O(n), it is when the list is already sorted.
Example:
#include<stdio.h>
#include<conio.h>
#include<dos.h>
void main()
{
int i,a[100],temp,j,no;
clrscr();
printf("How many no. do u want to insert: ");
scanf("%d",&no);
printf("\nEnter any %d num in array \n",no);

for(i=0;i<no;i++)
{
scanf("%d",&a[i]);
}
printf("\n\n\nData before sorting : ");
for(j=0;j<no;j++)
{
delay(200);
printf(" %d",a[j]);
}
for(i=0;i<no;i++)
{
for(j=0;j<no-i-1;j++)
{
if(a[j]>a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
printf("\n\n\nData after sorting : ");
for(j=0;j<no;j++)
{
delay(200);
printf(" %d",a[j]);
}
getch();}
Selection Sort Program in C
Selection sort is based of maximum and minimum value. First check minimum value in array
list and place it at first position (position 0) of array, next find second smallest element in
array list and place this value at second position (position 1) and so on. Same process is
repeated until sort all element of an array.

Algorithm

Step 1 − Set MIN to location 0


Step 2 − Search the minimum element in the list
Step 3 − Swap with value at location MIN
Step 4 − Increment MIN to point to next element
Step 5 − Repeat until list is sorted

How selection sort works?


We take the below depicted array for our example.

For the first position in the sorted list, the whole list is scanned sequentially. The first position
where 14 is stored presently, we search the whole list and find that 10 is the lowest value.

So we replace 14 with 10. After one iteration 10, which happens to be the minimum value in the
list, appears in the first position of sorted list.

For the second position, where 33 is residing, we start scanning the rest of the list in linear
manner.

We find that 14 is the second lowest value in the list and it should appear at the second place.
We swap these values.
After two iterations, two least values are positioned at the the beginning in the sorted manner.

The same process is applied on the rest of the items in the array. We shall see an pictorial
depiction of entire sorting process −

Complexity Analysis of Selection Sorting


Worst Case Time Complexity : O(n2)
Best Case Time Complexity : O(n2)
Average Time Complexity : O(n2)
Space Complexity : O(1)
Example:
#include<stdio.h>
#include<conio.h>
void main()
{
int array[100], n, c, d, position, swap;
clrscr();
printf("How many elements you want to enter: ");
scanf("%d", &n);
printf("Enter any %d elements: \n", n);
for (c=0; c<n; c++)
{
scanf("%d", &array[c]);
}
for (c=0; c<(n-1); c++)
{
position=c;
for (d=c+1; d<n; d++)
{
if (array[position]>array[d])
{
position=d;
}
}
if (position!=c)
{
swap=array[c];
array[c]=array[position];
array[position]=swap;
}
}
printf("Sorted list in ascending order:\n");
for (c=0; c<n; c++)
{
printf("%d ", array[c]);
}
getch();
}
Insertion Sorting
It is a simple Sorting algorithm which sorts the array by shifting elements one by one. Following
are some of the important characteristics of Insertion Sort.

1. It has one of the simplest implementation


2. It is efficient for smaller data sets, but very inefficient for larger lists.
3. Insertion Sort is adaptive, that means it reduces its total number of steps if given a partially
sorted list, hence it increases its efficiency.
4. It is better than Selection Sort and Bubble Sort algorithms.
5. Its space complexity is less, like Bubble Sorting, inerstion sort also requires a single
additional memory space.
6. It is Stable, as it does not change the relative order of elements with equal keys

How Insertion Sorting Works


/*Sorting Elements of an array in ascending order using insertion sort algorithm*/
#include<stdio.h>
#include<conio.h>
int main()
{
int data[100],n,temp,i,j;
clrscr();
printf("Enter number of terms(should be less than 100): ");
scanf("%d",&n);
printf("Enter elements: ");
for(i=0;i<n;i++)
{
scanf("%d",&data[i]);
}
for(i=1;i<n;i++)
{
temp = data[i];
j=i-1;
while(temp<data[j] && j>=0)
/*To sort elements in descending order, change temp<data[j] to temp>data[j] in above line.*/
{
data[j+1] = data[j];
--j;
}
data[j+1]=temp;
}
printf("In ascending order: ");
for(i=0; i<n; i++)
printf("%d\t",data[i]);
return 0;
}
The Shell Sort

The shell sort, sometimes called the “diminishing increment sort,” improves on the insertion sort
by breaking the original list into a number of smaller sublists, each of which is sorted using an
insertion sort. The unique way that these sublists are chosen is the key to the shell sort. Instead
of breaking the list into sublists of contiguous items, the shell sort uses an increment i,
sometimes called the gap, to create a sublist by choosing all items that are i items apart.

This can be seen in Figure 6. This list has nine items. If we use an increment of three, there are
three sublists, each of which can be sorted by an insertion sort. After completing these sorts, we
get the list shown in Figure 7. Although this list is not completely sorted, something very
interesting has happened. By sorting the sublists, we have moved the items closer to where they
actually belong.

Figure 6: A Shell Sort with Increments of Three

Figure 7: A Shell Sort after Sorting Each Sublist

Figure 8 shows a final insertion sort using an increment of one; in other words, a standard
insertion sort. Note that by performing the earlier sublist sorts, we have now reduced the total
number of shifting operations necessary to put the list in its final order. For this case, we need
only four more shifts to complete the process.
Figure 8: ShellSort: A Final Insertion Sort with Increment of 1

Figure 9: Initial Sublists for a Shell Sort


Example:

/*c program for sorting array using shell sorting method*/


#include<stdio.h>

#include<conio.h>

int main()

int arr[30];

int i,j,k,tmp,num;
printf("Enter total no. of elements : ");

scanf("%d", &num);

for(k=0; k<num; k++)

printf("\nEnter %d number : ",k+1);

scanf("%d",&arr[k]);

for(i=num/2; i>0; i=i/2)

for(j=i; j<num; j++)

for(k=j-i; k>=0; k=k-i)

if(arr[k+i]>=arr[k])

break;

else

tmp=arr[k];

arr[k]=arr[k+i];

arr[k+i]=tmp;

}
}

printf("\t**** Shell Sorting ****\n");

for(k=0; k<num; k++)

printf("%d\t",arr[k]);

getch();

return 0;

The Quick Sort


The quick sort uses divide and conquer to gain the same advantages as the merge sort, while
not using additional storage. As a trade-off, however, it is possible that the list may not be
divided in half. When this happens, we will see that performance is diminished.
A quick sort first selects a value, which is called the pivot value. Although there are many
different ways to choose the pivot value, we will simply use the first item in the list. The role of
the pivot value is to assist with splitting the list. The actual position where the pivot value
belongs in the final sorted list, commonly called the split point, will be used to divide the list for
subsequent calls to the quick sort.
Figure 12 shows that 54 will serve as our first pivot value. Since we have looked at this example
a few times already, we know that 54 will eventually end up in the position currently holding 31.
The partitionprocess will happen next. It will find the split point and at the same time move
other items to the appropriate side of the list, either less than or greater than the pivot value.

Figure 12: The First Pivot Value for a Quick Sort


Partitioning begins by locating two position markers—let‟s call them leftmark and rightmark—at
the beginning and end of the remaining items in the list (positions 1 and 8 in Figure 13). The goal
of the partition process is to move items that are on the wrong side with respect to the pivot
value while also converging on the split point. Figure 13 shows this process as we locate the
position of 54.
Figure 13: Finding the Split Point for 54
We begin by incrementing leftmark until we locate a value that is greater than the pivot value.
We then decrement rightmark until we find a value that is less than the pivot value. At this point
we have discovered two items that are out of place with respect to the eventual split point. For
our example, this occurs at 93 and 20. Now we can exchange these two items and then repeat
the process again.
At the point where rightmark becomes less than leftmark, we stop. The position of rightmark is
now the split point. The pivot value can be exchanged with the contents of the split point and the
pivot value is now in place (Figure 14). In addition, all the items to the left of the split point are
less than the pivot value, and all the items to the right of the split point are greater than the
pivot value. The list can now be divided at the split point and the quick sort can be invoked
recursively on the two halves.

Figure 14: Completing the Partition Process to Find the Split Point for 54
Example:
#include<stdio.h>
#include<conio.h>
void qsort(int arr[20], int fst, int last);
int main()
{
int arr[30];
int i,size;
printf("Enter total no. of the elements : ");
scanf("%d",&size);
printf("Enter total %d elements : \n",size);
for(i=0; i<size; i++)
scanf("%d",&arr[i]);
qsort(arr,0,size-1);
printf("Quick sorted elements are as : \n");
for(i=0; i<size; i++)
printf("%d\t",arr[i]);
getch();
return 0;
}
void qsort(int arr[20], int fst, int last)
{
int i,j,pivot,tmp;
if(fst<last)
{
pivot=fst;
i=fst;
j=last;
while(i<j)
{
while(arr[i]<=arr[pivot] && i<last)
i++;
while(arr[j]>arr[pivot])
j--;
if(i<j)
{
tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
}
tmp=arr[pivot];
arr[pivot]=arr[j];
arr[j]=tmp;
qsort(arr,fst,j-1);
qsort(arr,j+1,last);
}
}
The Merge Sort
We now turn our attention to using a divide and conquer strategy as a way to improve the
performance of sorting algorithms. The first algorithm we will study is the merge sort. Merge
sort is a recursive algorithm that continually splits a list in half. If the list is empty or has one
item, it is sorted by definition (the base case). If the list has more than one item, we split the list
and recursively invoke a merge sort on both halves. Once the two halves are sorted, the
fundamental operation, called a merge, is performed. Merging is the process of taking two
smaller sorted lists and combining them together into a single, sorted, new list. Figure 10 shows
our familiar example list as it is being split by mergeSort. Figure 11shows the simple lists, now
sorted, as they are merged back together.

Figure 10: Splitting the List in a Merge Sort


Figure 11: Lists as They Are Merged Together
/* c program for merge sorting */

#include<stdio.h>
#include<conio.h>
void merge(int [],int ,int ,int );
void part(int [],int ,int );
int main()
{
int arr[30];
int i,size;
printf("\n\t------- Merge sorting method -------\n\n");
printf("Enter total no. of elements : ");
scanf("%d",&size);
for(i=0; i<size; i++)
{
printf("Enter %d element : ",i+1);
scanf("%d",&arr[i]);
}
part(arr,0,size-1);
printf("\n\t------- Merge sorted elements -------\n\n");
for(i=0; i<size; i++)
printf("%d ",arr[i]);
getch();
return 0;
}
void part(int arr[],int min,int max)
{
int mid;
if(min<max)
{
mid=(min+max)/2;
part(arr,min,mid);
part(arr,mid+1,max);
merge(arr,min,mid,max);
}
}
void merge(int arr[],int min,int mid,int max)
{
int tmp[30];
int i,j,k,m=0;
j=min;
m=mid+1;
for(i=min; j<=mid && m<=max ; i++)
{
if(arr[j]<=arr[m])
{
tmp[i]=arr[j];
j++;
}
else
{
tmp[i]=arr[m];
m++;
}
}
if(j>mid)
{
for(k=m; k<=max; k++)
{
tmp[i]=arr[k];
i++;
}
}
else
{
for(k=j; k<=mid; k++)
{
tmp[i]=arr[k];
i++;
}
}
for(k=min; k<=max; k++)
arr[k]=tmp[k];
}

SEARCHING TECHNIQUES
LINEAR OR SEQUENTIAL SEARCH
Introduction
There are many applications requiring a search for a particular element. Searching refers to
finding out whether a particular element is present in the list. The method that we use for this
depends on how the elements of the list are organized. If the list is an unordered list, then we
use linear or sequential search, whereas if the list is an ordered list, then we use binary search.
The search proceeds by sequentially comparing the key with elements in the list, and continues
until either we find a match or the end of the list is encountered. If we find a match, the search
terminates successfully by returning the index of the element in the list which has matched. If
the end of the list is encountered without a match, the search terminates unsuccessfully.
Example:

#include <stdio.h>

#include<conio.h>

#define MAX 10
void lsearch(int list[],int n,int element)

int i, flag = 0;

clrscr();

for(i=0;i<n;i++)

if( list[i] == element)

printf(" The element whose value is %d is present at position %d in list\n",

element,i);

flag =1;

break;

if( flag == 0)

printf("The element whose value is %d is not present in the list\n", element);

void readlist(int list[],int n)

int i;

printf("Enter the elements\n");

for(i=0;i<n;i++)
scanf("%d",&list[i]);

void printlist(int list[],int n)

int i;

printf("The elements of the list are: \n");

for(i=0;i<n;i++)

printf("%d\t",list[i]);

void main()

int list[MAX], n, element;

printf("Enter the number of elements in the list max = 10\n");

scanf("%d",&n);

readlist(list,n);

printf("\nThe list before sorting is:\n");

printlist(list,n);

printf("\nEnter the element to be searched\n");

scanf("%d",&element);
lsearch(list,n,element);

}
BINARY SEARCH
Introduction
The prerequisite for using binary search is that the list must be a sorted one. We compare the
element to be searched with the element placed approximately in the middle of the list.
If a match is found, the search terminates successfully. Otherwise, we continue the search for the
key in a similar manner either in the upper half or the lower half. If the elements of the list are
arranged in ascending order, and the key is less than the element in the middle of the list, the
search is continued in the lower half. If the elements of the list are arranged in descending order,
and the key is greater than the element in the middle of the list, the search is continued in the
upper half of the list. The procedure for the binary search is given in the following program.
#include <stdio.h>
#define MAX 10
void bsearch(int list[],int n,int element)
{
int l,u,m, flag = 0;
l = 0;
u = n-1;
while(l <= u)
{
m = (l+u)/2;
if( list[m] == element)
{
printf(" The element whose value is %d is present atposition %d in list\n",element,m);
flag =1;
break;
}
else
if(list[m] < element)
l = m+1;
else
u = m-1;
}
if( flag == 0)
printf("The element whose value is %d is not present in the list\n",
element);
}
void readlist(int list[],int n)
{
int i;
printf("Enter the elements\n");
for(i=0;i<n;i++)
scanf("%d",&list[i]);
}

void printlist(int list[],int n)


{
int i;
printf("The elements of the list are: \n");
for(i=0;i<n;i++)
printf("%d\t",list[i]);
}
void main()
{
int list[MAX], n, element;
printf("Enter the number of elements in the list max = 10\n");
scanf("%d",&n);
readlist(list,n);
printf("\nThe list before sorting is:\n");
printlist(list,n);
printf("\nEnter the element to be searched\n");
scanf("%d",&element);
bsearch(list,n,element);
}
Tree represents nodes connected by edges. We'll going to discuss binary tree or binary search tree
specifically.
Binary Tree is a special datastructure used for data storage purposes. A binary tree has a special
condition that each node can have two children at maximum. A binary tree have benefits of both
an ordered array and a linked list as search is as quick as in sorted array and insertion or deletion
operation are as fast as in linked list.

Terms
Following are important terms with respect to tree.
 Path − Path refers to sequence of nodes along the edges of a tree.
 Root − Node at the top of the tree is called root. There is only one root per tree and one
path from root node to any node.
 Parent − Any node except root node has one edge upward to a node called parent.
 Child − Node below a given node connected by its edge downward is called its child node.
 Leaf − Node which does not have any child node is called leaf node.
 Subtree − Subtree represents descendents of a node.
 Visiting − Visiting refers to checking value of a node when control is on the node.
 Traversing − Traversing means passing through nodes in a specific order.
 Levels − Level of a node represents the generation of a node. If root node is at level 0,
then its next child node is at level 1, its grandchild is at level 2 and so on.
 keys − Key represents a value of a node based on which a search operation is to be carried
out for a node.

Binary Search Tree Representation


Binary Search tree exhibits a special behaviour. A node's left child must have value less than its
parent's value and node's right child must have value greater than it's parent value.

We're going to implement tree using node object and connecting them through references.

Node
A tree node should look like the below structure. It has data part and references to its left and
right child nodes.

struct node {
int data;
struct node *leftChild;
struct node *rightChild;
};

In a tree, all nodes share common construct.

BST Basic Operations


The basic operations that can be performed on binary search tree data structure, are following −
 Insert − insert an element in a tree / create a tree.
 Search − search an element in a tree.
 Preorder Traversal − traverse a tree in a preorder manner.
 Inorder Traversal − traverse a tree in an inorder manner.
 Postorder Traversal − traverse a tree in a postorder manner.
We shall learn creating (inserting into) tree structure and searching a data-item in a tree in this
chapter. We shall learn about tree traversing methods in the coming one.

Insert Operation
The very first insertion creates the tree. Afterwards, whenever an element is to be inserted. First
locate its proper location. Start search from root node then if data is less than key value, search
empty location in left subtree and insert the data. Otherwise search empty location in right
subtree and insert the data.

Algorithm

If root is NULL
then create root node
return

If root exists then


compare the data with node.data

while until insertion position is located

If data is greater than node.data


goto right subtree
else
goto left subtree

endwhile

insert data

end If

Implementation
The implementation of insert function should look like this −

void insert(int data) {


struct node *tempNode = (struct node*) malloc(sizeof(struct node));
struct node *current;
struct node *parent;

tempNode->data = data;
tempNode->leftChild = NULL;
tempNode->rightChild = NULL;

//if tree is empty, create root node


if(root == NULL) {
root = tempNode;
}else {
current = root;
parent = NULL;

while(1) {
parent = current;

//go to left of the tree


if(data < parent->data) {
current = current->leftChild;

//insert to the left


if(current == NULL) {
parent->leftChild = tempNode;
return;
}
}

//go to right of the tree


else {
current = current->rightChild;

//insert to the right


if(current == NULL) {
parent->rightChild = tempNode;
return;
}
}
}
}
}

Search Operation
Whenever an element is to be search. Start search from root node then if data is less than key
value, search element in left subtree otherwise search element in right subtree. Follow the same
algorithm for each node.

Algorithm

If root.data is equal to search.data


return root
else
while data not found

If data is greater than node.data


goto right subtree
else
goto left subtree

If data found
return node

endwhile

return data not found

end if

The implementation of this algorithm should look like this.

struct node* search(int data) {


struct node *current = root;
printf("Visiting elements: ");

while(current->data != data) {
if(current != NULL)
printf("%d ",current->data);

//go to left tree

if(current->data > data) {


current = current->leftChild;
}
//else go to right tree
else {
current = current->rightChild;
}

//not found
if(current == NULL) {
return NULL;
}

return current;
}
}

Tree Traversal
Traversal is a process to visit all the nodes of a tree and may print their values too. Because, all
nodes are connected via edges (links) we always start from the root (head) node. That is, we
cannot random access a node in tree. There are three ways which we use to traverse a tree −

 In-order Traversal
 Pre-order Traversal
 Post-order Traversal
Generally we traverse a tree to search or locate given item or key in the tree or to print all the
values it contains.

Inorder Traversal
In this traversal method, the left left-subtree is visited first, then root and then the right sub-tree.
We should always remember that every node may represent a subtree itself.
If a binary tree is traversed inorder, the output will produce sorted key values in ascending order.
We start from A, and following in-order traversal, we move to its left subtree B.B is also traversed
in-ordered. And the process goes on until all the nodes are visited. The output of in-order
traversal of this tree will be −
D→B→E→A→F→C→G

Algorithm

Until all nodes are traversed −


Step 1 − Recursively traverse left subtree.
Step 2 − Visit root node.
Step 3 − Recursively traverse right subtree.

Preorder Traversal
In this traversal method, the root node is visited first, then left subtree and finally right sub-tree.
We start from A, and following pre-order traversal, we first visit A itself and then move to its left
subtree B. B is also traversed pre-ordered. And the process goes on until all the nodes are visited.
The output of pre-order traversal of this tree will be −
A→B→D→E→C→F→G

Algorithm

Until all nodes are traversed −


Step 1 − Visit root node.
Step 2 − Recursively traverse left subtree.
Step 3 − Recursively traverse right subtree.

Postorder Traversal
In this traversal method, the root node is visited last, hence the name. First we traverse left
subtree, then right subtree and finally root.

We start from A, and following pre-order traversal, we first visit left subtree B.B is also traversed
post-ordered. And the process goes on until all the nodes are visited. The output of post-order
traversal of this tree will be −
D→E→B→F→G→C→A

Algorithm

Until all nodes are traversed −


Step 1 − Recursively traverse left subtree.
Step 2 − Recursively traverse right subtree.
Step 3 − Visit root node.
Binary Search Tree
A binary search tree (BST) is a tree in which all nodes follows the below mentioned properties −
 The left sub-tree of a node has key less than or equal to its parent node's key.
 The right sub-tree of a node has key greater than or equal to its parent node's key.
Thus, a binary search tree (BST) divides all its sub-trees into two segments;left sub-tree
and right sub-tree and can be defined as −

left_subtree (keys) ≤ node (key) ≤ right_subtree (keys)

Representation
BST is a collection of nodes arranged in a way where they maintain BST properties. Each node has
key and associated value. While searching, the desired key is compared to the keys in BST and if
found, the associated value is retrieved.
An example of BST −

We observe that the root node key (27) has all less-valued keys on the left sub-tree and higher
valued keys on the right sub-tree.

Basic Operations
Following are basic primary operations of a tree which are following.
 Search − search an element in a tree.
 Insert − insert an element in a tree.
 Preorder Traversal − traverse a tree in a preorder manner.
 Inorder Traversal − traverse a tree in an inorder manner.
 Postorder Traversal − traverse a tree in a postorder manner.

Node
Define a node having some data, references to its left and right child nodes.

struct node {
int data;
struct node *leftChild;
struct node *rightChild;
};

Search Operation
Whenever an element is to be search. Start search from root node then if data is less than key
value, search element in left subtree otherwise search element in right subtree. Follow the same
algorithm for each node.

struct node* search(int data){


struct node *current = root;
printf("Visiting elements: ");

while(current->data != data){

if(current != NULL) {
printf("%d ",current->data);

//go to left tree


if(current->data > data){
current = current->leftChild;
}//else go to right tree
else {
current = current->rightChild;
}

//not found
if(current == NULL){
return NULL;
}
}
}
return current;
}

Insert Operation
Whenever an element is to be inserted. First locate its proper location. Start search from root
node then if data is less than key value, search empty location in left subtree and insert the data.
Otherwise search empty location in right subtree and insert the data.

void insert(int data){


struct node *tempNode = (struct node*) malloc(sizeof(struct node));
struct node *current;
struct node *parent;

tempNode->data = data;
tempNode->leftChild = NULL;
tempNode->rightChild = NULL;

//if tree is empty


if(root == NULL){
root = tempNode;
}else {
current = root;
parent = NULL;

while(1){
parent = current;

//go to left of the tree


if(data < parent->data){
current = current->leftChild;
//insert to the left

if(current == NULL){
parent->leftChild = tempNode;
return;
}
}//go to right of the tree
else{
current = current->rightChild;
//insert to the right
if(current == NULL){
parent->rightChild = tempNode;
return;
}
}
}
}
}
Data Structure - Graph Data Structure
A graph is a pictorial representation of a set of objects where some pairs of objects are connected
by links. The interconnected objects are represented by points termed as vertices, and the links
that connect the vertices are callededges.
Formally, a graph is a pair of sets (V, E), where V is the set of vertices and E is the set of edges,
connecting the pairs of vertices. Take a look at the following graph −

In the above graph,


V = {a, b, c, d, e}
E = {ab, ac, bd, cd, de}

Graph Data Structure


Mathematical graphs can be represented in data-structure. We can represent a graph using an
array of vertices and a two dimensional array of edges. Before we proceed further, let's familiarize
ourselves with some important terms −
 Vertex − Each node of the graph is represented as a vertex. In example given below,
labeled circle represents vertices. So A to G are vertices. We can represent them using an
array as shown in image below. Here A can be identified by index 0. B can be identified
using index 1 and so on.
 Edge − Edge represents a path between two vertices or a line between two vertices. In
example given below, lines from A to B, B to C and so on represents edges. We can use a
two dimensional array to represent array as shown in image below. Here AB can be
represented as 1 at row 0, column 1, BC as 1 at row 1, column 2 and so on, keeping other
combinations as 0.
 Adjacency − Two node or vertices are adjacent if they are connected to each other
through an edge. In example given below, B is adjacent to A, C is adjacent to B and so on.
 Path − Path represents a sequence of edges between two vertices. In example given
below, ABCD represents a path from A to D.
Basic Operations
Following are basic primary operations of a Graph which are following.
 Add Vertex − add a vertex to a graph.
 Add Edge − add an edge between two vertices of a graph.
 Display Vertex − display a vertex of a graph.

S-ar putea să vă placă și