Sei sulla pagina 1di 62

VIT UNIVERSITY

CAT-1
Material for Data stuctures
U.Nagaraju
16MCM0004
M.tech (CCE)

Introduction to Data Structures:


What is Information?

If we arrange some data in an appropriate sequence, then it forms a Structure and gives us a meaning. T
meaning is called Information . The basic unit of Information in Computer Science is a bit, Binary Digit .
So, we found two things in Information: One is Data and the other is Structure .
What is Data Structure?
1. A data structure is a systematic way of organizing and accessing data.
2. A data structure tries to structure data!
Usually more than one piece of data
Should define legal operations on the data
The data might be grouped together (e.g. in an linked list)
3. When we define a data structure we are in fact creating a new data type of our own.
i.e. using predefined types or previously user defined types.
Such new types are then used to reference variables type within a program

Why Data Structures?

1. Data structures study how data are stored in a computer so that operations can be implemen
efficiently
2. Data structures are especially important when you have a large amount of information
3. Conceptual and concrete ways to organize data for efficient storage and manipulation.

Methods of Interpreting bit Setting

1. Binary Number System


o Non Negative
o Negative
Ones Complement Notation
Twos Complement Notation
2. Binary Coded Decimal
3. Real Number
4. Character String
Non-Negative Binary System

In this System each bit position represents a power of 2. The right most bit position represent 2 0 which equ
1. The next position to the left represents 21= 2 and so on. An Integer is represented as a sum of powers of 2
string of all 0s represents the number 0. If a 1 appears in a particular bit position, the power of 2 represented
that bit position is included in the Sum. But if a 0 appears, the power of 2 is not included in the Sum. F
example 10011, the sum is
Ones Complement Notation

Negative binary number is represented by ones Complement Notation. In this notation we represent a negat
number by changing each bit in its absolute value to the opposite bit setting. For example, since 0010011
represent 38, 11011001 is used to represent -38. The left most number is reserved for the sign of the numb
A bit String Starting with a 0 represents a positive number, where a bit string starting with a 1 represents
negative number.

Twos Complement Notation

In Twos Complement Notation is also used to represent a negative number. In this notation 1 is added to
Ones Complement Notation of a negative number. For example, since 11011001 represents -38 in On
Complement Notation 11011010 used represent -38 in Twos Complement Notation.

Types of Data structures:

The assignment of bit string to character may be entirely arbitrary, but it must be adhered to consistently. It may
that some convenient rule is used in assigning bit string to character. The number of bits varies computer wise used
represent a character.

Some computers are use 7-bit (therefore allow up to 128 possible characters), some computers are use 8-bits (up
256 character), and some use 10-bits (up to 1024 possible characters). The number of bits necessary to represent
character in a particular computer is called the byte size and a group of bits that number is called abyte .

Array

In computer programming, a group of homogeneous elements of a specific data type is known as


array , one of the simplest data structures. Arrays hold a series of data elements, usually of the sa
size and data type. Individual elements are accessed by their position in the array. The position is giv
by an index, which is also called a subscript. The index usually uses a consecutive range of intege
(as opposed to an associative array) but the index can have any ordinal set of values.

Some arrays are multi-dimensional , meaning they are indexed by a fixed number of integers, for example by a tuple
four integers. Generally, one- and two-dimensional arrays are the most common. Most programming languages have
built-in array data type.

Link List

In computer science, a linked list is one of the fundamental data structures used in computer programming
consists of a sequence of nodes, each containing arbitrary data fields and one or two references ("links") pointing
the next and/or previous nodes. A linked list is a self-referential data type because it contains a link to another data
the same type. Linked lists permit insertion and removal of nodes at any point in the list in constant time, but do
allow random access.

Types of Link List

1. Linearly-linked List
o Singly-linked list
o Doubly-linked list
2. Circularly-linked list
o Singly-circularly-linked list
o Doubly-circularly-linked list
3. Sentinel nodes

Stacks and Queues:

Stacks
Stack is an abstract data type with a bounded(predefined) capacity. It is a simple data structure that
allows adding and removing elements in a particular order. Every time an element is added, it goes
on the top of the stack, the only element that can be removed is the element that was at the top of
the stack, just like a pile of objects.

Basic features of Stack


1. Stack is an ordered list of similar data type.

2. Stack is a LIFO structure. (Last in First out).


3. push() function is used to insert new elements into the Stack and pop() is used to delete an
element from the stack. Both insertion and deletion are allowed at only one end of Stack
called Top.
4. Stack is said to be in Overflow state when it is completely full and is said to be
in Underflow state if it is completely empty.

Applications of Stack
The simplest application of a stack is to reverse a word. You push a given word to stack - letter by
letter - and then pop letters from the stack.
There are other uses also like : Parsing, Expression Conversion(Infix to Postfix, Postfix to Prefix
etc) and many more.

Implementation of Stack
Stack can be easily implemented using an Array or a Linked List. Arrays are quick, but are limited in
size and Linked List requires overhead to allocate, link, unlink, and deallocate, but is not limited in
size. Here we will implement Stack using array.

