0% found this document useful (0 votes)
13 views9 pages

Week 7N

The document covers concepts related to balanced search trees (AVL Trees) and various greedy algorithms, including interval scheduling, minimizing lateness, and Huffman coding. It provides detailed explanations, implementations, and examples for each topic. Key algorithms are outlined with their respective analyses of time complexity.

Uploaded by

Harshdeep Sharma
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views9 pages

Week 7N

The document covers concepts related to balanced search trees (AVL Trees) and various greedy algorithms, including interval scheduling, minimizing lateness, and Huffman coding. It provides detailed explanations, implementations, and examples for each topic. Key algorithms are outlined with their respective analyses of time complexity.

Uploaded by

Harshdeep Sharma
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

Week 7 Revision

Week 7 Revision
Balanced search tree (AVL Tree)
Greedy Algorithm
Interval scheduling
Minimize lateness
Huffman Algorithm

Balanced search tree (AVL Tree)


Binary search tree

find(), insert() and delete() all walk down a single path


Worst-case: height of the tree An unbalanced tree with n nodes may have height O(n)

AVL Tree

Balanced trees have height O(log n)


Using rotations, we can maintain height balance
Height balanced trees have height O(log n)
find(), insert() and delete() all walk down a single path, take time O(log n)
Minimum number of node
Maximum number of nodes

Implementation

1 class AVLTree:
2 # Constructor:
3 def __init__(self,initval=None):
4 self.value = initval
5 if self.value:
6 self.left = AVLTree()
7 self.right = AVLTree()
8 self.height = 1
9 else:
10 self.left = None
11 self.right = None
12 self.height = 0
13 return
14
15 def isempty(self):
16 return (self.value == None)
17
18 def isleaf(self):
19 return (self.value != None and self.left.isempty() and
self.right.isempty())
20
21 def leftrotate(self):
22 v = self.value
23 vr = self.right.value
24 tl = self.left
25 trl = self.right.left
26 trr = self.right.right
27 newleft = AVLTree(v)
28 newleft.left = tl
29 newleft.right = trl
30 self.value = vr
31 self.right = trr
32 self.left = newleft
33 return
34
35 def rightrotate(self):
36 v = self.value
37 vl = self.left.value
38 tll = self.left.left
39 tlr = self.left.right
40 tr = self.right
41 newright = AVLTree(v)
42 newright.left = tlr
43 newright.right = tr
44 self.right = newright
45 self.value = vl
46 self.left = tll
47 return
48
49
50 def insert(self,v):
51 if self.isempty():
52 self.value = v
53 self.left = AVLTree()
54 self.right = AVLTree()
55 self.height = 1
56 return
57 if self.value == v:
58 return
59 if v < self.value:
60 self.left.insert(v)
61 self.rebalance()
62 self.height = 1 + max(self.left.height, self.right.height)

63 if v > self.value:
64 self.right.insert(v)
65 self.rebalance()
66 self.height = 1 + max(self.left.height, self.right.height)
67
68 def rebalance(self):
69 if self.left == None:
70 hl = 0
71 else:
72 hl = self.left.height
73 if self.right == None:
74 hr = 0
75 else:
76 hr = self.right.height
77 if hl - hr > 1:
78 if self.left.left.height > self.left.right.height:
79 self.rightrotate()
80 if self.left.left.height < self.left.right.height:
81 self.left.leftrotate()
82 self.rightrotate()
83 self.updateheight()
84 if hl - hr < -1:
85 if self.right.left.height < self.right.right.height:
86 self.leftrotate()
87 if self.right.left.height > self.left.right.height:
88 self.right.rightrotate()
89 self.leftrotate()
90 self.updateheight()
91
92 def updateheight(self):
93 if self.isempty():
94 return
95 else:
96 self.left.updateheight()
97 self.right.updateheight()
98 self.height = 1 + max(self.left.height, self.right.height)

