Cs 97si: Introduction To Programming Contests: Jaehyun Park
Cs 97si: Introduction To Programming Contests: Jaehyun Park
Jaehyun Park
Todays Lecture
Stack/Queue Heap and Priority Queue Union-Find Structure Binary Search Tree (BST) Binary Indexed Tree (BIT) Lowest Common Ancestor (LCA)
GetNextTask()
GetNextTask()
the newest task (stack) Returns the oldest task (queue) Returns the most urgent task (priority queue) Returns the easiest task (priority queue)
Stack
Push(x):
inserts x into the stack Pop(): removes the newest item Top(): returns the newest item
Stack Implementation
Have a large enough array s[] and a counter k, which starts at zero
Push(x):
set s[k] = x and increment k by 1 Pop(): decrement k by 1 Top(): returns s[k - 1] (error if k is zero)
stack
Queue
Enqueue(x):
inserts x into the queue Dequeue(): removes the oldest item Front(): returns the oldest item
slightly trickier
Queue Implementation
In many cases, you know the total number of elements that enter a queue
Dequeue()
Priority Queue
Insert(x, p):
inserts x into the PQ, whose priority is p RemoveTop(): removes the element with the highest priority Top(): returns the element with the highest priority
All operations can be done quickly if implemented using a heap priority_queue (C++), PriorityQueue (Java)
Heap
Inserting/removing a node can be done in (log ) time without breaking the heap property
May
Heap Example
Start from the root, number the nodes 1, 2, from left to right Given a node , easy to compute the indices of its parent and children
node: /2 Children: 2, 2 + 1
Parent
4 8 9
1
2 5 6 3 7
Inserting a Node
2. If the new node breaks the heap property, swap with its parent node
The
new node moves up the tree, which may introduce another conflict
3. Repeat 2 until all conflicts are resolved Running time = tree height = (log )
void InsertNode(int v) { H[++n] = v; for(int k = n; k > 1; k /= 2) { if(H[k] > H[k / 2]) swap(H[k], H[k / 2]); else break; } }
1. Remove the root, and bring the last node (rightmost node in the last level) to the root 2. If the root breaks the heap property, look at its children and swap it with the larger one
Swapping
3. Repeat 2 until all conflicts are resolved Running time = log Exercise: implementation
Just
Union-Find Structure
Find(x):
returns the representative of the set that x merges two sets that contain x and y
belongs
Union(x, y):
Union-Find Structure
node maintains a link to its parent A root node is the representative of the corresponding set Example: two sets {x, y, z} and {a, b,
c, d}
x y z b
a c
Implementation Idea
Find(x):
itself
This
Union(x, y):
run Find(x) and Find(y) to find corresponding root nodes and direct one to the other
Implementation
int Find(int x) { while(x != L[x]) x = L[x]; return x; } void Union(int x, int y) { L[Find(x)] = Find(y); }
Path Compression
Path compression makes the trees shallower every time Find() is called We dont care how a tree looks like as long as the root stays the same
After Find(x)
returns the root, backtrack to x and reroute all the links to the root
Insert(x):
inserts a node with value x Delete(x): deletes a node with value x, if there is any Find(x): returns the node with value x, if there is any
Count(x):
counts the number of nodes with value less than or equal to x GetNext(x): returns the smallest node with value x
set, map
(C++) (Java)
TreeSet, TreeMap
Set(k, x):
Note:
sets the value of kth item equal to x Sum(k): computes the sum of items 1k
sum of items ij = Sum(j)
Sum(i 1)
BIT Structure
th leaf node stores the value of item Each internal node stores the sum of values of its children
e.g.
item[5] + item[6]
Main idea: choose the minimal set of nodes whose sum gives the desired value We will see that
at
most 1 node is chosen at each level so that the total number of nodes we look at is log 2 and this can be done in log time
Summing: Example 1
Sum(7)
Summing: Example 2
Sum(8)
Summing: Example 3
Sum(6)
Sum(3)
Implementing Sum(k)
Maintain a pointer P which initially points at leaf Climb the tree using the following procedure:
If P
Add
the value of P Set P to the parent node of Ps left neighbor If P has no left neighbor, terminate
Otherwise:
Set P
This part is a lot easier Only the values of leaf k and its ancestors change 1. Start at leaf k, change its value to x 2. Go to its parent, and recompute its value 3. Repeat 2 until the root
Extension
Min(i, j):
Max(i,
Input: a rooted tree and a bunch of node pairs Output: lowest (deepest) common ancestors of the given pairs of nodes Goal: preprocessing the tree in log time in order to answer each LCA query in log time
Preprocessing
Each node stores its depth, as well as the links to every 2 th ancestor
log additional storage per node We will use Anc[x][k] to denote the 2 th ancestor of node x
Computing Anc[x][k]
][ k-1 ]
Answering a Query
Maintain two pointers p and q, initially pointing at x and y If depth(p) < depth(q), bring q to the same depth as p
using Anc
Answering a Query
is 0, return ps parent node If Anc[p][k] is undefined, or if Anc[p][k] and Anc[q][k] point to the same node:
Decrease k
by 1 and q
= Anc[q][k]
Otherwise:
Set p = Anc[p][k] by 2 levels
to bring p and q up
Conclusion
many small examples with pencil and paper to completely internalize the material Review and solve relevant problems