/*

Below program is written in C++ language

Class Stack
{
int top;
public:
int a[10];

//Maximum size of Stack

Stack()
{
top = -1;
}
};

void Stack::push(int x)
{
if( top >= 10)
{
cout << "Stack Overflow";

*/

}
else
{
a[++top] = x;
cout << "Element Inserted";
}
}

int Stack::pop()
{
if(top < 0)
{
cout << "Stack Underflow";
return 0;
}
else
{
int d = a[top--];
return d;
}
}

void Stack::isEmpty()
{
if(top < 0)
{
cout << "Stack is empty";
}
else

{
cout << "Stack is not empty";
}
}

Position of Top

Status of Stack

-1

Stack is Empty

Only one element in Stack

N-1

Stack is Full

Overflow state of Stack

Analysis of Stacks
Below mentioned are the time complexities for various operations that can be performed on the
Stack data structure.

Push Operation : O(1)

Pop Operation : O(1)

Top Operation : O(1)

Search Operation : O(n)

Queue:

Queue is also an abstract data type or a linear data structure, in which the first element is inserted
from one end called REAR(also called tail), and the deletion of exisiting element takes place from
the other end called asFRONT(also called head). This makes queue as FIFO data structure, which
means that element inserted first will also be removed first.
The process to add an element into queue is called Enqueue and the process of removal of an
element from queue is called Dequeue.

Basic features of Queue


1. Like Stack, Queue is also an ordered list of elements of similar data types.
2. Queue is a FIFO( First in First Out ) structure.
3. Once a new element is inserted into the Queue, all the elements inserted before the new
element in the queue must be removed, to remove the new element.
4. peek( ) function is oftenly used to return the value of first element without dequeuing it.

Applications of Queue

Queue, as the name suggests is used whenever we need to have any group of objects in an order in
which the first one coming in, also gets out first while the others wait for there turn, like in the
following scenarios :

1. Serving requests on a single shared resource, like a printer, CPU task scheduling etc.
2. In real life, Call Center phone systems will use Queues, to hold people calling them in an order,
until a service representative is free.
3. Handling of interrupts in real-time systems. The interrupts are handled in the same order as they
arrive, First come first served.

Implementation of Queue
Queue can be implemented using an Array, Stack or Linked List. The easiest way of implementing a
queue is by using an Array. Initially the head(FRONT) and the tail(REAR) of the queue points at the
first index of the array (starting the index of array from 0). As we add elements to the queue, the tail
keeps on moving ahead, always pointing to the position where the next element will be inserted,
while the head remains at the first index.

When we remove element from Queue, we can follow two possible approaches (mentioned [A] and
[B] in above diagram). In [A] approach, we remove the element at head position, and then one by
one move all the other elements on position forward. In approach [B] we remove the element
from head position and then move headto the next position.
In approach [A] there is an overhead of shifting the elements one position forward every time we
remove the first element. In approach [B] there is no such overhead, but whener we move head one
position ahead, after removal of first element, the size on Queue is reduced by one space each time.
/* Below program is wtitten in C++ language */

#define SIZE 100


class Queue

{
int a[100];
int rear;

//same as tail

int front;

//same as head

public:
Queue()
{
rear = front = -1;
}
void enqueue(int x);

//declaring enqueue, dequeue and display functions

int dequeue();
void display();
}

void Queue :: enqueue(int x)


{
if( rear = SIZE-1)
{
cout << "Queue is full";
}
else
{
a[++rear] = x;
}
}

int queue :: dequeue()