99
100 def inorder(self):
101 if self.isempty():
102 return([])
103 else:
104 return(self.left.inorder()+ [self.value]+ self.right.inorder())
105 def preorder(self):
106 if self.isempty():
107 return([])
108 else:
109 return([self.value] + self.left.preorder()+
self.right.preorder())
110 def postorder(self):
111 if self.isempty():
112 return([])
113 else:
114 return(self.left.postorder()+ self.right.postorder() +
[self.value])
115
116 A = AVLTree()
117 nodes = eval(input())
118 for i in nodes:
119 A.insert(i)
120
121 print(A.inorder())
122 print(A.preorder())
123 print(A.postorder())

Sample Input

1 [1,2,3,4,5,6,7] #order of insertion

Output

1 [1, 2, 3, 4, 5, 6, 7] #inorder traversal


2 [4, 2, 1, 3, 6, 5, 7] #preorder traversal
3 [1, 3, 2, 5, 7, 6, 4] #postorder traveral
Greedy Algorithm
Need to make a sequence of choices to achieve a global optimum

At each stage, make the next choice based on some local criterion
Never go back and revise an earlier decision

Drastically reduces space to search for solutions


Greedy strategy needs a proof of optimality

Example :

Dijkstra's
Prim's
Kruskal's
Interval scheduling
Minimize lateness
Huffman coding

Interval scheduling
▪ IIT Madras has a special video classroom for delivering online lectures

▪ Different teachers want to book the classroom

▪ Slots may overlap, so not all bookings can be honored

▪ Choose a subset of bookings to maximize the number of teachers who get to use the room

Algorithm

1. Sort all jobs which based on end time in increasing order.


2. Take the interval which has earliest finish time.
3. Repeat net two steps till all you process all jobs.
4. Eliminate all intervals which have start time less than selected interval’s end time.
5. If interval has start time greater than current interval’s end time, at it to set. Set current
interval to new interval.

Example

In the table below, we have 8 activities with the corresponding start and finish times, It might not
be possible to complete all the activities since their time frame can conflict. For example, if any
activity starts at time 0 and finishes at time 4, then other activities can not start before 4. It can be
started at 4 or afterwards.

What is the maximum number of activities which can be performed without conflict? [NAT]
Activity Start time Finish time

A 1 3

B 3 4

C 0 7

D 1 2

E 5 6

F 5 9

G 10 11

H 7 8

Answer

Example

A popular meeting hall in a city receives many overlapping applications to hold meetings. The
manager wishes to satisfy as many customers as possible. Each application is a tuple (id,
start_day, end_day) where id , start_day and end_day are the unique id assigned to the
application, starting day of the meeting and ending day of meeting ends inclusive respectively.
Write a function no_overlap(L) to return the list of customer ids whose applications are
accepted that ensures optimal scheduling. Let L be a list tuples with (id, start_day,
end_day) .

Sample Input

1 L = [
2 (0, 1, 2),
3 (1, 1, 3),
4 (2, 1, 5),
5 (3, 3, 4),
6 (4, 4, 5),
7 (5, 5, 8),
8 (6, 7, 9),
9 (7, 10, 13),
10 (8, 11, 12)
11 ]

Sample output

1 [0, 3, 6, 8]

Solution

1 def tuplesort(L, index):


2 L_ = []
3 for t in L:
4 L_.append(t[index:index+1] + t[:index] + t[index+1:])
5 L_.sort()
6
7 L__ = []
8 for t in L_:
9 L__.append(t[1:index+1] + t[0:1] + t[index+1:])
10 return L__
11
12 def no_overlap(L):
13 sortedL = tuplesort(L, 2)
14 accepted = [sortedL[0][0]]
15 for i, s, f in sortedL[1:]:
16 if s > L[accepted[-1]][2]:
17 accepted.append(i)
18 return accepted
19
20 L = []
21 while True:
22 line = input().strip()
23 if line == '':
24 break
25 t = line.split()
26 L.append((int(t[0]), int(t[1]), int(t[2])))
27 print(len(no_overlap(L)))

Analysis

Initially, sort n bookings by finish time — O(n log n)


Single scan, O(n)
overall O(n log n)

Minimize lateness
▪ IIT Madras has a single 3D printer

▪ A number of users need to use this printer

▪ Each user will get access to the printer, but may not finish before deadline

