Binary Search
BinarySearch(v, a, lo, hi)
input value v
array a[lo...hi] of values
output true if v in a[lo...hi]
false otherwise
mid = (lo + hi)/2
if lo > hi return false
if a[mid] = v return true
else if a[mid] < v
return BinarySearch(v, a, mid + 1, hi)
else
return BinarySearch(v, a, lo, mid - 1)
Computing ploynomial
The ploynomial can be recursively computed as
Algorithm computingPloynomial(x, n , A[])
p = A[n]
for(i = 1; i < n+1; i++){
p = p*x + A[n -1]
}
return p;
Tree
Preorder Traversal
Algorithm preOrder(v){
visit(v);
for each child w of v
preorder(w);
}
Postordert Traversal
Algorithm postOrder(v):
{
for each child w of v
postOrder(w);
visit(v);
}
Inordert Traversal
only for Binary tree
Algorithm inOrder(v)
{
if v has a left child
inOrder(v.left)
visit(v)
if v has a right child
inOrder(v.right)
}
An inorder traversal of a binary search trees visits the keys in non-decreasing order
Evaluate Arithmetic Expressions
Specialization of a postororder traversal
Algorithm evalExpr(v)
{
if v is a left node
return v.element
else
x = evalExpr(v.left);
y = evalExpr(v.right);
operator = operator stored at v,
return x <operator> y;
}
BST
Search
Time complexity O(n) , 最壞情況:鏈表(極度不平衡)
TreeSearch(v, k)
Input v(node), k(key)
output the node
if v is null or v.key = k
return v;
if k<v.key
return TreeSeach(v.left, k);
else
return TreeSearch(v.right, k);
Insert
be careful: insert equal keys in the left subtree
Time complexity: O(n), 最壞情況:鏈表(極度不平衡)
TreeInsert(v, k)
input v(node), k(key)
output none
if root = null //empty tree
root = CreateNode(k) //create a root to store k
else if k < v.key //insert the duplicate keys in the left subtree
if v.left = null
v.left = CreateNode(k); //insert a new node as left child of v
else
TreeInsert(v.left, k);
else if k > v.key
if k > v.key
if v.right = null
v.left = CreateNode(k);
else
TreeInsert(v..right, k);
Deletion
AVL TREE
AVL tree is a binary search tree
AVL trees are balanced
height of an AVL tree storing n keys is O(log(n))
- a single restructure is O(1)
- find is O(log(n))
- Insert is O(log(n))
- delete is O(log(n))
Splay Tree
- is binary tree
- node is splayed after it is accessed
- splay cost O(h)
- O(h) worst-case is still O(n)
(2, 4)Tree
A (2, 4) tree storing n items has height O(log(n))
Overflow and Split
Insert
Algorithm insert(k, o)
search for key k to locate the insertion node vi //lO(og(n))
add the new entry (k, o) at node vi //lO(1)
while (overflow(v)){ //lO(og(n))
if (isRoot(v))
create a new empty root above vi
v = split(v);
}
Deletion
- visit O(log(n)) nodes
- each fusion and transfer takes O(1) time
- handle an underflow with a series of O(log(n))
deleting an item from a (2, 4)tree takes O(log(n)) time
underflow and Fusion
the adjacent sibling of v are 2-nodes
Fusion operation: merge adjacent sibing and move an entry from parent to the merged node
after a fusion, the under flow may propagate to the parent node
underflow and Transfer
an adjacent sibling is a 3-node or a 4-node
Transfer operation
1. move an item from u to v
2. move an item from w to u
after a transfer, no underfolw occurs
Priority Queue Sorting
Algorithm PQ-Sort(S)
Input sequence S
Output sequence S sorted in non-decreasing order
{
Create an empty priority queue P
while(! IsEmpty(S)){
e = RemoveFirst(S);
Insert(P, e);
}
while (! IsEmpty(P)){
e = RemoveMin(P);
InsertLast(S, e);
}
}
String
Brute-Force Pattern Matching
time complexity is O(nm)
Algorithm BruteForceMatch(T, P)
Input text T of size n and pattern
P of size m
Output starting index of a substring of T equal to P
or -1 if no such substring exists
for(i = 0; i < n-m+1; i++){
j = 0;
while(j<m && T[i+j] = p[j])
j = j+1
if(j = m)
return i; //match at i
return -1; //no match anywhere
}
Boyer-Moore Heuristics
Last-Occurrence Function
define as
- the largest index i such that P[i] = c or
- -1 if no such index exists
The last-occurence function can be computed in time O(m+s), where m is the size of P and s is the size of alphabeta
//Boyer-Moore Algorithm
Algorithm BoyerMooreMatch(T, P, D){
L = lastOccurenceFunction(P, D) //O(m+n)
i = m-1;
j = m -1
repeat
if T[i] = P[i] //run m-1 times in worst case, before character-jump
if j = 0
return i; //match at i
else
i = i - 1;
j = j -1;
else //character-jump, run 1 times every m times in worst case
l = L[T[i]];
i = i + m - min(j, 1 + l);
j = m - 1;
until i > n - 1 //run n times in worst case
return -1 //no match
}
Boyer-Moore’s algorithm runs in time O(nm + s)
The worst case
KMP Algorithm
Preprocessing Pattern
KMP failure Function
preprocesses the pattern to find matches of prefixes of the pattern.
The failure function can be represented by an array and can be computed in O(m) time
Algorithm failure Function(P)
F[0] = 0;
i = 1;
j = 0;
while i < m
if P[i] = P[j] //matched j + 1 char
F[i] = j + 1;
i = i + 1;
j = j + 1;
else if j >0 //use failure function to shift P
j = F[j - 1];
else //no match
F[i] = 0;
i = i + 1;
The KMP algorithm
The KMP’s algorithm runs in optimal time O(m+n)
Algorithm KMPMatch(T, P)
F = failureFuncion(P);
i = 0;
j = 0;
while i < n
if T[i] = P[j] //char match
if j = m - 1 //match
return i - j;
else
i = i + 1;
j = j + 1;
else //failure to match
if j > 0
j = F[j - 1]
else
i = i + 1
return -1; //no match
Preprocessing Text
If the text is large, immutable and searched for often, proprocessing the text instead of the pattern
Standard Tries
- O(n) space and support searches
- O(dm) insertion
- O(dm) deletion
n is total size of the string in S
m size of the string parameter of the operation
d size of the alphabet
Compressed Tries
Graph
A graph is a pair (V, E) where
- V is a set of nodes, called vertices
- E is a collection of pairs of vertices, called edges
- Vertices and edges are positions and store elements
Edge type:
- Directed edge(ordered pair of vertices)
- Undiected edge(unordered pair of vertices)
Properties
- In an undirected graph with no self-loops and no multiple edges
Graph Representations
Adjacency matrix
Advantages
- easily implemented as 2-dimensional asrray
- can represent graph, digraphs and weighted graphs
Disadvantages
- if few edges(sparse) => memory-inefficient
Space complexity is
Graph Initialization
newGraph(n)
input number of nodes n
Output new empty graph
g.nV = n;
g.nE =0;
allocate memory to g.edges[][]
for all i, j = 0 ... n-1 do
g.edges[i][j] = 0;
return g;
Edge Insertion
insertEdge(g, (v, w))
input : graph g, edge(v, w)
if (g.edges[v][w] = 0)
g.edges[v][w] = 1;
g.edges[v][w] = 1;
g.nE = g.nE + 1;
Edge Removal
RemoveEdge(g, (v, w))
Input graph g, edge(v, w)
if g.edges[v][w] = 0
g.edge[v][w] = 0;
g.edge[w][v] = 0;
g.nE = g.nE - 1;
Adjacency lists
Advantages
- relatively easy to implement
- memory efficient if E:V relatively small
Disadvantages:
- one graph has many possible representations unless lists are ordered by same criterion.
space complexity is
Graph Initialization
newGraph(n)
Input number of nodes n
Output new empty graph
g.nV = n;
g.nE = 0;
allocate memory for g.edges[]
for all i = 0...n-1 do
g.edges[i] = NULL
return g
Edge Insertion
if don’t check for duplicates
insertEdge(g, (v, w))
Input: graph g, edge (v, w)
if inLL(g.edges[v], w)
insertLL(g.edges[v], w)
insertLL(g.edges[w], v)
g.nE = g.nE + 1;
Edge Removal
removeEdge(g, (v, w))
Input graph g, edge (v, w)
if inLL(g.edges[v], w)
delete(g.edges[v], w);
delete(g.edges[w], v);
g.nE = g.nE - 1;