{

return a[++front];

//following approach [B], explained above

void queue :: display()


{
int i;
for( i = front; i <= rear; i++)
{
cout << a[i];
}
}
To implement approach [A], you simply need to change the dequeue method, and include a for loop
which will shift all the remaining elements one position.
return a[0];

//returning first element

for (i = 0; i < tail-1; i++)

//shifting all other elements

{
a[i]= a[i+1];
tail--;
}

Analysis of Queue

Enqueue : O(1)

Dequeue : O(1)

Size : O(1)

Queue Data Structure using Stack

A Queue is defined by its property of FIFO, which means First in First Out, i.e the element which is
added first is taken out first. Hence we can implement a Queue using Stack for storage instead of
array.
For performing enqueue we require only one stack as we can directly push data into stack, but to
performdequeue we will require two Stacks, because we need to follow queue's FIFO property and
if we directly popany data element out of Stack, it will follow LIFO approach(Last in First Out).

Implementation of Queue using Stacks


In all we will require two Stacks, we will call them InStack and OutStack.
class Queue {
public:
Stack S1, S2;
//defining methods

void enqueue(int x);

int dequeue();
}

We know that, Stack is a data structure, in which data can be added using push() method and data
can be deleted using pop() method. To learn about Stack, follow the link : Stack Data Structure

Adding Data to Queue


As our Queue has Stack for data storage in place of arrays, hence we will be adding data to Stack,
which can be done using the push() method, hence :
void Queue :: enqueue(int x) {
S1.push(x);
}

Removing Data from Queue


When we say remove data from Queue, it always means taking out the First element first and so on,
as we have to follow the FIFO approach. But if we simply perform S1.pop() in
our dequeue method, then it will remove the Last element first. So what to do now?

int Queue :: dequeue() {


while(S1.isEmpty()) {

x = S1.pop();
S2.push();
}

//removing the element


x = S2.pop();

while(!S2.isEmpty()) {
x = S2.pop();
S1.push(x);
}

return x;
}

Circular Queue:
What is Circular Queue?
The queue is considered as a circular queue when the positions 0 and MAX-1 are
adjacent. Any position before front is also after rear.

A circular queue looks like

Note:
Note that the container of items is an array. Array is stored in main memory. Main
memory is linear. So this circularity is only logical. There can not be physical circularity in
main memory.

Consider the example with Circular Queue implementation

See the logical circularity of the queue. Addition causes the increment in REAR. It
means that when REAR reaches MAX-1 position then Increment in REAR causes
REAR to reach at first position that is 0.
1

if( rear

rear

== MAX -1 )
= 0;

else

rear = rear + 1;

The short-hand equivalent representation may be


rear = ( rear + 1) % MAX;

As we know that, Deletion causes the increment in FRONT. It means that when
FRONT reaches the MAX-1 position, then increment in FRONT, causes FRONT to
reach at first position that is 0.
1

if( front

front

== MAX -1 )
= 0;

else

front = front + 1;

The short-hand equivalent representation may be


front = ( front + 1) % MAX;

Drawback of Circular Queue


The drawback of circular queue is , difficult to distinguished the full and empty
cases. It is also known as boundary case problem.
In circular queue it is necessary that:

Before insertion, fullness of Queue must be checked (for overflow).


Before deletion, emptiness of Queue must be checked (for underflow).

Condition to check FULL Circular Queue


1

if( ( front == MAX-1) || ( front ==0 && rear == MAX -1 ) )

Condition to check EMPTY Circular Queue

if( ( front == MAX-1) || ( front ==0 && rear == MAX -1 ) )

Solution of the drawback of circular Queue


Use count variable to hold the current position ( in case of insertion or deletion).

Operation of Circular Queue using count


1. Initialize Operation.
2.
3.
4.
5.

Is_Full check.
Is_Empty check.
Addition or Insertion operation.
Deletion operation.

Circular queue implementation of above operations:


#include <stdio.h>
#define MAX 5
/*Declaration of circular queue.*/
typedef struct
{
int front
;
int rear
;
int count
;
int ele[MAX]
;
}CirQueue;
/*Initailization of circular queue.*/
void initCirQueue(CirQueue * q)
{
q->front = 0;
q->rear = -1;
q->count = 0;
}
/*Check Queue is full or not*/
int isFull(CirQueue * q)
{
int full=0;
if(q->count == MAX)
full = 1;
return full;
}
/*Check Queue is empty or not*/

int isEmpty(CirQueue * q)
{
int empty=0;
if(q->count == 0)
empty = 1;
return empty;
}
/*To insert item into circular queue.*/
void insertCirQueue(CirQueue * q, int item)
{
if( isFull(q) )
{
printf("\nQueue Overflow");
return;
}
q->rear = (q->rear+1)%MAX;
q->ele[q->rear] = item;
q->count++;
printf("\nInserted item : %d",item);
}
/*To delete item from queue.*/
int deleteCirQueue(CirQueue * q, int *item)
{
if( isEmpty(q) )
{
printf("\nQueue Underflow");
return -1;
}
*item

= q->ele[q->front];

q->front = (q->front+1)%MAX;
q->count--;
return 0;
}

int main()
{
int item=0;
CirQueue q;
initCirQueue(&q);

insertCirQueue(&q,
insertCirQueue(&q,
insertCirQueue(&q,
insertCirQueue(&q,
insertCirQueue(&q,
insertCirQueue(&q,

10);
20);
30);
40);
50);
60);

if ( deleteCirQueue( &q, &item ) == 0 )


printf("\nDeleted item is : %d",item);

if ( deleteCirQueue( &q, &item ) == 0 )


printf("\nDeleted item is : %d",item);

if ( deleteCirQueue( &q, &item ) == 0 )


printf("\nDeleted item is : %d",item);

if ( deleteCirQueue( &q, &item ) == 0 )


printf("\nDeleted item is : %d",item);

if ( deleteCirQueue( &q, &item ) == 0 )


printf("\nDeleted item is : %d",item);
insertCirQueue(&q, 60);
if ( deleteCirQueue( &q, &item ) == 0 )
printf("\nDeleted item is : %d",item);

if ( deleteCirQueue( &q, &item ) == 0 )


printf("\nDeleted item is : %d",item);
printf("\n");
return 0;
}

Output:
Inserted item : 10
Inserted item : 20
Inserted item : 30
Inserted item : 40
Inserted item : 50
Queue Overflow

Deleted item is : 10
Deleted item is : 20
Deleted item is : 30
Deleted item is : 40
Deleted item is : 50
Inserted item : 60
Deleted item is : 60
Queue Underflow

Introduction to Linked Lists


Linked List is a linear data structure and it is very common data structure which consists of group of
nodes in a sequence which is divided in two parts. Each node consists of its own data and the
address of the next node and forms a chain. Linked Lists are used to create trees and graphs.

Advantages of Linked Lists

They are a dynamic in nature which allocates the memory when required.

Insertion and deletion operations can be easily implemented.

Stacks and queues can be easily executed.

Linked List reduces the access time.

Disadvantages of Linked Lists

The memory is wasted as pointers require extra memory for storage.

No element can be accessed randomly; it has to access each node sequentially.

Reverse Traversing is difficult in linked list.

Applications of Linked Lists

Linked lists are used to implement stacks, queues, graphs, etc.

Linked lists let you insert elements at the beginning and end of the list.

In Linked Lists we dont need to know the size in advance.

Types of Linked Lists

Singly Linked List : Singly linked lists contain nodes which have a data part as well as an
address part i.e. next, which points to the next node in sequence of nodes. The operations we
can perform on singly linked lists are insertion, deletion and traversal.

Doubly Linked List : In a doubly linked list, each node contains two links the first link points to
the previous node and the next link points to the next node in the sequence.

Circular Linked List : In the circular linked list the last node of the list contains the address of
the first node and forms a circular chain.

Singular Linked List


The element can be inserted in linked list in 2 ways :

Insertion at beginning of the list.

Insertion at the end of the list.

We will also be adding some more useful methods like :

Checking whether Linked List is empty or not.

Searching any element in the Linked List

Deleting a particular Node from the List

Before inserting the node in the list we will create a class Node. Like shown below :
class Node {
public:
int data;
//pointer to the next node
node* next;

node() {
data = 0;

next = NULL;
}

node(int x) {
data = x;
next = NULL;
}
}
We can also make the properties data and next as private, in that case we will need to add the
getter and setter methods to access them. You can add the getters and setter like this :
int getData() {
return data;
}

void setData(int x) {
this.data = x;
}

node* getNext() {
return next;
}

void setNext(node *n) {


this.next = n;
}
Node class basically creates a node for the data which you enter to be included into Linked List.
Once the node is created, we use various functions to fit in that node into the Linked List.

Linked List class


As we are following the complete OOPS methodology, hence we will create a separate class
for Linked List, which will have all its methods. Following will be the Linked List class :
class LinkedList {
public:
node *head;
//declaring the functions

//function to add Node at front


int addAtFront(node *n);
//function to check whether Linked list is empty
int isEmpty();
//function to add Node at the End of list
int addAtEnd(node *n);
//function to search a value
node* search(int k);
//function to delete any Node
node* deleteNode(int x);

LinkedList() {
head = NULL;
}
}

Insertion at the Beginning


Steps to insert a Node at beginning :

1. The first Node is the Head for any Linked List.

2. When a new Linked List is instantiated, it just has the Head, which is Null.
3. Else, the Head holds the pointer to the first Node of the List.
4. When we want to add any Node at the front, we must make the head point to it.
5. And the Next pointer of the newly added Node, must point to the previous Head, whether it be
NULL(in case of new List) or the pointer to the first Node of the List.
6. The previous Head Node is now the second Node of Linked List, because the new Node is
added at the front.
int LinkedList :: addAtFront(node *n) {
int i = 0;
//making the next of the new Node point to Head
n->next = head;
//making the new Node as Head
head = n;
i++;
//returning the position where Node is added
return i;
}

Inserting at the End


Steps to insert a Node at the end :

1. If the Linked List is empty then we simply, add the new Node as the Head of the Linked List.
2. If the Linked List is not empty then we find the last node, and make it' next to the new Node,
hence making the new node the last Node.
int LinkedList :: addAtEnd(node *n) {
//If list is empty
if(head == NULL) {

//making the new Node as Head


head = n;
//making the next pointe of the new Node as Null
n->next = NULL;
}
else {
//getting the last node
node *n2 = getLastNode();
n2->next = n;
}
}

node* LinkedList :: getLastNode() {


//creating a pointer pointing to Head
node* ptr = head;
//Iterating over the list till the node whose Next pointer points to null
//Return that node, because that will be the last node.
while(ptr->next!=NULL) {
//if Next is not Null, take the pointer one step forward
ptr = ptr->next;
}
return ptr;
}

Searching for an Element in the List


In searhing we do not have to do much, we just need to traverse like we did while getting the last
node, in this case we will also compare the data of the Node. If we get the Node with the same data,
we will return it, otherwise we will make our pointer point the next Node, and so on.

node* LinkedList :: search(int x) {


node *ptr = head;
while(ptr != NULL && ptr->data != x) {
//until we reach the end or we find a Node with data x, we keep moving
ptr = ptr->next;
}
return ptr;
}

Deleting a Node from the List


Deleting a node can be done in many ways, like we first search the Node with data which we want to
delete and then we delete it. In our approach, we will define a method which will take the data to be
deleted as argument, will use the search method to locate it and will then remove the Node from the
List.
To remove any Node from the list, we need to do the following :

If the Node to be deleted is the first node, then simply set the Next pointer of the Head to point to
the next element from the Node to be deleted.

If the Node is in the middle somewhere, then find the Node before it, and make the Node before
it point to the Node next to it.

node* LinkedList :: deleteNode(int x) {


//searching the Node with data x
node *n = search(x);
node *ptr = head;
if(ptr == n) {
ptr->next = n->next;
return n;
}

else {
while(ptr->next != n) {
ptr = ptr->next;
}
ptr->next = n->next;
return n;
}
}

Checking whether the List is empty or not


We just need to check whether the Head of the List is NULL or not.
int LinkedList :: isEmpty() {
if(head == NULL) {
return 1;
}
else { return 0; }
}

Now you know a lot about how to handle List, how to traverse it, how to search an element. You can
yourself try to write new methods around the List.
If you are still figuring out, how to call all these methods, then below is how your main() method will
look like. As we have followed OOP standards, we will create the objects of LinkedList class to
initialize our List and then we will create objects of Node class whenever we want to add any new
node to the List.
int main() {
LinkedList L;
//We will ask value from user, read the value and add the value to our Node
int x;

cout << "Please enter an integer value : ";


cin >> x;
Node *n1;
//Creating a new node with data as x
n1 = new Node(x);
//Adding the node to the list
L.addAtFront(n1);
}

Circular Linked List


Circular Linked List is little more complicated linked data structure. In the circular linked list we can
insert elements anywhere in the list whereas in the array we cannot insert element anywhere in the
list because it is in the contiguous memory. In the circular linked list the previous element stores the
address of the next element and the last element stores the address of the starting element. The
elements points to each other in a circular way which forms a circular chain. The circular linked list
has a dynamic size which means the memory can be allocated when it is required.

Application of Circular Linked List

The real life application where the circular linked list is used is our Personal Computers, where
multiple applications are running. All the running applications are kept in a circular linked list and
the OS gives a fixed time slot to all for running. The Operating System keeps on iterating over
the linked list until all the applications are completed.

Another example can be Multiplayer games. All the Players are kept in a Circular Linked List and
the pointer keeps on moving forward as a player's chance ends.

Circular Linked List can also be used to create Circular Queue. In a Queue we have to keep two
pointers, FRONT and REAR in memory all the time, where as in Circular Linked List, only one
pointer is required.

Implementing Circular Linked List


Implementing a circular linked list is very easy and almost similar to linear linked list implementation,
with the only difference being that, in circular linked list the last Node will have it's next point to
the Head of the List. In Linear linked list the last Node simply holds NULL in it's next pointer.
So this will be oue Node class, as we have already studied in the lesson, it will be used to form the
List.
class Node {
public:
int data;
//pointer to the next node
node* next;

node() {
data = 0;
next = NULL;
}

node(int x) {
data = x;
next = NULL;
}
}

Circular Linked List


Circular Linked List class will be almost same as the Linked List class that we studied in the previous
lesson, with a few difference in the implementation of class methods.
class CircularLinkedList {
public:
node *head;
//declaring the functions

//function to add Node at front


int addAtFront(node *n);
//function to check whether Linked list is empty
int isEmpty();
//function to add Node at the End of list
int addAtEnd(node *n);
//function to search a value
node* search(int k);
//function to delete any Node
node* deleteNode(int x);

CircularLinkedList() {
head = NULL;
}
}

Insertion at the Beginning


Steps to insert a Node at beginning :

1. The first Node is the Head for any Linked List.

2. When a new Linked List is instantiated, it just has the Head, which is Null.
3. Else, the Head holds the pointer to the fisrt Node of the List.
4. When we want to add any Node at the front, we must make the head point to it.
5. And the Next pointer of the newly added Node, must point to the previous Head, whether it be
NULL(in case of new List) or the pointer to the first Node of the List.
6. The previous Head Node is now the second Node of Linked List, because the new Node is
added at the front.
int CircularLinkedList :: addAtFront(node *n) {
int i = 0;
/* If the list is empty */
if(head == NULL) {
n->next = head;
//making the new Node as Head
head = n;
i++;
}
else {
n->next = head;
//get the Last Node and make its next point to new Node
Node* last = getLastNode();
last->next = n;
//also make the head point to the new first Node
head = n;
i++;
}
//returning the position where Node is added
return i;
}

Insertion at the End


Steps to insert a Node at the end :

1. If the Linked List is empty then we simply, add the new Node as the Head of the Linked List.
2. If the Linked List is not empty then we find the last node, and make it' next to the new Node, and
make the next of the Newly added Node point to the Head of the List.
int CircularLinkedList :: addAtEnd(node *n) {
//If list is empty
if(head == NULL) {
//making the new Node as Head
head = n;
//making the next pointer of the new Node as Null
n->next = NULL;
}
else {
//getting the last node
node *last = getLastNode();
last->next = n;
//making the next pointer of new node point to head
n->next = head;
}
}

Searching for an Element in the List


In searhing we do not have to do much, we just need to traverse like we did while getting the last
node, in this case we will also compare the data of the Node. If we get the Node with the same data,
we will return it, otherwise we will make our pointer point the next Node, and so on.

node* CircularLinkedList :: search(int x) {


node *ptr = head;
while(ptr != NULL && ptr->data != x) {
//until we reach the end or we find a Node with data x, we keep moving
ptr = ptr->next;
}
return ptr;
}

Deleting a Node from the List


Deleting a node can be done in many ways, like we first search the Node with data which we want to
delete and then we delete it. In our approach, we will define a method which will take the data to be
deleted as argument, will use the search method to locate it and will then remove the Node from the
List.
To remove any Node from the list, we need to do the following :

If the Node to be deleted is the first node, then simply set the Next pointer of the Head to point to
the next element from the Node to be deleted. And update the next pointer of the Last Node as
well.

If the Node is in the middle somewhere, then find the Node before it, and make the Node before
it point to the Node next to it.

If the Node is at the end, then remove it and make the new last node point to the head.

node* CircularLinkedList :: deleteNode(int x) {


//searching the Node with data x
node *n = search(x);
node *ptr = head;
if(ptr == NULL) {
cout << "List is empty";

return NULL;
}
else if(ptr == n) {
ptr->next = n->next;
return n;
}
else {
while(ptr->next != n) {
ptr = ptr->next;
}
ptr->next = n->next;
return n;
}
}

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(" ]");
}

