Advanced Data Structures
Advanced Data Structures
Shital Dongre
Asst. Prof.
IT & MCA Dept,
VIT, Pune
In Semester Assessment End Semester
Assessment
10 30 15 10 20 15
Hands on Theory
20 30 10 15 10 15
Program
Data
Algo
Algo- Ways for Data transformation
Data structure-
◦ Stores data
◦ makes algorithm simpler
◦ easier to maintain & often faster.
Sophisticated data str- simpler the algo
Simple algo- less expensive, less code
Logic is simple- modifications are less likely
to introduce errors
Easier to repair defects, make modifications,
or add enhancements
Ex- 1. array 2. Stack ex- pile of plates, box of
books 3. Non-Linear data str- Tree- used for
indexing, routing table
Section 1: Arrays , Stack , Queue, Linked List
e.g
int A[6];
double d[15];
8
After declaration, array contains some garbage
value.
Static initialization
int month_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
9
int A[6]; A[0] A[1] A[2] A[3] A[4] A[5]
0x100 0x100 0x100 0x101 0x101 0x102
0 4 8 2 6 0
6 5 4 3 2 1
Write to an element
A[3] = 5;
10
No “Strings” keyword
A string is an array of characters.
OR
char string*+ = “hello world”;
char *string = “hello world”;
11
char string*+ = “hello world”;
printf(“%s”, string);
12
• A char pointer points to a single byte.
• An int pointer points to first of the four bytes.
• A pointer itself has an address where it is stored
in the memory. Pointers are usually four bytes.
int *p; int* p;
* is called the dereference operator
• *p gives the value pointed by p
4 i
int i = 4;
p = &i; p
• & (ampersand) is called the reference operator
• &i returns the address of variable i
13
A 32-bit system has 32 bit address space.
To store any address, 32 bits are required.
14
int *p, x = 20;
p = &x;
printf("p = %p\n", p);
printf("p+1 = %p\n", (int*)p+1);
printf("p+1 = %p\n", (char*)p+1);
printf("p+1 = %p\n", (float*)p+1);
printf("p+1 = %p\n", (double*)p+1);
Sample output:
p = 0022FF70
p+1 = 0022FF74
p+1 = 0022FF71
p+1 = 0022FF74
p+1 = 0022FF78
15
Pointers and arrays are tightly coupled.
char a[] = “Hello World”;
char *p = &a[0];
16
Idea:
◦ Repeatedly pass through the array
◦ Swaps adjacent elements that are out of order
i
1 2 3 n
8 4 6 9 2 3 1
j
17
8 4 6 9 2 3 1 1 8 4 6 9 2 3
i=1 j i=2 j
8 4 6 9 2 1 3 1 2 8 4 6 9 3
i=1 j i=3 j
8 4 6 9 1 2 3 1 2 3 8 4 6 9
i=1 j i=4 j
8 4 6 1 9 2 3 1 2 3 4 8 6 9
i=1 j i=5 j
8 4 1 6 9 2 3 1 2 3 4 6 8 9
i=1 j i=6 j
8 1 4 6 9 2 3 1 2 3 4 6 8 9
i=1 j i=7
j
1 8 4 6 9 2 3
i=1 j 18
Alg.: BUBBLESORT(A)
for i 1 to length[A]
do for j length[A] downto i + 1
do if A[j] < A[j -1]
then exchange A[j] A[j-1]
i
8 4 6 9 2 3 1
i=1 j
19
int d[3][2];
20
A Multidimensional array is stored in a row major format.
A two dimensional case:
next memory element to d[0][3] is d[1][0]
24
To insert 12, we need to
make room for it by
moving first 36 and then
24.
25
26
27
input array
5 2 4 6 1 3
at each iteration, the array is divided in two sub-arrays:
sorted unsorted
28
29
Alg.: INSERTION-SORT(A) 1 2 3 4 5 6 7 8
for j ← 2 to n a1 a2 a3 a4 a5 a6 a7 a8
do key ← A[ j ]
key
Insert A[ j ] into the sorted sequence A[1 . . j -1]
i←j-1
while i > 0 and A[i] > key
do A[i + 1] ← A[i]
i←i–1
A[i + 1] ← key
Insertion sort – sorts the elements in place
30
INSERTION-SORT(A) cost times
for j ← 2 to n c1 n
do key ← A[ j ] c2 n-1
Insert A[ j ] into the sorted sequence A[1 . . j 0
-1] n-1
i←j-1 c4 n-1
while i > 0 and A[i] > key c5
n
tj
j2
do A[i + 1] ← A[i] c6
n
( t j 1)
j2
i←i–1 c7 n
(t 1)
A[i + 1] ← key c8 n-1
j2 j
T ( n ) c 1 n c 2 ( n 1 ) c 4 ( n 1 ) c 5 t j c 6 t j 1 c 7 t j 1 c 8 ( n 1 )
j2 j2 j2
31
Idea:
◦ Find the smallest element in the array
◦ Exchange it with the element in the first position
◦ Find the second smallest element and exchange it
with the element in the second position
◦ Continue until the array is sorted
Disadvantage:
◦ Running time depends only slightly on the amount
of order in the file
32
8 4 6 9 2 3 1 1 2 3 4 9 6 8
1 4 6 9 2 3 8 1 2 3 4 6 9 8
1 2 6 9 4 3 8 1 2 3 4 6 8 9
1 2 3 9 4 6 8 1 2 3 4 6 8 9
33
Alg.: SELECTION-SORT(A)
8 4 6 9 2 3 1
n ← length[A]
for j ← 1 to n - 1
do smallest ← j
for i ← j + 1 to n
do if A[i] < A[smallest]
then smallest ← i
exchange A[j] ↔ A[smallest]
34
cost times
Alg.: SELECTION-SORT(A)
c1 1
n ← length[A]
c2 n
for j ← 1 to n - 1
do smallest ← j c3 n-1
n2/2 for i ← j + 1 to n c4 n 1
j 1
( n j 1)
n then smallest ← i c6
n 1
j 1
(n j)
T ( n ) c 1 c 2 n c 3 ( n 1) c 4 ( n j 1) c 5 n j n j 35
c 7 ( n 1) ( n )
2
c6
j 1 j 1 j2
Insertion sort
◦ Design approach: incremental
◦ Sorts in place: Yes
◦ Best case: (n)
◦ Worst case:
(n2)
Bubble Sort
◦ Design approach:
incremental
◦ Sorts in place:
Yes
◦ Running time:
(n2)
36
Selection sort
◦ Design approach: incremental
◦ Sorts in place: Yes
◦ Running time:
(n2)
Merge Sort
◦ Design approach:
divide and conquer
◦ Sorts in place:
No
◦ Running time:
37
Bucket sort works by partitioning the
elements into buckets and the return the
result
Buckets are assigned based on each
element‟s search key
To return the result, concatenate each bucket
and return as a single array
Some variations
◦ Make enough buckets so that each will only hold
one element, use a count for duplicates
◦ Use fewer buckets and then sort the contents of
each bucket
44
To sort an array A[p . . r]:
Divide
◦ Divide the n-element sequence to be sorted into
two subsequences of n/2 elements each
Conquer
◦ Sort the subsequences recursively using merge sort
◦ When the size of the sequences is 1 there is
nothing more to do
Combine
◦ Merge the two sorted subsequences
45
p q r
1 2 3 4 5 6 7 8
Alg.: MERGE-SORT(A, p, r) 5 2 4 7 1 3 2 6
MERGE(A, p, q, r) Combine
46
1 2 3 4 5 6 7 8
Divide 5 2 4 7 1 3 2 6 q=4
1 2 3 4 5 6 7 8
5 2 4 7 1 3 2 6
1 2 3 4 5 6 7 8
5 2 4 7 1 3 2 6
1 2 3 4 5 6 7 8
5 2 4 7 1 3 2 6
47
1 2 3 4 5 6 7 8
Conquer 1 2 2 3 4 5 6 7
and
Merge 1 2 3 4 5 6 7 8
2 4 5 7 1 2 3 6
1 2 3 4 5 6 7 8
2 5 4 7 1 3 2 6
1 2 3 4 5 6 7 8
5 2 4 7 1 3 2 6
48
1 2 3 4 5 6 7 8 9 10 11
4 7 2 6 1 4 7 3 5 2 6 q=6
Divide
1 2 3 4 5 6 7 8 9 10 11
q=3 4 7 2 6 1 4 7 3 5 2 6 q=9
1 2 3 4 5 6 7 8 9 10 11
4 7 2 6 1 4 7 3 5 2 6
1 2 3 4 5 6 7 8 9 10 11
4 7 2 6 1 4 7 3 5 2 6
1 2 4 5 7 8
4 7 6 1 7 3
49
1 2 3 4 5 6 7 8 9 10 11
Conquer 1 2 2 3 4 4 5 6 6 7 7
and
Merge 1 1 2
2
3
4
4
4
5
6
6
7
7
2
8
3
9
5
10
6
11
1 2 3 4 5 6 7 8 9 10 11
2 4 7 1 4 6 3 5 7 2 6
1 2 3 4 5 6 7 8 9 10 11
4 7 2 1 6 4 3 7 5 2 6
1 2 4 5 7 8
4 7 6 1 7 3
50
p q r
1 2 3 4 5 6 7 8
2 4 5 7 1 2 3 6
51
p q r
Idea for merging: 1 2 3 4 5 6 7 8
2 4 5 7 1 2 3 6
◦ Two piles of sorted cards
Choose the smaller of the two top cards
Remove it and place it in the output pile
◦ Repeat the process until one pile is empty
◦ Take the remaining input pile and place it face-
down onto the output pile
A1 A[p, q]
A[p, r]
A2 A[q+1, r]
52
p q r
Alg.: MERGE(A, p, q, r) 1 2 3 4 5 6 7 8
2 4 5 7 1 2 3 6
1. Compute n1 and n2
2. Copy the first n1 elements into
n1 n2
L[1 . . n1 + 1] and the next n2 elements into R[1 . . n2
+ 1] p q
3. L[n1 + 1] ← ; R[n2 + 1] ←
L 2 4 5 7
4. i ← 1; j←1 q+1 r
5. for k ← p to r R 1 2 3 6
6. do if L[ i ] ≤ R[ j ]
7. then A[k] ← L[ i ]
8. i ←i + 1
9. else A[k] ← R[ j ]
10. j←j+1
53
void merge(int a[], int low, int
high, int mid)
{
void mergesort(int a[], int low, int high)
int i,j,k,c[max];
{
i=low;
j=mid+1; int mid;
k=0; if(low<high)
while(i<=mid) && (j<=high) {
{ mid=(low+high)/2;
if(a[i]<a[j]) mergesort(a,low,mid);
c[k]=a[i++]; mergesort(a,mid+1,high);
else merge(a,low,high,mid);
c[k]=a[j++]; }
k++; }
}
while(i<=mid)
c[k++]=a[i++];
while(j<=high)
c[k++]=a[j++];
for(i=low,j=0;i<=high;i++,j++)
{
a[i]=c[j];
}
}
Given an array of n elements (e.g., integers):
If array only contains one element, return
Else
◦ pick one element to use as pivot.
◦ Partition elements into two sub-arrays:
Elements less than or equal to pivot
Elements greater than pivot
◦ Quicksort two sub-arrays
◦ Return results
We are given array of n integers to sort:
40 20 10 80 60 50 7 30 100
There are a number of ways to pick the pivot
element. In this example, we will use the first
element in the array:
40 20 10 80 60 50 7 30 100
Given a pivot, partition the elements of the
array such that the resulting array consists
of:
1. One sub-array that contains elements >= pivot
2. Another sub-array that contains elements <
pivot
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
5. Swap data[too_small_index] and data[pivot_index]
too_big_index too_small_index
1. While data[too_big_index] <= data[pivot]
++too_big_index
2. While data[too_small_index] > data[pivot]
--too_small_index
3. If too_big_index < too_small_index
swap data[too_big_index] and data[too_small_index]
4. While too_small_index > too_big_index, go to 1.
5. Swap data[too_small_index] and data[pivot_index]
pivot_index = 4 7 20 10 30 40 50 60 80 100
too_big_index too_small_index
7 20 10 30 40 50 60 80 100
86
Def: A heap is a nearly complete binary tree
with the following two properties:
◦ Structural property: all levels are full, except
possibly the last one, which is filled from left to
right
◦ Order (heap) property: for any node x
Parent(x) ≥ x
8 From the heap
property, it follows
7 4 that:
5 2
“The root is the
maximum
Heap
element of the heap!”
A heap is a binary tree that is filled 87in order
A heap can be stored as an
array A.
◦ Root of tree is A[1]
◦ Left child of A[i] = A[2i]
◦ Right child of A[i] = A[2i + 1]
◦ Parent of A[i] = A[ i/2 ]
◦ Heapsize[A] ≤ length[A]
The elements in the
subarray A[(n/2+1) .. n]
are leaves
88
Max-heaps (largest element at root), have the
max-heap property:
◦ for all nodes i, excluding the root:
A[PARENT(i)] ≥ A[i]
89
New nodes are always inserted at the bottom
level (left to right)
Nodes are removed from the bottom level
(right to left)
90
Maintain/Restore the max-heap property
◦ MAX-HEAPIFY
Create a max-heap from an unordered array
◦ BUILD-MAX-HEAP
Sort an array in place
◦ HEAPSORT
Priority queues
91
Suppose a node is smaller than a
child
◦ Left and Right subtrees of i are max-
heaps
To eliminate the violation:
◦ Exchange with larger child
◦ Move down the tree
◦ Continue until node is not smaller than
children
92
MAX-HEAPIFY(A, 2, 10)
A[2] A[4]
A[2] violates the heap property A[4] violates the heap property
A[4] A[9]
95
MAX-HEAPIFY(A, 1, 4) MAX-HEAPIFY(A, 1, 3) MAX-HEAPIFY(A, 1, 2)
MAX-HEAPIFY(A, 1, 1)
96
1. BUILD-MAX-HEAP(A) O(n)
2. for i ← length[A] downto 2
3. do exchange A[1] ↔ A[i] n-1 times
4. MAX-HEAPIFY(A, 1, i - 1)
O(lgn)
97
Binary search. Given value and sorted array
a[], find index i
lo hi
Binary search. Given value and sorted array
a[], find index i
lo mid hi
Binary search. Given value and sorted array
a[], find index i
6 13 14 25 33 43 51 53 64 72 84 93 95 96 97
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
lo hi
Binary search. Given value and sorted array
a[], find index i
lo mid hi
Binary search. Given value and sorted array
a[], find index i
6 13 14 25 33 43 51 53 64 72 84 93 95 96 97
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
lo hi
Binary search. Given value and sorted array
a[], find index i
lo mid hi
Binary search. Given value and sorted array
a[], find index i
lo
hi
Binary search. Given value and sorted array
a[], find index i
lo
hi
mid
Binary search. Given value and sorted array
a[], find index i
lo
hi
mid
low = 0;
high = length - 1;
while (low <= high) {
mid = (low + high) / 2;
if (a[mid] < target) {
low = mid + 1;
} else if (a[mid] > target) {
high = mid - 1;
} else {
return mid; // target found
}
}
Similarities with Binary Search:
Works for sorted arrays
A Divide and Conquer Algorithm.
Has Log n time complexity.
Differences with Binary Search:
Fibonacci Search divides given array in unequal parts
Binary Search uses division operator to divide range.
Fibonacci Search doesn‟t use /, but uses + and -. The
division operator may be costly on some CPUs.
Fibonacci Search examines relatively closer elements
in subsequent steps. So when input array is big that
cannot fit in CPU cache or even in RAM, Fibonacci
Search can be useful.
Fibonacci Numbers are recursively defined as
F(n) = F(n-1) + F(n-2), F(0) = 0, F(1) = 1.
First few Fibinacci Numbers are 0, 1, 1, 2, 3,
5, 8, 13, 21, 34, 55, 89, 144, …
Below observation is used for range
elimination, and hence for the O(log(n))
complexity.
F(n - 2) ≈ (1/3)*F(n) and F(n - 1)
≈ (2/3)*F(n).
Let the searched element be x.
The idea is to first find the smallest
Fibonacci number that is greater than or
equal to the length of given array. Let the
found Fibonacci number be fib (m‟th
Fibonacci number). We use (m-2)‟th
Fibonacci number as the index (If it is a
valid index). Let (m-2)‟th Fibonacci
Number be i, we compare arr[i] with x, if x
is same, we return i. Else if x is greater, we
recur for subarray after i, else we recur for
subarray before i.
Below is the complete algorithm
Let arr[0..n-1] be the input array and element to be searched be x.
Find the smallest Fibonacci Number greater than or equal to n. Let
this number be fibM [m‟th Fibonacci Number]. Let the two Fibonacci
numbers preceding it be fibMm1 [(m-1)‟th Fibonacci Number] and
fibMm2 [(m-2)‟th Fibonacci Number].
While the array has elements to be inspected:
◦ Compare x with the last element of the range covered by fibMm2
◦ If x matches, return index
◦ Else If x is less than the element, move the three Fibonacci variables two
Fibonacci down, indicating elimination of approximately rear two-third of
the remaining array.
◦ Else x is greater than the element, move the three Fibonacci variables one
Fibonacci down. Reset offset to index. Together these indicate elimination
of approximately front one-third of the remaining array.
Since there might be a single element remaining for comparison,
check if fibMm1 is 1. If Yes, compare x with that remaining element.
If match, return index.
Ex
A={10,22,35,40,45,50,80,82,85,90,100},
1 2 3 4 5 6 7 8 9 10 11
10 22 35 40 45 50 80 82 85 90 100
X=85
N=11
Fib=0,1,1,2,3,5,8,13,21,34
Fib(7)=13 >11
(m-1)= 8, (m-2)=5
i=min(offset+m2,n)
Offset-It marks the range that has been
eliminated, starting from the front. We will
update it time to time.
Input Algorithm Output
Running Time
80
difficult to determine 60
◦ Easier to analyze
0
1000 2000 3000 4000
◦ Crucial to applications
Input Size
To analyze algorithms:
◦ First, we start to count the number of
significant operations in a particular solution
to assess its efficiency.
◦ Then, we will express the efficiency of
algorithms using growth functions.
12
0
Each operation in an algorithm (or a program) has a
cost.
Each operation takes a certain of time.
A sequence of operations:
Total Cost = c1 + c2
12
1
Example: Simple If-Statement
Cost Times
if (n < 0) c1 1
absval = -n c2 1
else
absval = n; c3 1
12
2
Example: Simple Loop
Cost Times
i = 1; c1 1
sum = 0; c2 1
while (i <= n) { c3 n+1
i = i + 1; c4 n
sum = sum + i; c5 n
}
12
3
Example: Nested Loop
Cost Times
i=1; c1 1
sum = 0; c2 1
while (i <= n) { c3 n+1
j=1; c4 n
while (j <= n) { c5 n*(n+1)
sum = sum + i; c6 n*n
j = j + 1; c7 n*n
}
i = i +1; c8 n
}
Total Cost = c1 + c2 + (n+1)*c3 + n*c4 +
n*(n+1)*c5+n*n*c6+n*n*c7+n*c8
The time required for this algorithm is proportional to n2
12
4
Loops: The running time of a loop is at most the
running time of the statements inside of that loop
times the number of iterations.
Nested Loops: Running time of a nested loop
containing a statement in the inner most loop is the
running time of statement multiplied by the product
of the sized of all loops.
Consecutive Statements: Just add the running times
of those consecutive statements.
If/Else: Never more than the running time of the test
plus the larger of running times of S1 and S2.
12
5
We measure an algorithm‟s time requirement as a function
of the problem size.
◦ Problem size depends on the application: e.g. number of elements
in a list for a sorting algorithm, the number disks for towers of
hanoi.
So, for instance, we say that (if the problem size is n)
◦ Algorithm A requires 5*n2 time units to solve a problem of size n.
◦ Algorithm B requires 7*n time units to solve a problem of size n.
The most important thing to learn is how quickly the
algorithm‟s time requirement grows as a function of the
problem size.
◦ Algorithm A requires time proportional to n2.
◦ Algorithm B requires time proportional to n.
An algorithm‟s proportional time requirement is known as
growth rate.
We can compare the efficiency of two algorithms by
comparing their growth rates.
12
6
Time requirements as a function of the problem size n
12
7
Function Growth Rate Name
c Constant
log N Logarithmic
log2N Log-squared
N Linear
N log N
N2 Quadratic
N3 Cubic
2N Exponential
128
CENG 213 Data Structures
The big-Oh notation gives an upper bound on the
growth rate of a function
The statement “f(n) is O(g(n))” means that the
growth rate of f(n) is no more than the growth
rate of g(n)
We can use the big-Oh notation to rank functions
according to their growth rate
f(n) is O(g(n)) g(n) is O(f(n))
g(n) grows more Yes No
f(n) grows more No Yes
Same growth Yes Yes
12
Analysis of Algorithms 9
- Shital Dongre
- Assistant Professor
- VIT, Pune.
What is a stack?
linear data structure
It is an ordered group of homogeneous items
of elements.
Elements are added to and removed from the
top of the stack
Stack principle: LAST IN FIRST OUT(LIFO)
It means the last element inserted is the first
one to be removed
Ex- stack of plates
Last In First Out
4 top
3 top 3 3 top
2 top
2 2 2
1 top 1 1 1 1
Applications of stack
Balancing of symbols
Infix to Postfix /Prefix conversion
Redo-undo features at many places like in editors.
Forward and backward feature in web browsers
Used in many algorithms like Tower of
Hanoi, tree traversals, topological graph sorting
etc.
Other applications can be Backtracking, N queen
problem etc.
Operations on stack
isEmpty
Push
Pop
isFull
Below is the complete algorithm
Let arr[0..n-1] be the input array and element to be searched be x.
Find the smallest Fibonacci Number greater than or equal to n. Let this
number be fibM [m’th Fibonacci Number]. Let the two Fibonacci numbers
preceding it be fibMm1 [(m-1)’th Fibonacci Number] and fibMm2 [(m-2)’th
Fibonacci Number].
While the array has elements to be inspected:
Compare x with the last element of the range covered by fibMm2
If x matches, return index
Else If x is less than the element, move the three Fibonacci variables two Fibonacci
down, indicating elimination of approximately rear two-third of the remaining array.
Else x is greater than the element, move the three Fibonacci variables one Fibonacci
down. Reset offset to index. Together these indicate elimination of approximately
front one-third of the remaining array.
Since there might be a single element remaining for comparison, check if
fibMm1 is 1. If Yes, compare x with that remaining element. If match, return
index.
i=min(offset+m2,n)
Offset-It marks the range that has been eliminated,
starting from the front. We will update it time to time.
isEmpty - Returns true(1) if stack is empty,
else false(0).
int isEmpty()
{
if (top==-1)
return 1;
else
return 0;
}
int top= -1
int stack[MAX_STACK_SIZE]
isFull - Returns true(1) if stack is Full,
else false(0).
int isFull()
{
if (top==(MAX_STACK_SIZE -1))
return 1;
else
return 0;
}
Push- Add item in stack
top = top + 1;
stack[top] = num;
}
Pop- Remove item from stack
int pop()
{
int num;
if(isEmpty())
printf(“\n Stack is empty”);
num=stack[top];
top--;
return num;
}
Stack using Linked list
Extend stack size dynamically
Push(..,3)
void pop(struct Node** head)
{
if (isEmpty(*head))
printf(“ Stack is Empty”);
2. Postfix to Infix
Input : abc++
Output : (a + (b + c))
Input : ab*c+
Output : ((a*b)+c)
1.While there are input symbol left
…1.1 Read the next symbol from the input.
2.If the symbol is an operand
…2.1 Push it onto the stack.
3.Otherwise,
…3.1 the symbol is an operator.
…3.2 Pop the top 2 values from the stack.
…3.3 Put the operator, with the values as arguments and form a string.
…3.4 Push the resulted string back to stack.
4.If there is only one value in the stack
…4.1 That value in the stack is the desired infix string.
3. Solving postfix expression
The Postfix notation is used to represent algebraic expressions. The expressions
written in postfix form are evaluated faster compared to infix notation as parenthesis
are not required in postfix.
Following is algorithm for evaluation postfix expressions.
1) Create a stack to store operands (or values).
2) Scan the given expression and do following for every scanned element.
…..a) If the element is a number, push it into the stack
…..b) If the element is a operator, pop operands for the operator from stack. Evaluate
the operator and push the result back to the stack
3) When the expression is ended, the number in the stack is the final answer
“2 3 1 * + 9 -“
abc-+de-fg-h+/*
Expression Stack
abc-+de-fg-h+/* NuLL
bc-+de-fg-h+/* "a"
c-+de-fg-h+/* "b"
"a"
"c"
-+de-fg-h+/*
"b"
"a"
e-fg-h+/* "d"
"a+b-c"
"e"
-fg-h+/*
"d"
"a+b-c"
"f"
g-h+/*
"d - e"
"a+b-c"
"g"
-h+/* "f"
"d - e"
"a+b-c"
"f-g"
h+/*
"d - e"
"a+b-c"
"h"
+/* "f-g"
"d - e"
"a+b-c"
"f-g+h"
/*
"d - e"
"a+b-c"
* "(d-e)/(f-g-h)"
"a+b-c"
Null
"(a+b-c)*(d-e)/(f-g+h)"
Ans = (a+b-c)*(d-e)/(f-g+h)
{
// If the scanned character is an operand (number here),
// push it to the stack.
if (isdigit(exp[i]))
push(stack, exp[i] - '0');
4. Infix to Prefix
Input : A * B + C / D
Output : + * A B/ C D
5. Prefix to Infix
6. Prefix to Postfix
Input : Prefix : *+AB-CD
Output : Postfix : AB+CD-*
Explanation : Prefix to Infix : (A+B) * (C-D)
Infix to Postfix : AB+CD-*
7. Postfix to Prefix
Input : Postfix : AB+CD-*
Output : Prefix : *+AB-CD
Explanation : Postfix to Infix : (A+B) * (C-D)
Infix to Prefix : *+AB-CD
(A + B) * C *+ABC AB+C*
(A + B) * (C + D) *+AB+CD AB+CD+*
The major rules to remember during the conversion process are that the operations with highest precedence
are converted first and that after a portion of an expression has been converted to postfix, it is to be treated
as a single operand. Let us now consider the same example with the precedence of operators reversed by
the deliberate insertion of parentheses.
Note that in the conversion from AB + * C to AB + C *, AB+ was treated as a single operand. The rules
for converting from infix to postfix are simple, provided that you know the order of precedence.
We consider five binary operations: addition, subtraction, multiplication, division, and exponentiation.
These operations are denoted by the usual operators, +, –, *, /, and ^, respectively. There are three levels of
operator precedence. Both * and / have higher precedence than + and –. ^ has higher precedence than *
and /. Furthermore, when operators of the same precedence are scanned, +, –, * and / are left associative,
but ^ is right associative. Parentheses may be used in infix expressions to override the default precedence.
The postfix form requires no parentheses. The order of the operators in the postfix expressions determines
the actual order of operations in evaluating the expression, making the use of parentheses unnecessary.
Input
A collection of error-free simple arithmetic expressions. Expressions are presented one per line. The input
has an arbitrary number of blanks between any two symbols. A symbol may be a letter (A – Z), an operator
(+, – , *, or /), a left parenthesis, or a right parenthesis. Each operand is composed of a single letter. The
input expressions are in infix notation.
Example
A + B – C
A + B * C
(A + B) / (C – D)
( ( A + B ) * ( C – D ) + E ) / (F + G)
Output
Your output will consist of the input expression, followed by its corresponding postfix expression. All
output (including the original infix expression) must be clearly formatted (or reformatted) and also clearly
labeled.
Example
(Only the four postfix expressions corresponding to the above sample input are shown here.)
AB+C–
ABC*+
AB+CD-/
AB+CD-*E+FG+/
Discussion
In converting infix expressions to postfix notation, the following fact should be taken into consideration: In
infix form, the order of applying operators is governed by the possible appearance of parentheses and the
operator precedence relations; however, in postfix form, the order is simply the “natural” order – i.e., the
order of appearance from left to right.
Accordingly, subexpressions within innermost parentheses must first be converted to postfix, so that they
can then be treated as single operands. In this fashion, parentheses can be successively eliminated until the
entire expression has been converted. The last pair of parentheses to be opened within a group of nested
parentheses encloses the first subexpression within the group to be transformed. This last-in, first-out
behavior should immediately suggest the use of a stack.
Your program should utilize the basic stack methods. You will need to PUSH certain symbols on the stack,
POP symbols, test to see if the stack is EMPTY, look at the TOP element of the stack, etc.
In addition, you must devise a boolean method that takes two operators and tells you which has higher
precedence. This will be helpful, because in Rule 3 below, you need to compare the next symbol to the one
on the top of the stack. [Question: what precedence do you assign to ‘(‘? You need to answer this question
since ‘(‘ may be on top of the stack.]
You should formulate the conversion algorithm using the following six rules:
1. Scan the input string (infix notation) from left to right. One pass is sufficient.
2. If the next symbol scanned is an operand, it may be immediately appended to the postfix string.
3. If the next symbol is an operator,
i. Pop and append to the postfix string every operator on the stack that
a. is above the most recently scanned left parenthesis, and
b. has precedence higher than or is a right-associative operator of equal precedence to that of the
new operator symbol.
ii. Push the new operator onto the stack.
4. When a left parenthesis is seen, it must be pushed onto the stack.
5. When a right parenthesis is seen, all operators down to the most recently scanned left parenthesis must
be popped and appended to the postfix string. Furthermore, this pair of parentheses must be discarded.
6. When the infix string is completely scanned, the stack may still contain some operators. [Why are there
no parentheses on the stack at this point?] All the remaining operators should be popped and appended
to the postfix string.
Examples
Here are two examples to help you understand how the algorithm works. Each line below demonstrates the
state of the postfix string and the stack when the corresponding next infix symbol is scanned. The
rightmost symbol of the stack is the top symbol. The rule number corresponding to each line demonstrates
which of the six rules was used to reach the current state from that of the previous line.
Example 1
Input expression: A + B * C / D - E
Next Symbol Postfix String Stack Rule
A A 2
+ A + 3
B AB + 2
* AB +* 3
C ABC +* 2
/ ABC* +/ 3
D ABC*D +/ 2
- ABC*D/+ - 3
E ABC*D/+E - 2
ABC*D/+E- 6
Example 2
Input expression: ( A + B * ( C - D ) ) / E.
• Done!
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack:
• Output:
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: (
• Output:
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: (
• Output: 300
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: ( +
• Output: 300
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: ( +
• Output: 300 23
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: (
• Output: 300 23 +
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack:
• Output: 300 23 +
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: *
• Output: 300 23 +
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: * (
• Output: 300 23 +
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: * (
• Output: 300 23 + 43
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: * ( -
• Output: 300 23 + 43
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: * ( -
• Output: 300 23 + 43 21
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: * (
• Output: 300 23 + 43 21 -
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: *
• Output: 300 23 + 43 21 -
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack:
• Output: 300 23 + 43 21 - *
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: /
• Output: 300 23 + 43 21 - *
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: / (
• Output: 300 23 + 43 21 - *
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: / (
• Output: 300 23 + 43 21 - * 84
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: / ( +
• Output: 300 23 + 43 21 - * 84
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: / ( +
• Output: 300 23 + 43 21 - * 84 7
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: / (
• Output: 300 23 + 43 21 - * 84 7 +
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack: /
• Output: 300 23 + 43 21 - * 84 7 +
Infix to Postfix Conversion
Example 2
• (300+23)*(43-21)/(84+7)
• Stack:
• Output: 300 23 + 43 21 - * 84 7 + /
• Done!
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack:
• Output:
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: (
• Output:
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: (
• Output: 4
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: ( +
• Output: 4
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: ( +
• Output: 4 8
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: (
• Output: 4 8 +
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack:
• Output: 4 8 +
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: *
• Output: 4 8 +
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: * (
• Output: 4 8 +
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: * (
• Output: 4 8 + 6
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: * ( -
• Output: 4 8 + 6
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: * ( -
• Output: 4 8 + 6 5
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: * (
• Output: 4 8 + 6 5 -
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: *
• Output: 4 8 + 6 5 -
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack:
• Output: 4 8 + 6 5 - *
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: /
• Output: 4 8 + 6 5 - *
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / (
• Output: 4 8 + 6 5 - *
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( (
• Output: 4 8 + 6 5 - *
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( (
• Output: 4 8 + 6 5 - * 3
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( ( -
• Output: 4 8 + 6 5 - * 3
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( ( -
• Output: 4 8 + 6 5 - * 3 2
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( (
• Output: 4 8 + 6 5 - * 3 2 -
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / (
• Output: 4 8 + 6 5 - * 3 2 -
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( *
• Output: 4 8 + 6 5 - * 3 2 -
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( * (
• Output: 4 8 + 6 5 - * 3 2 -
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( * (
• Output: 4 8 + 6 5 - * 3 2 - 2
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( * ( +
• Output: 4 8 + 6 5 - * 3 2 - 2
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( * ( +
• Output: 4 8 + 6 5 - * 3 2 – 2 2
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( * (
• Output: 4 8 + 6 5 - * 3 2 – 2 2 +
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / ( *
• Output: 4 8 + 6 5 - * 3 2 – 2 2 +
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: / (
• Output: 4 8 + 6 5 - * 3 2 – 2 2 + *
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack: /
• Output: 4 8 + 6 5 - * 3 2 – 2 2 + *
Infix to Postfix Conversion
Example 3
• (4+8)*(6-5)/((3-2)*(2+2))
• Stack:
• Output: 4 8 + 6 5 - * 3 2 – 2 2 + * /
• Done!
Balanced Parentheses
(5+6)∗(7+8)/(4+3) balanced
2 * ( ( 4/2 ) + 5 not balanced
((()[]()())
{((()))}
(()((())()))
Algorithm:
Declare a character stack S.
Now traverse the expression string exp.
1. If the current character is a starting bracket (‘(‘ or ‘{‘ or ‘[‘) then push it
to stack.
2. If the current character is a closing bracket (‘)’ or ‘}’ or ‘]’) then pop
from stack and if the popped character is the matching starting bracket
then fine else brackets are not balanced.
After complete traversal, if there is some starting bracket left in stack then
“not balanced”
Parenthesis checking
switch (expr[i])
{
case ')':
case '}':
case ']':
if (areParanthesisBalanced(expr))
cout << "Balanced";
else
cout << "Not Balanced";
return 0;
}
#include<stdio.h>
char stack[20];
int top = -1;
void push(char x)
{
stack[++top] = x;
}
char pop()
{
if(top == -1)
return -1;
else
return stack[top--];
}
int priority(char x)
{
if(x == '(')
return 0;
if(x == '+' || x == '-')
return 1;
if(x == '*' || x == '/')
return 2;
}
main()
{
char exp[20];
char *e, x;
printf("Enter the expression :: ");
scanf("%s",exp);
e = exp;
while(*e != '\0')
{
if(isalnum(*e))
printf("%c",*e);
else if(*e == '(')
push(*e);
else if(*e == ')')
{
while((x = pop()) != '(')
printf("%c", x);
}
else
{
while(priority(stack[top]) >= priority(*e))
printf("%c",pop());
push(*e);
}
e++;
}
while(top != -1)
{
printf("%c",pop());
}
}
Enter the expression :: a+b*c
abc*+
Enter the expression :: (a+b)*c+(d-a)
ab+c*da-+
// length of expression
int length = pre_exp.size();
// if symbol is an operand
else {
// Driver Code
int main() {
string pre_exp = "*-A/BC-/AKL";
cout << "Infix : " << preToInfix(pre_exp);
return 0;
}
Run on IDE
Output:
Infix : ((A-(B/C))*((A/K)-L))
Input : Prefix : *+AB-CD
Output : Infix : ((A+B)*(C-D))
Algorithm
1. Scan the infix expression from left to right.
2. If the scanned character is an operand, output it.
3. Else,
…..3.1 If the precedence of the scanned operator is greater than the precedence of the operator in
the stack(or the stack is empty), push it.
…..3.2 Else, Pop the operator from the stack until the precedence of the scanned operator is less-
equal to the precedence of the operator residing on the top of the stack. Push the scanned operator
to the stack.
4. If the scanned character is an ‘(‘, push it to the stack.
5. If the scanned character is an ‘)’, pop and output from the stack until an ‘(‘ is encountered.
6. Repeat steps 2-6 until infix expression is scanned.
7. Pop and output from the stack until it is not empty.
Prefix : An expression is called the prefix expression if the operator appears in the expression
before the operands. Simply of the form (operator operand1 operand2).
Example : *+AB-CD (Infix : (A+B) * (C-D) )
Input : Prefix : *-A/BC-/AKL
Output : Infix : ((A-(B/C))*((A/K)-L))
Postfix to Infix
Algorithm
1.While there are input symbol left
…1.1 Read the next symbol from the input.
2.If the symbol is an operand
…2.1 Push it onto the stack.
3.Otherwise,
…3.1 the symbol is an operator.
…3.2 Pop the top 2 values from the stack.
…3.3 Put the operator, with the values as arguments and form a string.
…3.4 Push the resulted string back to stack.
4.If there is only one value in the stack
…4.1 That value in the stack is the desired infix string.
- Shital Dongre
- Assistant Professor
- VIT, Pune.
What is a Queue?
Linear data structure
Queue Principle: FIRST IN FIRST OUT
Access set of elements in FIFO order
It means the first element inserted is the
first one to be removed
Ex- waiting queue for bus-first one in
line is the first one to get entered in bus
FIRST IN FIRST OUT
Applications of Queue
Job scheduling
Resource scheduling
int queue[MAX_QUEUE_SIZE]
void isEmpty()
{
if(front==rear)
{
printf("\n Queue is empty");
}
}
int isFull()
{
if(rear== (MAX_QUEUE_SIZE-1))
printf("\n Queue is Full");
}
void EnQueue( int num)
{
if(rear== (MAX_QUEUE_SIZE-1))
printf("\n Queue is Full");
else
{
rear = rear + 1;
Queue[rear] = num;
}
}
void DeQueue()
{
if(front==rear)
{
printf("\n Queue is empty");
}
else
{
front ++;
printf("\n Deleted Element is %d", queue[front]);
}
}
void Q_Display
{
printf("\n Queue Elements are:\n ");
if(front==rear)
printf("\n Queue is Empty");
else
{
for(i=(front+1); i<=rear; i++)
{
printf(“\n%d",queue[i]);
}
}
First In First Out
5 rear
4 rear 4
3 rear 3 3
2 rear
2 2
1 2
1 rear 1 1 front
front =-1 front =-1 front =-1 front =-1
Queue using Linked list
struct Node
{
int data;
struct Node *next;
};
struct Queue
{
struct Node *front, *rear;
};
struct Queue *createQ()
{
struct Queue *Q = (struct
Queue*)malloc(sizeof(struct Queue));
if (Q->rear == NULL)
{
Q->front = Q->rear = temp;
return;
}
Q->rear->next = temp;
Q->rear = temp;
}
struct QNode *deQueue(struct Queue *Q)
{
if (Q ->front == NULL)
return NULL;
if (Q ->front == NULL)
Q ->rear = NULL;
return temp;
}
void display(struct Queue *Q)
{
struct Node *temp = Q->front;
while(temp!=NULL)
{
printf(“Queue element= %d”, temp->data);
temp=temp->next;
}
Queue
9/14/2020
09/10/08 S P Dongre, VIT, Pune 1
Queue (Linear Queue)
• It is a linear data structure consisting of list of items.
• In queue, data elements are added at one end, called the rear and removed from another
end, called the front of the list.
• Two basic operations are associated with queue:
7
1. “Insert” operation is used to insert an element into a queue.
2. “Delete” operation is used to delete an element from a queue. 6
● FIFO list
• Example: EEE 5
Queue: AAA, BBB, CCC, DDD, EEE
1 2 3 4 5 6 7 DDD 4
BBB 2
Front Rear
AAA 1
Rear Front
9/14/2020
09/10/08 S P Dongre, VIT, Pune 2
Algorithms for Insert and Delete Operations in Linear Queue
For Insert Operation
Insert-Queue(Queue, Rear, Front, N, Item)
Here, Queue is the place where to store data. Rear represents the location in which the
data element is to be inserted and Front represents the location from which the data
element is to be removed. Here N is the maximum size of the Queue and finally, Item is
the new item to be added.
4. Return.
9/14/2020
09/10/08 S P Dongre, VIT, Pune 3
For Delete Operation
Delete-Queue(Queue, Front, Rear, Item)
Here, Queue is the place where data are stored. Rear represents the location in which the
data element is to be inserted and Front represents the location from which the data
element is to be removed. Front element is assigned to Item.
4. Return.
9/14/2020
09/10/08 S P Dongre, VIT, Pune 4
Example: Consider the following queue (linear queue).
Rear = 4 and Front = 1 and N = 7
10 50 30 40
1 2 3 4 5 6 7
(1) Insert 20. Now Rear = 5 and Front = 1
10 50 30 40 20
1 2 3 4 5 6 7
(2) Delete Front Element. Now Rear = 5 and Front = 2
50 30 40 20
1 2 3 4 5 6 7
(3) Delete Front Element. Now Rear = 5 and Front = 3
30 40 20
1 2 3 4 5 6 7
(4) Insert 60. Now Rear = 6 and Front = 3
30 40 20 60
1 2 3 4 5 6 7
9/14/2020
09/10/08 S P Dongre, VIT, Pune 5
Drawback of Linear Queue
• Once the queue is full, even though few elements from the front are deleted and
some occupied space is relieved, it is not possible to add anymore new elements,
as the rear has already reached the Queue’s rear most position.
Circular Queue
• This queue is not linear but circular.
"Rear" most element, if and only if the "Front" Figure: Circular Queue having
Rear = 5 and Front = 0
has moved forward. otherwise it will again be
9/14/2020
09/10/08 S P Dongre, VIT, Pune 6
Algorithms for Insert and Delete Operations in Circular Queue
For Insert Operation
Insert-Circular-Q(CQueue, Rear, Front, N, Item)
Here, CQueue is a circular queue where to store data. Rear represents the
location in which the data element is to be inserted and Front represents the
location from which the data element is to be removed. Here N is the maximum
size of CQueue and finally, Item is the new item to be added. Initailly Rear = 0 and
Front = 0.
1. If Front = 0 and Rear = 0 then Set Front := 1 and go to step 4.
2. If Front =1 and Rear = N or Front = Rear + 1
then Print: “Circular Queue Overflow” and Return.
3. If Rear = N then Set Rear := 1 and go to step 5.
4. Set Rear := Rear + 1
5. Set CQueue [Rear] := Item.
6. Return
9/14/2020
09/10/08 S P Dongre, VIT, Pune 7
For Delete Operation
Delete-Circular-Q(CQueue, Front, Rear, Item)
Here, CQueue is the place where data are stored. Rear represents the location in
which the data element is to be inserted and Front represents the location from
which the data element is to be removed. Front element is assigned to Item.
Initially, Front = 1.
1. If Front = 0 then
Print: “Circular Queue Underflow” and Return. /*..Delete without Insertion
9/14/2020
09/10/08 S P Dongre, VIT, Pune 8
Example: Consider the following circular queue with N = 5.
1. Initially, Rear = 0, Front = 0. 4. Insert 20, Rear = 3, Front = 0.
Front
Rear
Rear
3. Insert 50, Rear = 2, Front = 1. 6. Delete front, Rear = 4, Front = 2.
Front Rear Front
Rear
9/14/2020
09/10/08 S P Dongre, VIT, Pune 9
7. Insert 100, Rear = 5, Front = 2. 10. Delete front, Rear = 1, Front = 3.
Rear
Front
Front
Rear
Front
Rear Front
Front
9/14/2020
09/10/08 S P Dongre, VIT, Pune 10
Types of Queues in Data Structure
Circular Queue
In a circular queue, the last node is connected to the first node.
Circular queue is also called as Ring Buffer.
Insertion in a circular queue happens at the FRONT and deletion at
the END of the queue.
9/14/2020
09/10/08 S P Dongre, VIT, Pune 17
By
Shital Dongre
Asst. Prof.
IT & MCA Dept,
VIT, Pune
linked list is a linear data structure
elements are not stored at contiguous memory
locations.
elements in a linked list are linked using pointers
linked list consists of nodes where each node
contains a data field and a reference(link) to the
next node in the list.
Arrays can be used to store linear data of similar
types, but arrays have following limitations.
1) The size of the arrays is fixed: So we must know
the upper limit on the number of elements in
advance. Also, generally, the allocated memory is
equal to the upper limit irrespective of the usage.
2) Inserting a new element in an array of elements
is expensive, because room has to be created for
the new elements and to create room existing
elements have to shifted.
Advantages over arrays
1) Dynamic size
2) Ease of insertion/deletion
Drawbacks:
1) Random access is not allowed. We have to
access elements sequentially starting from the
first node. So we cannot do binary search with
linked lists.
2) Extra memory space for a pointer is required
with each element of the list.
3) Not cache friendly. Since array elements are
contiguous locations, there is locality of
reference which is not there in case of linked
lists.
A linked list is represented by a pointer to the
first node of the linked list. The first node is
called head. If the linked list is empty, then
value of head is NULL.
Each node in a list consists of at least two
parts:
1) data
2) Pointer (Or Reference) to the next node
// A linked list node
Node
struct Node
{
int data; A
struct Node *next;
data pointer
};
struct Node
{
int data;
struct Node *next;
};
int main()
{
struct Node* head = NULL;
struct Node* second = NULL;
struct Node* third = NULL;
second->data = 2;
second->next = third;
third->data = 3;
third->next = NULL;
return 0;
}
Linked list traversal
/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL)
{
*head_ref = new_node;
return;
}
}
/* Counts no. of nodes in linked list */
int getCount(struct Node* head)
{
int count = 0; // Initialize count
struct Node* current = head; // Initialize current
while (current != NULL)
{
count++;
current = current->next;
}
return count;
}
From front
From matching key
Last
void deleteNode(struct Node *head, int key)
{
struct Node* temp = *head, *prev;
if (temp != NULL && temp->data == key)
{ *head = temp->next;
free(temp);
return;
}
while (temp != NULL && temp->data != key)
{
prev = temp;
temp = temp->next;
}
if (temp == NULL)
return;
prev->next = temp->next;
free(temp);
}
Delete entire linked list
void deleteList(struct Node* head)
{
/* deref head_ref to get the real head */
struct Node* current = *head;
struct Node* next_node;
3 7 2 5 9 0 NU
C=A+B LL
struct Node
{
int coef;
int expo;
struct Node *next;
};
A Doubly Linked List (DLL) contains an extra
pointer, typically called previous pointer,
together with next pointer and data which are
there in singly linked list.
return;
}
void insertBefore(struct Node* next_node, int new_data)
{
/*1. check if the given new_node is NULL */
if (next_node == NULL) {
printf("the given next node cannot be NULL");
return;
}
struct Node {
int data;
struct Node* next;
struct Node* prev;
};
void push(struct Node** head_ref, int new_data)
{
/* allocate node */
struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
Let K1 be the Kth node from beginning and K2 be Kth node from ending. Then:
The previous node to K2 has to be changed to the previous node of K1.
The next node to K2 has to be changed to the next node of K1.
The previous node to K1 has to be changed to the previous node of K2.
The next node to K1 has to be changed to the next node of K2.
delAllOccurOfGivenKey(head_ref, x)
if head_ref == NULL
return
Initialize current = head_ref
Declare next
while current != NULL
if current->data == x
next = current->next
deleteNode(head_ref, current)
current = next
else
current = current->next
5.Delete all the nodes from the doubly
linked list that are greater than a given
value
6.Delete all the even nodes from a Doubly
Linked List
void deleteEvenNodes(Node** head_ref)
{
Node* ptr = *head_ref;
Node* next;
while (ptr != NULL) {
next = ptr->next;
// if true, delete node 'ptr'
if (ptr->data % 2 == 0)
deleteNode(head_ref, ptr);
ptr = next;
}
}
void deleteNode(Node** head_ref, Node* del)
{
// base case
if (*head_ref == NULL || del == NULL)
return;
return;
}
7.
8.
9.
10.
11.
12.
13.
14.
15.
Reverse a Doubly Linked List
void reverse(struct Node **head_ref)
{
struct Node *temp = NULL;
struct Node *current = *head_ref;
123
Circular Linked List
Circular Linked Lists
⚫ In linear linked lists if a list is traversed (all
the elements visited) an external pointer to
the list must be preserved in order to be able
to reference the list again.
⚫ Circular linked lists can be used to help the
traverse the same list again and again if
needed. A circular list is very similar to the
linear list where in the circular list the pointer
of the last node points not NULL but the first
node.
Circular Linked Lists
b) Assume that the list pointer points the header with the sentinel account number -99.
struct node{
int flag;
float info;
struct node *next;
};
typedef struct node *NODEPTR;
float avList(NODEPTR *plist)/*assume that plist points the
header node*/
{
int count=0;
float sum =0.0;
NODEPTR p;
p=*plist;
if((p == NULL)){
printf(“Empty list\n”);
exit(1);
}
do{
sum=sum + p->info;
p =p->next;
count++;
}while(p->flag !=1);
return sum/count;
}
Circular
Linked List
COMP104 Circular Linked List / Slide 2
10 20 40 55 70
Rear
COMP104 Circular Linked List / Slide 3
Motivation
struct Node{
int data;
Node* next;
};
typedef Node* NodePtr;
COMP104 Circular Linked List / Slide 5
print(NodePtr Rear)
//print the Circular Linked List once
COMP104 Circular Linked List / Slide 6
Rear
COMP104 Circular Linked List / Slide 7
Insert Node
Insert into an empty list
Rear = New;
Rear->next = Rear;
10
New Rear
COMP104 Circular Linked List / Slide 8
10 20 40 55 70
10 20 55 70
40
Prev Cur Rear
New
COMP104 Circular Linked List / Slide 10
10 20 40 55 70
10
Delete Node
Delete the head node from a Circular Linked List
Prev->next = Cur->next; // same as: Rear->next = Cur->next
delete Cur;
10 20 40 55 70
Rear Prev
Cur
COMP104 Circular Linked List / Slide 14
10 20 40 55 70
delete Cur;
Rear = Prev;
10 20 40 55 70
Prev Cur
Rear
void deleteNode(NodePtr& Rear, int item){
NodePtr Cur, Prev;
if(Rear == NULL){
cout << "Trying to delete empty list" << endl;
return;
}
Prev = Rear;
Cur = Rear->next;
do{ // find Prev and Cur
if(item <= Cur->data) break;
Prev = Cur;
Cur = Cur->next;
}while(Cur != Rear->next);
if(Cur->data != item){ // data does not exist
cout << "Data Not Found" << endl;
return;
}
if(Cur == Prev){ // delete single-node list
Rear = NULL;
delete Cur;
return;
}
if(Cur == Rear) // revise Rear pointer if deleting end
Rear = Prev;
Prev->next = Cur->next; // revise pointers
delete Cur;
}
void main(){
NodePtr Rear = NULL;
insertNode(Rear, 3);
insertNode(Rear, 1);
insertNode(Rear, 7);
insertNode(Rear, 5);
insertNode(Rear, 8); Result is:
print(Rear); 13578
deleteNode(Rear, 1); 57
deleteNode(Rear, 3);
1578
deleteNode(Rear, 8);
print(Rear);
insertNode(Rear, 1);
insertNode(Rear, 8);
print(Rear);
}
Generalized Linked list
• A Generalized Linked List L, is defined as a
finite sequence of n>=0 elements, l1, l2, l3, l4,
…, ln, such that li are either atom or the list of
atoms. Thus
L = (l1, l2, l3, l4, …, ln)
where n is total number of nodes in the list.
To represent a list of items there are certain
assumptions about the node structure.
• Flag = 1 implies that down pointer exists
• Data means the atom
• Down pointer is the address of node which is
down of the current node
• Next pointer is the address of node which is
attached as the next node
• Why Generalized Linked List?
Generalized linked lists are used because
although the efficiency of polynomial
operations using linked list is good but still,
the disadvantage is that the linked list is
unable to use multiple variable polynomial
equation efficiently. It helps us to represent
multi-variable polynomial along with the list
of elements.
typedef struct node {
char c; //Data
int index; //Flag
struct node *next, *down; //Next & Down
pointer
}GLL;
• ( a, (b, c), d)
• Polynomial Representation using Generalized
Linked List
The typical node structure will be:
9x5 + 7x4y + 10xz
struct Person
{
char name[50];
int citNo;
float salary;
};
int main()
{
struct Point p1;
printf(“%d”,sizeof(p1));
}
#include <stdio.h>
/* Created a structure here. The name of the structure is
* StudentData.
*/
struct StudentData{
char *stu_name;
int stu_id;
int stu_age;
};
int main()
{
/* student is the variable of structure StudentData*/
struct StudentData student;
#include <stdio.h>
struct Distance
{
int feet;
float inch;
} dist1, dist2, sum;
int main()
{
printf("1st distance\n");
printf("Enter feet: ");
scanf("%d", &dist1.feet);
// adding feet
sum.feet = dist1.feet + dist2.feet;
// adding inches
sum.inch = dist1.inch + dist2.inch;
struct Distance {
int feet;
float inch;
} d1, d2, result;
int main() {
// take first distance input
printf("Enter 1st distance\n");
printf("Enter feet: ");
scanf("%d", &d1.feet);
printf("Enter inch: ");
scanf("%f", &d1.inch);
// adding distances
result.feet = d1.feet + d2.feet;
result.inch = d1.inch + d2.inch;
Output
#include <stdio.h>
typedef struct complex {
float real;
float imag;
} complex;
/*struct complex {
float real;
float imag;
};*/
complex add(complex n1, complex n2);
int main() {
complex n1, n2, result;
Output
Keyword typedef
We use the typedef keyword to create an alias name for data types. It is
commonly used with structures to simplify the syntax of declaring variables.
This code
struct Distance{
int feet;
float inch;
};
int main() {
struct Distance d1, d2;
}
is equivalent to
typedef struct Distance{
int feet;
float inch;
} distances;
int main() {
distances d1, d2;
}
int main()
{
// A valid initialization. member x gets value 0 and y
// gets value 1. The order of declaration is followed.
struct Point p1 = {0, 1};
}
struct Point
{
int x, y;
};
int main()
{
struct Point p1 = {0, 1};
Limitations of C Structures
In C language, Structures provide a method for packing together data of
different types. A Structure is a helpful tool to handle a group of logically
related data items. However, C structures have some limitations.
The C structure does not allow the struct data type to be treated like built-in data types:
We cannot use operators like +,- etc. on Structure variables.
No Data Hiding: C Structures do not permit data hiding. Structure members can be
accessed by any function, anywhere in the scope of the Structure.
Functions inside Structure: C structures do not permit functions inside Structure
Static Members: C Structures cannot have static members inside their body
Access Modifiers: C Programming language do not support access modifiers. So they
cannot be used in C Structures.
Construction creation in Structure: Structures in C cannot have constructor inside
Structures.
struct Point
{
int x, y, z;
};
int main()
{
// Examples of initialization using designated initialization
struct Point p1 = {.y = 0, .z = 1, .x = 2};
struct Point p2 = {.x = 20};
struct Point
{
int x, y;
};
int main()
{
// Create an array of structures
struct Point arr[10];
#include <stdio.h>
struct student {
char firstName[50];
int roll;
float marks;
} s[10];
int main() {
int i;
printf("Enter information of students:\n");
// storing information
for (i = 0; i < 5; ++i) {
s[i].roll = i + 1;
printf("\nFor roll number%d,\n", s[i].roll);
printf("Enter first name: ");
scanf("%s", s[i].firstName);
printf("Enter marks: ");
scanf("%f", &s[i].marks);
}
printf("Displaying Information:\n\n");
// displaying information
for (i = 0; i < 5; ++i) {
printf("\nRoll number: %d\n", i + 1);
printf("First name: ");
puts(s[i].firstName);
printf("Marks: %.1f", s[i].marks);
printf("\n");
}
return 0;
}
Output
Roll number: 1
Name: Tom
Marks: 98
.
.
.
struct Point
{
int x, y;
};
int main()
{
struct Point p1 = {1, 2};
// p2 is a pointer to structure p1
struct Point *p2 ;
p2= &p1;
Output:
1 2
#include <stdio.h>
struct person
{
int age;
float weight;
};
int main()
{
struct person *personPtr, person1;
personPtr = &person1;
printf("Displaying:\n");
printf("Age: %d\n", personPtr->age);
printf("weight: %f", personPtr->weight);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
struct person {
int age;
float weight;
char name[30];
};
int main()
{
struct person *ptr;
int i, n;
printf("Displaying Information:\n");
for(i = 0; i < n; ++i)
printf("Name: %s\tAge: %d\n", (ptr+i)->name, (ptr+i)->age);
return 0;
}
int main() {
struct course *ptr;
int noOfRecords;
printf("Enter the number of records: ");
scanf("%d", &noOfRecords);
printf("Displaying Information:\n");
for (int i = 0; i < noOfRecords; ++i) {
printf("%s\t%d\n", (ptr + i)->subject, (ptr + i)->marks);
}
free(ptr);
return 0;
}
Output
Nested Structures
struct complex
int imag;
float real;
};
struct number
int integers;
} num1, num2;
Suppose, you want to set imag of num2 variable to 11. Here's how you can
do it:
num2.comp.imag = 11;
#include <stdio.h>
struct TIME {
int seconds;
int minutes;
int hours;
};
int main() {
struct TIME startTime, stopTime, diff;
printf("Enter the start time. \n");
printf("Enter hours, minutes and seconds: ");
scanf("%d %d %d", &startTime.hours,
&startTime.minutes,
&startTime.seconds);
Output
8
More terminology
Node A is the parent of node B if node B is a child of A
Node A is an ancestor of node B if A is a parent of B, or
if some child of A is an ancestor of B
In less formal terms, A is an ancestor of B if B is a child
of A, or a child of a child of A, or a child of a child of a
child of A, etc.
Node B is a descendant of A if A is an ancestor of B
Nodes A and B are siblings if they have the same
parent
9
More Properties of Trees
depth: the path length from the root of the tree to this node
height of a node: The maximum distance (path length) of
any leaf from this node
a leaf has a height of 0
the height of a tree is the height of the root of that tree
descendants: any nodes that can be reached via 1 or more
edges from this node
ancestors: any nodes for which this node is a descendant
10
Tree
Length of a path =
number of edges
A
Depth of a node N
= length of path
from root to N
B C D
Height of node N =
length of longest
path from N to a
E F
leaf
Level- level of root
is 0
11
The maximum number of nodes at level ‘l’ of a
binary tree is 2l.
For root, l = 0, number of nodes = 20 = 1
Assume that maximum number of nodes on level ‘l’ is 2l
Height of Trees
Trees come in many shapes
15
Examples of Binary Trees
Expression tree
Non-leaf (internal) nodes contain operators
Leaf nodes contain operands
Huffman tree
Represents Huffman codes for characters appearing in a file or
stream
Huffman code may use different numbers of bits to encode
different characters
ASCII or Unicode uses a fixed number of bits for each character
Chapter 8: Trees 16
Examples of Binary Trees (2)
Chapter 8: Trees 17
Examples of Binary Trees (3)
Chapter 8: Trees 18
Fullness and Completeness
(In computer science) trees grow from the top down
New values inserted in new leaf nodes
A binary tree is full if all leaves are at the same level
In a full tree, every node has 0 or 2 non-null children
Chapter 8: Trees 19
Full Binary Tree
If the height is h>0
The number of leaves is 2^(h-1)
The number of nodes is 2^h – 1
If the number of nodes is N>0
The height is log2(N+1)
The number of leaves is (N+1)/2
Fullness and Completeness (2)
A binary tree is complete if:
All leaves are at level h or level h-1 (for some h)
All level h-1 leaves are to the right
Chapter 8: Trees 21
Balanced Binary Trees
Balanced binary tree
The height of any node’s right subtree differs from the
height of the node’s left subtree by no more than 1
A complete binary tree is balanced
Unbalanced Trees
Some trees can be unbalanced.
They have most of their nodes on one side of the
root or the other. Individual subtrees may also be
unbalanced.
Trees become unbalanced because of the order in
which the data items are inserted.
If the key values are inserted in ascending or
descending order the tree will be unbalanced.
For search-centric application (Binary tree), an
unbalanced tree must be re-balanced.
Array-Based Implementation of a
Complete Binary Tree
• If nodes numbered
according to a level-by-
level scheme
– Root index: 0
– Given any node tree[i]
• Left child index: 2*i+1
• Right chile index: 2*i+2
• Parent index: (i-1)/2
Pointer-Based Implementation
Traversals of Binary Trees
Often want iterate over and process nodes of a tree
Can walk the tree and visit the nodes in order
This process is called tree traversal
Three kinds of binary tree traversal:
Preorder(Root, Left, Right)
Inorder(Left, Root, Right)
Postorder(Left, Right, Root)
According to order of subtree root w.r.t. its children
Chapter 8: Trees 27
(a) Inorder (Left, Root, Right) : 4 2 5 1 3
(b) Preorder (Root, Left, Right) : 1 2 4 5 3
(c) Postorder (Left, Right, Root) : 4 5 2 3 1
struct node
{
int data;
struct node* left;
struct node* right;
};
struct node* newNode(int data)
{
struct node* node = (struct node*)
malloc(sizeof(struct node));
node->data = data;
node->left = NULL;
node->right = NULL;
return(node);
}
void printInorder(struct node* node)
{
if (node == NULL)
return;
printInorder(node->left);
printInorder(node->right);
}
void printPreorder(struct node* node)
{
if (node == NULL)
return;
printPreorder(node->left);
printPreorder(node->right);
}
void printPostorder(struct node* node)
{
if (node == NULL)
return;
printPostorder(node->left);
printPostorder(node->right);
while (!done)
{
if(current != NULL)
{
push(&s, current);
current = current->left;
}
else
{
if (!isEmpty(s))
{
current = pop(&s);
printf("%d ", current->data);
current = current->right;
}
else
done = 1;
}
} /* end of while */
}
Construct tree
Construct Tree from given Inorder and
Preorder traversals
Inorder sequence: D B E A F C
Preorder sequence: A B D E C F
• Example
buildTree()
1) Pick an element from Preorder. Increment a Preorder Index
Variable (preIndex in below code) to pick next element in next
recursive call.
2) Create a new tree node tNode with the data as picked
element.
3) Find the picked element’s index in Inorder. Let the index be
inIndex.
4) Call buildTree for elements before inIndex and make the built
tree as left subtree of tNode.
5) Call buildTree for elements after inIndex and make the built
tree as right subtree of tNode.
6) return tNode.
Construct a Binary Tree from Postorder
and Inorder
Process of constructing tree from in[] = {4, 8, 2, 5, 1, 6, 3,
7} and post[] = {8, 4, 5, 2, 6, 7, 3, 1}
pre[] = {1, 2, 4, 8, 9, 5, 3, 6, 7}
post[] = {8, 9, 4, 5, 2, 6, 7, 3, 1}
1 3 4 6 7 8 10 13 14
Traversal
void preorder(struct node *root)
{
if (root != NULL)
printf("%d \n", root->key);
preorder(root->left);
preorder(root->right);
}
8 3 1 6 4 7 10 14 13
Traversal
void postorder(struct node *root)
{
if (root != NULL)
postorder(root->left);
postorder(root->right);
printf("%d \n", root->key);
}
1 4 7 6 3 13 14 10 8
Delete
1. Node to be deleted at leaf
2. Node to be deleted has only one child
3. Node to be deleted has two children
Node to be deleted at leaf
Node to be deleted has only one child
Node to be deleted has two children
struct node* deleteNode(struct node* root, int key)
{
// base case
if (root == NULL) return root;
return current;
}
Construct tree
Preorder-{10, 5, 1, 7, 40, 50}
[8,5,1,7,10,12]
50, 70, 60, 20, 90, 10, 40, 100
The first element of preorder traversal is always root.
We first construct the root.
Then we find the index of first element which is
greater than root. Let the index be ‘i’. The values
between root and ‘i’ will be part of left subtree, and the
values between ‘i+1’ and ‘n-1’ will be part of right
subtree.
Divide given pre[] at index “i” and recur for left and
right sub-trees.
( O(n2) time complexity )
From postorder traversal
{1, 7, 5, 50, 40, 10}
if (temp->left != NULL)
q.push(temp->left);
if (temp->right != NULL)
q.push(temp->right);
}
return count;
Recursive
unsigned int getCount(struct Node* root)
{
if (root == NULL)
return 0;
int res = 0;
if (root->left && root->right)
res++;
/* do the subtrees */
mirror(node->left);
mirror(node->right);
1
Threaded Trees
• Binary trees have a lot of wasted space: the
leaf nodes each have 2 null pointers
• We can use these pointers to help us in
inorder traversals
• We have the pointers reference the next node
in an inorder traversal; called threads
• We need to know if a pointer is an actual link
or a thread, so we keep a boolean for each
pointer
2
• Inorder traversal of a Binary tree can either be
done using recursion or with the use of a
auxiliary stack. The idea of threaded binary
trees is to make inorder traversal faster and do
it without stack and without recursion. A
binary tree is made threaded by making all
right child pointers that would normally be
NULL point to the inorder successor of the
node (if it exists).
• The threads are also useful for fast accessing
ancestors of a node.
3
There are two types of threaded binary trees.
• Single Threaded: Where a NULL right pointers is
made to point to the inorder successor (if
successor exists)
• Double Threaded: Where both left and right
NULL pointers are made to point to inorder
predecessor and inorder successor respectively.
The predecessor threads are useful for reverse
inorder traversal and postorder traversal.
4
Threaded Tree
5
Threaded Tree Traversal
• We start at the leftmost node in the tree, print
it, and follow its right thread
• If we follow a thread to the right, we output
the node and continue to its right
• If we follow a link to the right, we go to the
leftmost node, print it, and continue
6
Threaded Tree Traversal
Output
6 1
3 8
1 5 7 11
9 13
Start at leftmost node, print it
7
Threaded Tree Traversal
Output
6 1
3
3 8
1 5 7 11
9 13
Follow thread to right, print node
8
Threaded Tree Traversal
Output
6 1
3
5
3 8
1 5 7 11
9 13
Follow link to right, go to leftmost
node and print
9
Threaded Tree Traversal
Output
6 1
3
5
3 8 6
1 5 7 11
9 13
Follow thread to right, print node
10
Threaded Tree Traversal
Output
6 1
3
5
3 8 6
7
1 5 7 11
9 13
Follow link to right, go to
leftmost node and print
11
Threaded Tree Traversal
Output
6 1
3
5
3 8 6
7
1 5 7 11 8
9 13
Follow thread to right, print node
12
Threaded Tree Traversal
Output
6 1
3
5
3 8 6
7
1 5 7 11 8
9
9 13
Follow link to right, go to
leftmost node and print
13
Threaded Tree Traversal
Output
6 1
3
5
3 8 6
7
1 5 7 11 8
9
11
9 13
Follow thread to right, print node
14
Threaded Tree Traversal
Output
6 1
3
5
3 8 6
7
1 5 7 11 8
9
11
9 13 13
16
Threaded Tree Traversal Code
// C code to do inorder traversal in a threaded binary tree
void inOrder(struct Node *root)
{
struct Node *cur = leftmost(root);
while (cur != NULL)
{
printf("%d ", cur->data);
return n;
}
18
Threaded Tree Modification
• We’re still wasting pointers, since half of our
leafs’ pointers are still null
• We can add threads to the previous node in
an inorder traversal as well, which we can use
to traverse the tree backwards or even to do
postorder traversals
19
Threaded Tree Modification
6
3 8
1 5 7 11
9 13
20
Insertion
• First element
• Left Child
• Right Child
21
• First element
root = temp;
temp -> left = NULL;
temp -> right = NULL;
22
• Left Child
temp -> left = parent ->left;
temp -> right = parent;
parent -> leftThreaded = false;
parent -> left = temp;
23
• Right Child
temp -> left = parent;
temp -> right = parent -> right;
parent -> rightThreaded = false;
parent -> right = temp;
24
struct Node insert(struct Node root, int val)
{
// Search for the correct position
struct Node *ptr = root;
struct Node *parent = NULL; // parentent of key to be inserted
while (ptr != NULL)
{
25
// Create a new node
struct Node *temp = (struct Node* )malloc(sizeof(struct Node));
temp -> data = val;
temp -> leftThreaded = true;
temp -> rightThreaded = true;
if (parent == NULL)
{
root = temp;
temp -> left = NULL;
temp -> right = NULL;
}
else if (val < (parent -> data))
{
temp -> left = parent -> left;
temp -> right = parent;
parent -> leftThreaded = false;
parent -> left = temp;
}
else
{
temp -> left = parent;
temp -> right = parent -> right;
parent -> rightThreaded = false;
parent -> right = temp;
}
return root;
}
26
AVL Tree
Requirement:
– Define and maintain a balance to ensure
Q(ln(n)) height
Prototypical Examples
These two examples demonstrate how we can correct
for imbalances: starting with this tree, add 1:
Prototypical Examples
This is more like a linked list; however, we can fix this…
Prototypical Examples
Promote 2 to the root, demote 3 to be 2’s right child, and
1 remains the left child of 2
Prototypical Examples
Again, the product is a linked list; however, we can fix
this, too
Why AVL Trees?
• Most of the BST operations (e.g., search, max, min,
insert, delete.. etc) take O(h) time where h is the height
of the BST.
• The cost of these operations may become O(n) for a
skewed Binary tree.
• If we make sure that height of the tree remains
O(Logn) after every insertion and deletion, then we can
guarantee an upper bound of O(Logn) for all these
operations.
• The height of an AVL tree is always O(Logn) where n is
the number of nodes in the tree
AVL Trees
We will focus on the first strategy: AVL trees
– Named after Adelson-Velskii and Landis
Recall:
– An empty tree has height –1
– A tree with a single node has height 0
AVL Trees
• AVL tree is a self-balancing Binary Search Tree
(BST) where the difference between heights of
left and right subtrees cannot be more than
one for all nodes.
• A binary search tree is said to be AVL
balanced if:
– The difference in the heights between the left
and right sub-trees is at most 1, and
– Both sub-trees are themselves AVL trees
AVL Trees
AVL trees with 1, 2, 3, and 4 nodes:
AVL Trees
Here is a larger AVL tree (42 nodes):
AVL Trees
All other nodes are AVL balanced
– The sub-trees differ in height by at most one
Height of an AVL Tree
By the definition of complete trees, any
complete binary search tree is an AVL tree
14
11 17
7 53
4
AVL Tree Example:
• Insert 14, 17, 11, 7, 53, 4, 13 into an empty AVL tree
14
7 17
4 11 53
13
AVL Tree Example:
• Now insert 12
14
7 17
4 11 53
13
12
AVL Tree Example:
• Now insert 12
14
7 17
4 11 53
12
13
AVL Tree Example:
• Now the AVL tree is balanced.
14
7 17
4 12 53
11 13
AVL Tree Example:
• Now insert 8
14
7 17
4 12 53
11 13
8
AVL Tree Example:
• Now insert 8
14
7 17
4 11 53
8 12
13
AVL Tree Example:
• Now the AVL tree is balanced.
14
11 17
7 12 53
4 8 13
AVL Tree Example:
• Now remove 53
14
11 17
7 12 53
4 8 13
AVL Tree Example:
• Now remove 53, unbalanced
14
11 17
7 12
4 8 13
AVL Tree Example:
• Balanced! Remove 11
11
7 14
4 8 12 17
13
AVL Tree Example:
• Remove 11, replace it with the largest in its left branch
7 14
4 12 17
13
AVL Tree Example:
• Remove 8, unbalanced
4 14
12 17
13
AVL Tree Example:
• Remove 8, unbalanced
4 12
14
13 17
AVL Tree Example:
• Balanced!!
12
7 14
4 13 17
• Build an AVL tree with the following values:
15, 20, 24, 10, 13, 7, 30, 36, 25
15, 20, 24, 10, 13, 7, 30, 36, 25
20
15
15 24
20
10
24
13
20 20
13 24 15 24
10 15 13
10
15, 20, 24, 10, 13, 7, 30, 36, 25
20
13
13 24 10 20
10 15 7 15 24
7 30
13 36
10 20
7 15 30
24 36
15, 20, 24, 10, 13, 7, 30, 36, 25
13 13
10 20 10 20
7 15 30 7 15 24
24 36 30
25 13 25 36
10 24
7 20 30
15 25 36
Remove 24 and 20 from the AVL tree.
13 13
10 24 10 20
7 20 30 7 15 30
15 25 36 25 36
13 13
10 30 10 15
7 15 36 7 30
25 25 36
struct Node *rightRotate(struct Node *y)
{
struct Node *x = y->left;
struct Node *T2 = x->right;
// Perform rotation
x->right = y;
y->left = T2;
// Update heights
y->height = max(height(y->left), height(y->right))+1;
x->height = max(height(x->left), height(x->right))+1;
// Perform rotation
y->left = x;
x->right = T2;
// Update heights
x->height = max(height(x->left), height(x->right))+1;
y->height = max(height(y->left), height(y->right))+1;
struct Node
{
int data;
bool color;
Node *left, *right, *parent;
};
void inorder (Node *root)
{
if (root == NULL)
return;
inorder (root->left);
cout << root->data << " ";
inorder (root->right);
}
Node* BSTInsert(Node* root, Node *pt)
{
/* If the tree is empty, return a new node */
if (root == NULL)
return pt;
else
{
}
/* Case : 2 pt is left child of its parent Right-rotation required */
if (pt == parent_pt->left)
{
rotateRight(root, parent_pt);
pt = parent_pt;
parent_pt = pt->parent;
}
rotateLeft(root, grand_parent_pt);
swap(parent_pt->color, grand_parent_pt->color);
pt = parent_pt;
}
root->color = BLACK;
RBTree tree;
tree.insert(7);
void rotateLeft(Node *&root, Node *&pt)
{
Node *pt_right = pt->right;
pt->right = pt_right->left;
if (pt->right != NULL)
pt->right->parent = pt;
pt_right->parent = pt->parent;
if (pt->parent == NULL)
root = pt_right;
else
pt->parent->right = pt_right;
pt_right->left = pt;
pt->parent = pt_right;
}
void rotateRight(Node *&root, Node *&pt)
{
Node *pt_left = pt->left;
pt->left = pt_left->right;
if (pt->left != NULL)
pt->left->parent = pt;
pt_left->parent = pt->parent;
if (pt->parent == NULL)
root = pt_left;
else
pt->parent->right = pt_left;
pt_left->right = pt;
pt->parent = pt_left;
}
Insertion Vs Delete
Insert Delete
Recoloring and rotations are used Recoloring and rotations are used
We check color of uncle to decide the We check color of sibling to decide the
appropriate case appropriate case.
Main property that violates after insertion Main violated property is, change of black
is two consecutive red. height in subtrees as deletion of a black
node may cause reduced black height in
one root to leaf path.
When a black node is deleted and replaced by a black child, the child is
marked as double black. The main task now becomes to convert this double
black to single black.
Deletion Steps
1) Perform standard BST delete.
(Delete node which is either leaf or has only one
child. Successor will be used to replace. )
2) Case 1. If either u or v is red-
we mark the replaced child as black (No change
in black height). Note that both u and v cannot
be red as v is parent of u and two consecutive
reds are not allowed in red-black tree.
• If Both u and v are Black
Color u as double black. Now our task reduces to convert this
double black to single black. Note that If v is leaf, then u is
NULL and color of NULL is considered as black. So the deletion
of a black leaf also causes a double black.
while the current node u is double black and it is not root. Let sibling of
node be s.
Case1:If sibling s is black and at least one of sibling’s children is red
Case2: If sibling is black and its both children are black
Case3:If sibling is red
.(a): If sibling s is black and at least one of sibling’s
children is red, perform rotation(s). Let the red child of s
be r. This case can be divided in four subcases depending
upon positions of s and r.
…………..(i) Left Left Case (s is left child of its parent and r
is left child of s or both children of s are red). This is
mirror of right right case.
if (parent->isOnLeft())
// uncle on right
return parent->parent->right;
else
// uncle on left
return parent->parent->left;
}
Node * findsibling() {
// sibling null if no parent
if (parent == NULL)
return NULL;
if (isOnLeft())
return parent->right;
return parent->left;
}
bool hasRedChild() {
return (left != NULL and left->color == RED) or
(right != NULL and right->color == RED);
}
• bool isOnLeft() { return this == parent->left; }
x->moveDown(nParent);
x->moveDown(nParent);
B-Trees
1
Motivation for B-Trees
B-Trees 2
Motivation (cont.)
B-Trees 3
Definition of a B-tree
B-Trees 4
An example B-Tree
26 A B-tree of order 5
containing 26 items
6 12
42 51 62
1 2 4 7 8 13 15 18 25
27 29 45 46 48 53 55 60 64 70 90
B-Trees 5
Constructing a B-tree
1 2 8 12
B-Trees 6
1 12 8 2 25 6 14 28 17 7 52 16 48 68 3 26 29 53 55 45
1 2 12 25
1 2 6 12 14 25 28
B-Trees 7
1 12 8 2 25 6 14 28 17 7 52 16 48 68 3 26 29 53 55 45
Adding 17 to the right leaf node would over-fill it, so we take the
middle key, promote it (to the root) and split the leaf
8 17
1 2 6 12 14 25 28
1 2 6 7 12 14 16 25 28 48 52
B-Trees 8
1 12 8 2 25 6 14 28 17 7 52 16 48 68 3 26 29 53 55 45
1 2 6 7 12 14 16 25 26 28 29 52 53 55 68
B-Trees 9
Constructing a B-tree (contd.)
17
3 8 28 48
1 2 6 7 12 14 16 25 26 29 45 52 53 55 68
B-Trees 10
Inserting into a B-Tree
B-Trees 11
Exercise in Inserting a B-Tree
B-Trees 12
Removal from a B-tree
• During insertion, the key always goes into a leaf. For deletion
we wish to remove from a leaf. There are three possible ways
we can do this:
• 1 - If the key is already in a leaf node, and removing it doesn’t
cause that leaf node to have too few keys, then simply remove
the key to be deleted.
• 2 - If the key is not in a leaf then it is guaranteed (by the
nature of a B-tree) that its predecessor or successor will be in
a leaf -- in this case we can delete the key and promote the
predecessor or successor key to the non-leaf deleted key’s
position.
B-Trees 13
Removal from a B-tree (2)
B-Trees 14
Type #1: Simple leaf deletion
Assuming a 5-way
B-Tree, as before... 12 29 52
2 7 9 15 22 31 43 56 69 72
B-Trees 15
Type #2: Simple non-leaf deletion
12 29 56
52 Delete 52
7 9 15 22 31 43 56 69 72
B-Trees 16
Type #4: Too few keys in node and
its siblings
12 29 56
7 9 15 22 31 43 69 72
Too few keys!
Delete 72
B-Trees 17
Type #4: Too few keys in node and
its siblings
12 29
7 9 15 22 31 43 56 69
B-Trees 18
Type #3: Enough siblings
12 29
Demote root key and
promote leaf key
7 9 15 22 31 43 56 69
Delete 22
B-Trees 19
Type #3: Enough siblings
12 31
7 9 15 29 43 56 69
B-Trees 20
Exercise in Removal from a B-Tree
B-Trees 21
Analysis of B-Trees
B-Trees 22
Reasons for using B-Trees
B-Trees 23
Comparing Trees
• Binary trees
– Can become unbalanced and lose their good time complexity (big O)
– AVL trees are strict binary trees that overcome the balance problem
– Heaps remain balanced but only prioritise (not order) the keys
• Multi-way trees
– B-Trees can be m-way, they can have any (odd) number of children
– One B-Tree, the 2-3 (or 3-way) B-Tree, approximates a permanently
balanced binary tree, exchanging the AVL tree’s balancing operations
for insertion and (more complex) deletion operations
B-Trees 24
B-Trees 25
B+ Tree
• B+ tree eliminates the drawback B-tree used for indexing
• It store data pointers only at the leaf nodes of the tree.
• Data pointers are present only at the leaf nodes, the leaf nodes
must necessarily store all the key values along with their
corresponding data pointers to the disk file block, in order to
access them.
• Leaf nodes are linked to providing ordered access to the
records.
• The leaf nodes, therefore form the first level of the index, with
the internal nodes forming the other levels of a multilevel
index.
• Some of the key values of the leaf nodes also appear in the
internal nodes, to simply act as a medium to control the
B-Trees 26
searching of a record.
B-Trees 27
Insertion
• Each node except root can have a maximum of M children and at
least ceil(M/2) children.
• Each node can contain a maximum of M – 1 keys and a minimum of ceil(M/2) –
1 keys.
• The root has at least two children and atleast one search key.
• While insertion overflow of the node occurs when it contains more than M – 1 search
key values.
Here M is the order of B+ tree.
Steps for insertion in B+ Tree
• Every element is inserted into a leaf node. So, go to the appropriate leaf node.
• Insert the key into the leaf node in increasing order only if there is no overflow. If there
is an overflow go ahead with the following steps mentioned below to deal with
overflow while maintaining the B+ Tree properties.
B-Trees 28
Case 1: Overflow in leaf node
• Split the leaf node into two nodes.
• First node contains ceil((m-1)/2) values.
• Second node contains the remaining values.
• Copy the smallest search key value from second node to the
parent node.(Right biased)
B-Trees 29
Case 2: Overflow in non-leaf node
• Split the non leaf node into two nodes.
• First node contains ceil(m/2)-1 values.
• Move the smallest among remaining to the parent.
• Second node contains the remaining keys.
B-Trees 30
Insert the following key values 6, 16, 26, 36, 46 on a B+ tree
with order = 3.
B-Trees 31