▪ Goal is to minimize the lateness

Algorithm

1. Sort all job in ascending order of deadlines

2. Start with time t = 0

3. For each job in the list


1. Schedule the job at time t
2. Finish time = t + processing time of job
3. t = finish time
4. Return (start time, finish time) for each job

Example

1 from operator import itemgetter


2
3 jobs = [(1, 3, 6), (2, 2, 9), (3, 1, 8), (4, 4, 9),
4 (5, 3, 14), (6, 2, 15)]
5
6 def minimize_lateness():
7 schedule =[]
8 max_lateness = 0
9 t = 0
10
11 sorted_jobs = sorted(jobs,key=itemgetter(2))
12
13 for job in sorted_jobs:
14 job_start_time = t
15 job_finish_time = t + job[1]
16
17 t = job_finish_time
18 if(job_finish_time > job[2]):
19 max_lateness = max (max_lateness, (job_finish_time - job[2]))
20 schedule.append((job[0],job_start_time, job_finish_time))
21
22 return max_lateness, schedule
23
24 max_lateness, sc = minimize_lateness()
25 print ("Maximum lateness will be :" + str(max_lateness))
26 for t in sc:
27 print (t[0], t[1],t[2])

Analysis

Sort the requests by D(i) — O(n log n)


Read all schedule in sorted order — O(n)
overall O(n log n)

Huffman Algorithm
Algorithm

1. Calculate the frequency of each character in the string.


2. Sort the characters in increasing order of the frequency.
3. Make each unique character as a leaf node.
4. Create an empty node z. Assign the minimum frequency to the left child of z and assign the
second minimum frequency to the right child of z. Set the value of the z as the sum of the
above two minimum frequencies.
5. Remove these two minimum frequencies from Q and add the sum into the list of
frequencies.
6. Insert node z into the tree.
7. Repeat steps 3 to 5 for all the characters.
8. For each non-leaf node, assign 0 to the left edge and 1 to the right edge.

Example
We received a message 10010000010001110111100011010 encoded using Huffman coding of
a,b,c,d and e generated by the given Huffman tree. Which of the following is the correct
decoded message for the given encoded message? [MCQ]

(a) cbaababdebadb

(b) cbaabcbdebadc

(c) cbaababdecadc

(d) cbaabcbedcadc

Answer

(c)

Implementation

1
2 class Node:
3 def __init__(self,frequency,symbol = None,left = None,right = None):
4 self.frequency = frequency
5 self.symbol = symbol
6 self.left = left
7 self.right = right
8
9 # Solution
10
11 def Huffman(s):
12 huffcode = {}
13 char = list(s)
14 freqlist = []
15 unique_char = set(char)
16 for c in unique_char:
17 freqlist.append((char.count(c),c))
18 nodes = []
19 for nd in sorted(freqlist):
20 nodes.append((nd,Node(nd[0],nd[1])))
21 while len(nodes) > 1:
22 nodes.sort()
23 L = nodes[0][1]
24 R = nodes[1][1]
25 newnode = Node(L.frequency + R.frequency, L.symbol + R.symbol,L,R)
26 nodes.pop(0)
27 nodes.pop(0)
28 nodes.append(((L.frequency + R.frequency, L.symbol +
R.symbol),newnode))
29
30 for ch in unique_char:
31 temp = newnode
32 code = ''
33 while ch != temp.symbol:
34 if ch in temp.left.symbol:
35 code += '0'
36 temp = temp.left
37 else:
38 code += '1'
39 temp = temp.right
40 huffcode[ch] = code
41 return huffcode
42
43
44
45 s = input()
46 res = Huffman(s)
47 for char in sorted(res):
48 print(char,res[char])

Analysis

At each recursive step, extract letters with minimum frequency and replace by composite
letter with combined frequency
Store frequencies in an array
Linear scan to find minimum values
|𝐴| = 𝑘, number of recursive calls is k – 1
Complexity is
Instead, maintain frequencies in an heap
Extracting two minimum frequency letters and adding back compound letter are both 𝑂(log
𝑘)
Complexity drops to 𝑂(k log 𝑘)

You might also like