DOUBLE 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.

For program check my assignment document

Other operations performed over linked list:


1.Concatenation of two Linked Lists

2.Copying of linked lists


3. Splitting of two lists from linked list:
4.Reversing a one way linked list

1.Concatenation of two Linked Lists:


Let us assume that the two linked lists are referenced by head1 and head2 respectively.
1. If the first linked list is empty then return head2.
2. If the second linked list is empty then return head1.
3. Store the address of the starting node of the first linked
list in a pointer variable, say p.
4. Move the p to
the last node of the linked list through simple linked list traversal
technique.
5. Store the address of the first node of the second linked
list in the next field of the node pointed by p. Return head1.

How to do:

C Function to Concatenate two Linked Lists:


node * concatenate (node *head1, node *head2)
{

node
*p;
if (head1==NULL)

//if the first linked

list is empty
return
(head2);
if (head2==NULL)

//if second linked

list is empty
return
(head1);

p=head1;

//place p on the first

node of the first linked list

while (p->next!=NULL)

//move p to the last node

p=p->next;
p->next=head2;

//address

of the first node of the second linked list stored in the last node of the
first linked list

return
(head1);
}

2.Copy A Link List Into Other Link List :

Consider the linked list with its start pointer as begin1.For copying this given linked list into
another list, use a new pointer variable begin2 for the list in which source list will be copied.

Initially we will store null in the list variable begin2.

Now we will traverse the entire source list from begin to the end by copying the contents to
the new target.

How to do:
Step 1: If Begin1 = Null Then
Print: Source List is Empty
Exit
[End If]
Step 2: Set Begin2 = Null
Step 3: If Free = Null Then
Print: Free space not available
Exit
Else
Allocate memory to the node New
Set New = Free And Free = Free Next
[End If]

Step 4: Set New Info=Begin1 Info And New Next =Null


Step 5: Set Begin2 = New
Step 6: Set Pointer1=Begin1 Next And Pointer2=Begin2
Step 7: Repeat While Pointer1 Null And Free Null
a. Allocate memory to node New
(New=Free And Free=Free Next)
b. Set New Info=Pointer1 Info And New Next=Null
c. Set Pointer2 Next=New
d. Set Pointer1=Pointer1 Next And Pointer2=New
[End Loop]
Step 8: If Pointer1 Null Then
Print: List copied successfully
Else
Print: Not enough space to perform copy operation
[End If]
Step 9: Exit

3.Splitting of two lists from linked list:

Suppose we have a linked list which we want to split into lists.

First we check total no. Of nodes then (N/2)th and (N/2+1)th Node.

After finding these addresses we will store null in the next part of the (n/2)th node and
address of (n/2+1)th node will be stored in the new list pointer variable begin2.

Now our list divide into 2 parts n/2 and n-n/2 with list begin1 and begin2.

How to do:
Step 1: If Begin=Null
Print: Splitting cannot be performed on empty list
Exit
[End If]
Step 2: Set pointer = Begin And Count = 0
Step 3: Repeat Steps 4 and 5 While Pointer Null
Step 4:

Set Count = Count + 1

Step 5:

Set Pointer = pointer Next


[End Loop]

Step 6: Set Mid = Integer(count/2)


Step 7: Set Begin2 = Null And Pointer = Begin And i =1
Step 8: Repeat Step 9 While i Mid
Step 9:

Set Pointer = Pointer Next


Set i = i +1
[End Loop]

Step 10: Set Begin2 = Pointer Next And


Pointer Next = Null
Step 11: Exit

4.Reversing A One Way Linked List :


To reverse a linked list, we need to use three pointers variables.
One pointer variable is used to store the address of current node.
Second pointer variable will be used to store the address of next node.

The third pointer variable will be used to store the address of next to
next of current node.

How to do:
Step 1: If Begin = Null Then
Print: No node is present in link list
Exit
[End If]
Step 2: If Begin Next = Null Then
Print: link list is having only one node
Exit
[End If]
Step 3: If Begin Next Null Then
Set Pointer1 = Begin
Set Pointer2 = Begin Next
Set Pointer3 = Pointer2 Next
[End If]
Step 4: If Pointer3 = Null Then

Set Pointer2 Next = Pointer1


Set Pointer1 Next = Null
Set Begin = Pointer2
Exit
[End If]
Step 5: Set Pointer1 Next=Null
Step 6: Repeat steps 7 to 10 while Pointer3 Next Null
Step 7:

Set Pointer2 Next=Pointer1

Step 8:

Set Pointer1=Pointer2

Step 9:

Set Pointer2=Pointer3

Step 10:

Set Pointer3=Pointer3 Next

[End Loop]
Step 11: Set Pointer2 Next=Pointer1
Step 12: Set Pointer3 Next=Pointer2
Step 13: Set Begin=Pointer3
Step 14: Exit

Binary tees and binary search tree:

Binary Trees
Introduction

We extend the concept of linked data structures to


structure containing nodes with more than one selfreferenced field. A binary tree is made of nodes,
where each node contains a "left" reference, a
"right" reference, and a data element. The topmost
node in the tree is called the root.
Every node (excluding a root) in a tree is
connected by a directed edge from exactly one
other node. This node is called a parent. On the
other hand, each node can be connected to
arbitrary number of nodes, called children. Nodes
with no children are called leaves, or external
nodes. Nodes which are not leaves are called
internal nodes. Nodes with the same parent are
called siblings.

More tree terminology:

The depth of a node is the number of edges from the root to the node.
The height of a node is the number of edges from the node to the deepest leaf.
The height of a tree is a height of the root.
A full binary tree.is a binary tree in which each node has exactly zero or two
children.
A complete binary tree is a binary tree, which is completely filled, with the
possible exception of the bottom level, which is filled from left to right.

A complete binary tree is very special tree, it provides the best possible ratio between
the number of nodes and the height. The height h of a complete binary tree with N
nodes is at most O(log N). We can easily prove this by counting nodes on each level,
starting with the root, assuming that each level has the maximum number of nodes:
n = 1 + 2 + 4 + ... + 2h-1 + 2h = 2h+1 - 1

Solving this with respect to h, we obtain


h = O(log n)

where the big-O notation hides some superfluous details.


Advantages of trees
Trees are so useful and frequently used, because they have some very serious
advantages:

Trees reflect structural relationships in the data


Trees are used to represent hierarchies
Trees provide an efficient insertion and searching
Trees are very flexible data, allowing to move subtrees around with minumum
effort

Traversals
A traversal is a process that visits all the nodes in the tree. Since a tree is a nonlinear
data structure, there is no unique traversal. We will consider several traversal
algorithms with we group in the following two kinds

depth-first traversal
breadth-first traversal

There are three different types of depth-first traversals, :

PreOrder traversal - visit the parent first and then left and right children;
InOrder traversal - visit the left child, then the parent and the right child;
PostOrder traversal - visit left child, then the right child and then the parent;

There is only one kind of breadth-first traversal--the level order traversal. This
traversal visits nodes by levels from top to bottom and from left to right.

As an example consider the following tree and


its four traversals:
PreOrder - 8, 5, 9, 7, 1, 12, 2, 4, 11, 3
InOrder - 9, 5, 1, 7, 2, 12, 8, 4, 3, 11
PostOrder - 9, 1, 2, 12, 7, 5, 3, 11, 4, 8
LevelOrder - 8, 5, 4, 9, 7, 11, 1, 12, 3, 2

In the next picture we demonstarte the order of node visitation. Number 1 denote the
first node in a particular traversal and 7 denote the last node.

These common traversals can be represented as a single algorithm by assuming that


we visit each node three times. An Euler tour is a walk around the binary tree where
each edge is treated as a wall, which you cannot cross. In this walk each node will be
visited either on the left, or under the below, or on the right. The Euler tour in which
we visit nodes on the left produces a preorder traversal. When we visit nodes from the
below, we get an inorder traversal. And when we visit nodes on the right, we get a
postorder traversal.

Binary Search Trees


We consider a particular kind of a binary tree called a Binary Search Tree (BST). The
basic idea behind this data structure is to have such a storing repository that provides
the efficient way of data sorting, searching and retriving.
A BST is a binary tree where nodes are ordered
in the following way:

each node contains one key (also known


as data)
the keys in the left subtree are less then
the key in its parent node, in short L < P;
the keys in the right subtree are greater
the key in its parent node, in short P < R;
duplicate keys are not allowed.

In the following tree all nodes in the left subtree


of 10 have keys < 10 while all nodes in the right
subtree > 10. Because both the left and right
subtrees of a BST are again search trees; the
above definition is recursively applied to all
internal nodes:

Implementation

We implement a binary search tree using a private inner class BSTNode. In order to
support the binary search tree property, we require that data stored in each node is
Comparable:
public class BST <AnyType extends Comparable<AnyType>>
{
private Node<AnyType> root;
private class Node<AnyType>
{
private AnyType data;
private Node<AnyType> left, right;
public Node(AnyType data)
{
left = right = null;
this.data = data;
}
}
...
}

Insertion
The insertion procedure is quite similar to searching. We start at the root and
recursively go down the tree searching for a location in a BST to insert a new node. If
the element to be inserted is already in the tree, we are done (we do not insert
duplicates). The new node will always replace a NULL reference.

Exercise. Given a sequence of numbers:


11, 6, 8, 19, 4, 10, 5, 17, 43, 49, 31

Draw a binary search tree by inserting the above numbers from left to right.

Searching
Searching in a BST always starts at the root. We compare a data stored at the root
with the key we are searching for (let us call it as toSearch). If the node does not
contain the key we proceed either to the left or right child depending upon
comparison. If the result of comparison is negative we go to the left child, otherwise to the right child. The recursive structure of a BST yields a recursive algorithm.
Searching in a BST has O(h) worst-case runtime complexity, where h is the height of
the tree. Since s binary search tree with n nodes has a minimum of O(log n) levels, it
takes at least O(log n) comparisons to find a particular node. Unfortunately, a binary
serch tree can degenerate to a linked list, reducing the search time to O(n).
Deletion
Deletion is somewhat more tricky than insertion. There are several cases to consider.
A node to be deleted (let us call it as toDelete)

is not in a tree;
is a leaf;
has only one child;
has two children.

If toDelete is not in the tree, there is nothing to delete. If toDelete node has only one
child the procedure of deletion is identical to deleting a node from a linked list - we
just bypass that node being deleted

Deletion of an internal node with two children is less straightforward. If we delete


such a node, we split a tree into two subtrees and therefore, some children of the
internal node won't be accessible after deletion. In the picture below we delete 8:

Deletion starategy is the following: replace the node being deleted with the largest
node in the left subtree and then delete that largest node. By symmetry, the node being
deleted can be swapped with the smallest node is the right subtree.
See BST.java for a complete implementation.
Exercise. Given a sequence of numbers:
11, 6, 8, 19, 4, 10, 5, 17, 43, 49, 31

Draw a binary search tree by inserting the above numbers from left to right and then
show the two trees that can be the result after the removal of 11.

Non-Recursive Traversals
Depth-first traversals can be easily implemented recursively.A non-recursive
implementation is a bit more difficult. In this section we implement a pre-order
traversal as a tree iterator
public Iterator<AnyType> iterator()
{
return new PreOrderIterator();
}

where the PreOrderIterator class is implemented as an inner private class of the BST
class
private class PreOrderIterator implements Iterator<AnyType>
{
...
}

The main difficulty is with next() method, which requires the implicit recursive stack
implemented explicitly. We will be using Java's Stack. The algorithm starts with the
root and push it on a stack. When a user calls for the next() method, we check if the
top element has a left child. If it has a left child, we push that child on a stack and
return a parent node. If there is no a left child, we check for a right child. If it has a
right child, we push that child on a stack and return a parent node. If there is no right
child, we move back up the tree (by popping up elements from a stack) until we find a
node with a right child. Here is the next()implementation
public AnyType next()
{
Node cur = stk.peek();

if(cur.left != null)
{
stk.push(cur.left);
}
else
{
Node tmp = stk.pop();
while(tmp.right == null)
{
if (stk.isEmpty()) return cur.data;
tmp = stk.pop();
}
stk.push(tmp.right);
}
return cur.data;
}

The following example.shows the output and the state of the stack during each call
to next(). Note, the algorithm works on any binary trees, not necessarily binary
search trees..

Output

Stack

2
1

4
2
1

6
4
2
1

5
1

7
5
1

8
1

A non-recursive preorder traversal can be eloquently implemented in just three lines


of code. If you understand next()'s implementation above, it should be no problem to
grasp this one:

public AnyType next()


{
if (stk.isEmpty()) throw new java.util.NoSuchElementException();
Node cur = stk.pop();
if(cur.right != null) stk.push(cur.right);
if(cur.left != null) stk.push(cur.left);
return cur.data;
}

Note, we push the right child before the left child.

All copyrights@Nagaraju

This is prepared by -U.Nagaraju(B.tech,M.tech) (VIT Univertity)

1.

1.

Potrebbero piacerti anche