Presentation is loading. Please wait.

Presentation is loading. Please wait.

Data Structures and Algorithm Analysis

Similar presentations


Presentation on theme: "Data Structures and Algorithm Analysis"— Presentation transcript:

1 Data Structures and Algorithm Analysis
Lecturer: Jing Liu

2 Textbook Mark Allen Weiss, Data Structures and Algorithm Analysis in C, China Machine Press.

3 What are Data Structures and Algorithms?
Data Structures are methods of organizing large amounts of data. An algorithm is a procedure that consists of finite set of instructions which, given an input from some set of possible inputs, enables us to obtain an output if such an output exists or else obtain nothing at all if there is no output for that particular input through a systematic execution of the instructions.

4 Instructions Inputs (Problems) Outputs (Answers) Computers

5 Software Systems Programming Languages Data Structure Algorithms

6 Contents Chapter 3 Lists, Stacks, and Queues Chapter 4 Trees
Chapter 5 Hashing Chapter 6 Priority Queues (Heaps) Chapter 7 Sorting Chapter 8 The Disjoint Set ADT Chapter 9 Graph Algorithms Chapter 10 Algorithm Design Techniques

7 Abstract Data Types (ADTs)
One of the basic rules concerning programming is to break the program down into modules. Each module is a logical unit and does a specific job. Its size is kept small by calling other modules. Modularity has several advantages. (1) It is much easier to debug small routines than large routines; (2) It is easier for several people to work on a modular program simultaneously; (3) A well-written modular program places certain dependencies in only one routing, making changes easier.

8 Abstract Data Types (ADTs)
An abstract data type (ADT) is a set of operations. Abstract data types are mathematical abstractions; nowhere in an ADT’s definition is there any mention of how the set of operations is implemented. Objects such as lists, sets, and graphs, along with their operations, can be viewed as abstract data types, just as integers, reals, and booleans are data types. Integers, reals, and booleans have operations associated with them, and so do ADTs.

9 Abstract Data Types (ADTs)
The basic idea is that the implementation of the operations related to ADTs is written once in the program, and any other part of the program that needs to perform an operation on the ADT can do so by calling the appropriate function. If for some reason implementation details need to be changed, it should be easy to do so by merely changing the routings that perform the ADT operations. There is no rule telling us which operations must be supported for each ADT; this is a design decision.

10 The List ADT The form of a general list: A1, A2, A3, …, AN;
The size of this list is N; An empty list is a special list of size 0; For any list except the empty list, we say that Ai+1 follows (or succeeds) Ai (i<N) and that Ai-1 precedes Ai (i>1); The first element of the list is A1, and the last element is AN. We will not define the predecessor of A1 or the successor of AN. The position of element Ai in a list is i.

11 The List ADT There is a set of operations that we would like to perform on the list ADT: PrintList MakeEmpty Find: return the position of the first occurrence of a key Insert and Delete: insert and delete some key from some position in the list FindKth: return the element in some position Next and Previous: take a position as argument and return the position of the successor and predecessor

12 The List ADT Example: The list is 34, 12, 52, 16, 13 Find(52)
Insert(X, 3) Delete(52) The interpretation of what is appropriate for a function is entirely up to the programmer.

13 Simple Array Implementation of Lists
All these functions about lists can be implemented by using an array. PrintList MakeEmpty Find Insert Delete Next Previous

14 Simple Array Implementation of Lists
Disadvantages: An estimate of the maximum size of the list is required, even if the array is dynamically allocated. Usually this requires a high overestimate, which wastes considerable space. Insertion and deletion are expensive. For example, inserting at position 0 requires first pushing the entire array down one spot to make room. Because the running time for insertions and deletions is so slow and the list size must be known in advance, simple arrays are generally not used to implement lists.

15 Linked Lists In order to avoid the linear cost of insertion and deletion, we need to ensure that the list is not stored contiguously, since otherwise entire parts of the list will need to be moved. A1 A2 A3 A4 A5 A linked list The linked list consists of a series of structures, which are not necessarily adjacent in memory. Each structure contains the element and a pointer to a structure containing its successor. We call this the Next pointer. The last cell’s Next pointer points to NULL;

16 Linked list with actual pointer values
Linked Lists If P is declared to be a pointer to a structure, then the value stored in P is interpreted as the location, in main memory, where a structure can be found. A field of that structure can be accessed by P->FieldName, where FieldName is the name of the field we wish to examine. A1 800 A2 712 A3 992 A4 692 A5 1000 800 712 992 692 Linked list with actual pointer values In order to access this list, we need to know where the first cell can be found. A pointer variable can be used for this purpose.

17 Linked Lists To execute PrintList(L) or Find(L, Key), we merely pass a pointer to the first element in the list and then traverse the list by following the Next pointers. The Delete command can be executed in one pointer change. A1 A2 A3 A4 A5 The Insert command requires obtaining a new cell from the system by using a malloc call and then executing two pointer maneuvers. A1 A2 A3 A4 A5 X

18 Linked Lists There are several places where you are likely to go wrong: (1) There is no really obvious way to insert at the front of the list from the definitions given; (2) Deleting from the front of the list is a special case, because it changes the start of the list; careless coding will lose the list; (3) A third problem concerns deletion in general. Although the pointer moves above are simple, the deletion algorithm requires us to keep track of the cell before the one that we want to delete.

19 Linked list with a header
Linked Lists One simple change solves all three problems. We will keep a sentinel node, referred to an a header or dummy node. Header A1 A2 A3 A4 A5 Linked list with a header L To avoid the problems associated with deletions, we need to write a routing FindPrevious, which will return the position of the predecessor of the cell we wish to delete. If we use a header, then if we wish to delete the first element in the list, FindPrevious will return the position of the header.

20 Doubly Linked Lists Sometimes it is convenient to traverse lists backwards. The solution is simple. Merely add an extra field to the data structure, containing a pointer to the previous cell. The cost of this is an extra link, which adds to the space requirement and also doubles the cost of insertions and deletions because there are more pointers to fix. A1 A2 A3 A4 A5 A doubly linked list How to implement doubly linked lists?

21 Circularly Linked Lists
A popular convention is to have the last cell keep a pointer back to the first. This can be done with or without a header. If the header is present, the last cell points to it. It can also be done with doubly linked lists, the first cell’s previous pointer points to the last cell. A1 A2 A3 A4 A5 A double circularly linked list

22 Example – The Polynomial ADT
If most of the coefficients Ai are nonzero, we can use a simple array to store the coefficients. Write codes to calculate F(X) based on array.

23 Example – The Polynomial ADT
If most of the coefficients Ai are zero, the implementation based on array is not efficient, since most of the time is spent in multiplying zeros. 10 1000 5 14 1 P1 An alternative is to use a singly linked list. Each term in the polynomial is contained in one cell, and the cells are sorted in decreasing order of exponents.

24 Stack ADT A stack is a list with the restriction that insertions and deletions can be performed in only one position, namely, the end of the list, called the top. The fundamental operations on a stack are Push, which is equivalent to an insert, and Pop, which deletes the most recently inserted element. The most recently inserted element can be examined prior to performing a Pop by use of the Top routine.

25 Stack ADT A Pop or Top on an empty stack is generally considered an error in the stack ADT. Running out of space when performing a Push is an implementation error but not an ADT error. Stacks are sometimes known as LIFO (last in, first out) lists.

26 Stack ADT Stack model: only the top element is accessible Top 7 9 4 2
3 6 Stack model: only the top element is accessible

27 Implementation of Stacks
Since a stack is a list, any list implementation will do. We will give two popular implementations. One uses pointers and the other uses an array. No matter in which case, if we use good programming principles, the calling routines do not need to know which method is being used.

28 Linked List Implementation of Stacks
We perform a Push by inserting at the front of the list We perform a Pop by deleting the element at the front of the list A Top operation merely examines the element at the front of the list, returning its value.

29 Array Implementation of Stacks
A Stack is defined as a pointer to a structure. The structure contains the TopOfStack and Capacity fields. Once the maximum size is known, the stack array can be dynamically allocated. Associated with each stack is TopOfStack, which is -1 for an empty stack (this is how an empty stack is initialized).

30 Array Implementation of Stacks
To push some element X onto the stack, we increment TopOfStack and then set Stack[TopOfStack]=X, where Stack is the array representing the actual stack. To pop, we set the return value to Stack[TopOfStack] and then decrement TopOfStack.

31 Example – Conversion of Numbers
We have many different data systems, like Decimal system, Binary system, Hexadecimal system, Octal system Convert a decimal number to a binary number Decimal Number Divisor Quotient Remainder 30 2 15 7 1 3 Function calls.

32 The Queue ADT Like stacks, queues are lists.
With a queue, however, insertion is done at one end, whereas deletion is performed at the other end. The basic operations on a queue are Enqueue, which inserts an element at the end of the list (called the rear), and Dequeue, which deletes (and returns) the element at the start of the list (known as the front).

33 Array Implementation of Queues
For each queue data structure, we keep an array, Queue[], and the positions Front and Rear, which represent the ends of the queue. We also keep track of the number of elements that are actually in the queue, Size. All this information is part of one structure. The following figure shows a queue in some intermediate state. The cells that are blanks have undefined values in them: 5 2 7 1 Front Rear

34 Array Implementation of Queues
To Enqueue an element X, we increment Size and Rear, then set Queue[Rear]=X. To Dequeue an element, we set the return value to Queue[Front], decrement Size, and then increment Front. Whenever Front or Rear gets to the end of the array, it is wrapped around to the beginning. This is known as a circular array implementation.

35 Dequeue, which returns 3 and makes the Queue empty 1 3 2 4
Initial State 2 4 Front Rear 1 2 4 Enqueue (1) Rear Front Enqueue (3) 1 3 2 4 Rear Front Dequeue, which returns 2 1 3 2 4 Rear Front Dequeue, which returns 4 1 3 2 4 Front Rear Dequeue, which returns 1 1 3 2 4 Rear Front Dequeue, which returns 3 and makes the Queue empty 1 3 2 4 Rear Front

36 Linked List Implementation of Queues
Front Header …… Rear /

37 Linked List Implementation of Queues
Front Empty Queue / Reart Front Enqueue x x / Reart Front Enqueue y x y / Reart Front Dequeue x x y / Reart

38 Example Applications When jobs are submitted to a printer, they are arranged in order of arrival. Every real-life line is a queue. For instance, lines at ticket counters are queues, because service is first-come first-served. A whole branch of mathematics, known as queueing theory, deals with computing, probabilistically, how long users expect to wait on a line, how long the line gets, and other such questions.

39 Data Structures and Algorithm Analysis Trees
Lecturer: Jing Liu Homepage:

40 Preliminaries A tree can be defined in several ways. One natural way to define a tree is recursively. A tree is a collection of nodes. The collection can be empty; otherwise, a tree consists of a distinguished node r, called the root, and zero or more nonempty (sub)trees T1, T2, …, Tk, each of whose roots are connected by a directed edge from r. The root of each subtree is said to be a child of r, and r is the parent of each subtree root. root T1 T2 T3 T4 T10

41 Preliminaries From the recursive definition, we find that a tree is a collection of N nodes, one of which is the root, and N-1 edges. That there are N-1 edges follows from the fact that each edge connects some node to its parent, and every node except the root has one parent. The root is A. Node E has A as a parent and I, J as children. Each node may have an arbitrary number of children, possibly zero. Nodes with no children are known as leaves. Nodes with the same parent are siblings. Grandparent and grandchild relations can be defined in a similar manner. A B C D E F G H I J K L

42 Preliminaries A path from node n1 to nk is defined as a sequence of nodes n1, n2, n3…, nk such that ni is the parent of ni+1 for 1i<k. The length of this path is the number of edges on the path, namely k-1. There is a path of length zero from every node to itself. Notice that in a tree there is exactly one path from the root to each node.

43 Preliminaries For any node ni, the depth of ni is the length of the unique path from the root to ni. Thus, the root is at depth 0. The height of ni is the length of the longest path from ni to a leaf. Thus all leaves are at height 0. The height of a tree is equal to the height of the root. The depth of a tree is equal to the depth of the deepest leaf; this is always equal to the height of the tree. If there is a path from n1 to n2, then n1 is an ancestor of n2 and n2 is a descendant of n1. If n1n2, then n1 is a proper ancestor of n2 and n2 is a proper descendant of n1.

44 Preliminaries For example, E is at depth 1 and height 2; D is at depth 1 and height 1; the height of the tree is 3. A B C D E F G H I J K L

45 Implementation of Trees
One way to implement a tree would be to have in each node, besides its data, a pointer to each child of the node. However, since the number of children per node can vary so greatly and is not known in advance, it might be infeasible to make the children direct links in the data structure, because there would be too much wasted space. The solution is simple: Keep the children of each node in a linked list of tree nodes.

46 Implementation of Trees
struct TreeNode { char Element; TreeNode* FirstChild; TreeNode* NextSibling; }

47 Implementation of Trees
B C D E F G B C D E F G H I J H I J K L K L Arrows that point downward are FirstChild pointers. Arrows that go left to right are NextSibling pointers. Node E has both a pointer to a sibling (F) and a pointer to a child (I), while some nodes have neither.

48 Implementation of Trees
Example: Please give the first child/next sibling representation of the following tree. A B C D E H I J F G K L

49 Application of Trees There are many applications for trees. One of the popular uses is the directory structure in many common operating systems. /usr* mark* alex* bill* course* junk.c prog1.r prog2.r The root of this directory is /usr. The asterisk next to the name indicates that /usr is itself a directory.

50 Tree Traversal The purpose of tree traversal: visit (perform some operations on) each node in a tree systematically: Preorder traversal: the operations at a node are performed before (pre) its children are processed. Postorder traversal: the operations at a node are performed after (post) its children are processed. The operations on each node are performed recursively.

51 Tree Traversal Example: Suppose the operation on each node is print the name of this node. Please give the outputs of the preorder and postorder traversals on the following tree. A B C D E H I J F G K L

52 Tree Traversal Answer: Preorder traversal: A B F G C H K L D E I J
Postorder traversal: F G B K L H C D I J E A

53 Tree Traversal A Example: Suppose the operation on each node is print the name of this node. Please give the outputs of the preorder and postorder traversals on the left tree. B C D E F G H I J K L

54 Tree Traversal Answer: Preorder traversal: A B C E H I J K L D F G
Postorder traversal: B H I K L J E C F G D A

55 Tree Traversal Write codes to implement the preorder and postorder tree traversal.

56 Binary Trees A binary tree is a tree in which no node can have more than two children. A property of a binary tree that is sometimes important is that the depth of an average binary tree is considerably smaller than N if the tree has N nodes. A root B C TR TL Generic binary tree: a root and two subtrees, TL and TR, both of which could possibly be empty D Worst-case binary tree

57 Implementation of Binary Trees
Because a binary tree has at most two children, we can keep direct pointers to them. A node is a structure consisting of the Key information plus two pointers (Left and Right) to other nodes. Many of the rules that apply to linked lists will apply to trees as well. When an insertion is performed, a node will have to be created by a call to malloc. Nodes can be freed after deletion by calling free.

58 Implementation of Binary Trees
struct BinaryTreeNode { char Element; BinaryTreeNode* Left; BinaryTreeNode* Right; }

59 Binary Tree Traversal Preorder traversal: First, the operations at the node are performed; second, the left child, and then the right child. Postorder traversal: First, the operations at a node’s left child are performed; second, the right child, and then the node. Inorder traversal: First, the operations at a node’s left child are performed; second, the node, and then the right node.

60 Binary Tree Traversal Example: Suppose the operation on each node is print the name of this node. Please give the outputs of the preorder, postorder, and inorder traversals on the left tree. A B C D E F G H I J K L

61 Binary Tree Traversal Answer:
Preorder traversal: A B D E G I J L H K C F Postorder traversal: D I L J G K H E B F C A Inorder traversal: D B I G L J E H K A C F

62 Binary Tree Traversal Example: Suppose the operation on each node is print the name of this node. Please give the outputs of the preorder, postorder, and inorder traversals on the left tree. A B C H D G K E I J L

63 Binary Tree Traversal Answer:
Preorder traversal: A B D E C G I J L H K Postorder traversal: E D B I L J G K H C A Inorder traversal: D E B A I G L J C H K

64 Binary Tree Traversal Write codes to implement the preorder, postorder, and inorder binary tree traversal.

65 Expression Trees One of the principal uses of binary trees is in the area of compiler design. Expression tree for (a+b*c)+((d*e+f)*g) + The leaves of an expression tree are operands, such as constants or variable names The other nodes contain operators. This particular tree happens to be binary, because all of the operations are binary It is possible for nodes to have more than two children. It is also possible for a node to have only one child, such as unary minus operator We can evaluate an expression tree, T, by applying the operator at the root to the values obtained by recursively evaluating the left and right subtrees. In this example, the left subtree evaluates to a+(b*c) and the right subtree evaluates to ((d*e)+f)*g. The entire tree therefore represents (a+(b*c))+(((d*e)+f)*g). + * a * + g b c * f d e

66 Expression Trees We can produce an (overly parenthesized) infix expression by recursively producing a parenthesized left expression, then printing out the operator at the root, and finally recursively producing a parenthesized right expression. This general strategy (left, node, right) is an inorder traversal; An alternate traversal strategy is to recursively print out the left subtrees, the right subtrees, and then the operators. If we apply this strategy to our tree above, the output is abc*+de*f+g*+, which is easily seen to be the postfix representation. This traversal strategy (left, right, node) is a postorder traversal. A third traversal strategy is to print out the operator first and then recursively print out the left and right subtrees. The resulting expression, ++a*bc*+*defg, is the less useful prefix notation and the traversal strategy (node, left, right) is a preorder traversal.

67 Constructing an Expression Tree
We now give an algorithm to convert a postfix expression into an expression tree. We read the expression one symbol at a time. If the symbol is an operand, we create a one-node tree and push a pointer to it onto a stack. If the symbol is an operator, we pop pointers to two trees T1 and T2 from the stack (T1 is popped first) and form a new tree whose root is the operator and whose left and right children point to T2 and T1, respectively. A pointer to this new tree is then pushed onto the stack.

68 Constructing an Expression Tree
Input: ab+cde+** The first two symbols are operands, so we create one-node trees and push pointers to them onto a stack. For convenience, we will have the stack grow from left to right in the diagrams. a b Next, a ‘+’ is read, so two pointers to trees are popped, a new tree is formed, and a pointer to it is pushed onto the stack. + a b Next, c, d, and e are read, and for each a one-node tree is created and a pointer to the corresponding tree is pushed onto the stack. + c d e a b

69 Constructing an Expression Tree
Now a ‘+’ is read, so two trees are merged. + c + a b d e Continuing, a ‘*’ is read, so we pop two tree pointers and form a new tree with a ‘*’ as root. + * a b c + d e

70 Constructing an Expression Tree
Finally, the last symbol is read, two trees are merged, and a pointer to the final tree is left on the stack. * + * a b c + d e

71 Constructing an Expression Tree
Write codes to implement the process of constructing an expression tree.

72 The Search Tree ADT-Binary Search Trees
An important application of binary trees is their use in searching. Let us assume that each node in the tree is assigned a key value. We will also assume that all the keys are distinct. The property that makes a binary tree into a binary search tree is that for every node, X, in the tree, the values of all the keys in its left subtree are smaller than the key value in X, and the values of all the keys in its right subtree are larger than the key value in X. Notice that this implies that all the elements in the tree can be ordered in some consistent manner.

73 The Search Tree ADT-Binary Search Trees
6 6 2 8 2 8 1 4 1 4 3 3 7 The tree on the left is a binary search tree, but the tree on the right is not.

74 The Search Tree ADT-Binary Search Trees
We now give brief descriptions of the operations that are usually performed on binary search trees. Note that because of the recursive definition of trees, it is common to write these routings recursively. (1) MakeEmpty: this operation is mainly for initialization. (2) Find: This operation generally requires returning a pointer to the node in tree T that has key X, or NULL if there is no such node. If T is NULL, then we can just return NULL. Otherwise, if the key stored at T is X, we can return T. Otherwise, we make a recursive call on a subtree of T, either left or right, depending on the relationship of X to the key stored in T. (3) FindMin and FindMax: These routines return the position of the smallest and largest elements in the tree, respectively.

75 The Search Tree ADT-Binary Search Trees
(4) Insert: To insert X into tree T, proceed down the tree as you would with a Find. If X is found, do nothing (or “update” something). Otherwise, insert X at the last spot on the path traversed. Example: To insert 5, we traverse the tree as though a Find were occurring. At the node with key 4, we need to go right, but there is no subtree, so 5 is not in the tree, and this is the correct spot. 6 6 2 2 8 8 Binary search trees before and after inserting 5. 1 4 1 4 3 3 5

76 The Search Tree ADT-Binary Search Trees
(5) Delete: Once we have found the node to be deleted, we need to consider several possibilities. (a) If the node is a leaf, it can be deleted immediately. (b) If the node has one child, the node can be deleted after its parent adjusts a pointer to bypass the node. 6 6 Deletion of a node (4) with one child, before and after. 2 2 8 8 1 4 1 4 3 3

77 The Search Tree ADT-Binary Search Trees
(c) The complicated case deals with a node with two children. The general strategy is to replace the data of this node with the smallest data of the right subtree and recursively delete that node. Because the smallest node in the right subtree cannot have a left child, the second Delete is an easy one. 6 6 2 8 3 8 Deletion of a node (2) with two children, before and after. Node (2) is replaced with the smallest data in its right subtree (3), and then that node is deleted as before. 1 5 1 5 3 3 4 4

78 The Search Tree ADT-Binary Search Trees
Write codes to implement the previous operations.

79 Binary Search Tree Traversals
Inorder traversal Preorder traversal Postorder traversal

80 Data Structures and Algorithm Analysis Hashing
Lecturer: Jing Liu Homepage:

81 General Idea Purpose: Design a kind of tables that can perform insertions, deletions, and finds in constant average time. In this chapter, we discuss the Hash table ADT. The ideal hash table data structure is merely an array of some fixed size, containing the keys. Typically, a key is a string with an associated value (for instance, salary information).

82 General Idea The implementation of hash tables is frequently called hashing. We will refer to the table size as TableSize. The common convention is to have the table run from 0 to TableSize-1.

83 General Idea Each key is mapped into some number in the range 0 to TableSize-1 and placed in the appropriate cell. The mapping is called a hash function, which ideally should be simple to compute and should ensure that any two distinct keys get different cells. Since there are a finite number of cells and a virtually inexhaustible supply of keys, this is clearly impossible, and thus we seek a hash function that distributes the keys evenly among the cells.

84 General Idea 1 John 25000 2 3 Phil 45000 4 5 6 Dave 15000 7 Mary 35000
1 John 25000 2 3 Phil 45000 4 5 6 Dave 15000 7 Mary 35000 The only remaining problems deal with choosing a function, deciding what to do when two keys hash to the same value (this is known as a collision), and deciding on the table size.

85 Hash Function If the input keys are integers, when simply returning (Key mod TableSize) is generally a reasonable strategy, unless Key happens to have some undesirable properties. In this case, the choice of hash function needs to be carefully considered. For instance, if the table size is 10 and the keys all end in zero, then the standard hash function is a bad choice.

86 Hash Function To avoid situations like the one above, it is usually a good idea to ensure that the table size is prime. When the input keys are random integers, then this function is not only very simple to compute but also distributes the keys evenly.

87 Hash Function Usually, the keys are strings; in this case, the hash function needs to be chosen carefully. One option is to add up the ASCII values of the characters in the string. int Hash(char *Key, int TableSize) { unsigned int HashVal=0; while (*Key!=‘\0’) HashVal+=*Key++; return HashVal%TableSize; }

88 Hash Function However, if the table size is large, the function does not distribute the keys well. For instance, suppose that TableSize=10007 (10007 is a prime number), and all keys are eight or fewer characters long. Since a char has an integer value that is always at most 127, the hash function can only assume values between 0 and 1016, which is 127*8. This is clearly not an equitable distribution!

89 Hash Function int Hash(char *Key, int TableSize) {
return (Key[0]+27*Key[1]+729*Key[2]) % TableSize; } This hash function assumes that Key has at least two characters plus the NULL terminator. The value 27 represents the number of letters in the English alphabet, plus the blank, and 729 is 272. This function examines only the first three characters, but if these are random and the table size is 10,007, as before, then we would expect a reasonably equitable distribution.

90 Hash Function Unfortunately, English is not random.
Although there are 263=17,576 possible combinations of three characters (ignoring blanks), a check of a reasonably large on-line dictionary reveals that the number of different combinations is actually only 2,851. Even if non of these combinations collide, only 28 percent of the table can actually be hashed to.

91 Collision If, when an element is inserted, it hashes to the same value as an already inserted element, then we have a collision and need to resolve it. There are several methods for dealing with this. We will discuss two of the simplest: separate chaining and open addressing.

92 Separate Chaining The first strategy, separate chaining, is to keep a list of all elements that hash to the same value. For convenience, our lists have headers. We assume for this section that the keys are the first 10 perfect squares, namely 0, 1, 4, 9, 16, 25, 36, 49, 64, 81. The hashing function is simply Hash(X)=X mod 10 (The table size is not prime but is used here for simplicity).

93 Separate Chaining / 1 1 81 / 2 / 3 / 4 4 64 / 5 25 / 16 36 6 / 7 / 8 /
/ 1 1 81 / 2 / 3 / 4 4 64 / 5 25 / 16 36 6 / 7 / 8 / 9 9 49 /

94 Separate Chaining To perform a Find, we use the hash function to determine which list to traverse. We then traverse this list in the normal manner, returning the position where the item is found. To perform an Insert, we traverse down the appropriate list to check whether the element is already in place (if duplicates are expected, an extra field is usually kept, and this field would be incremented in the event of a match). If the element turns out to be new, it is inserted either at the front of the list or at the end of the list, which ever is easiest. The Delete is a straightforward implementation in a linked list.

95 Open Addressing In an open addressing hashing system, if a collision occurs, alternative cells are tried until an empty cell is found. More formally, cells h0(X), h1(X), h2(X), … are tried in succession, where hi(X)=(Hash(X)+F(i)) mod TableSize, with F(0)=0. The function, F, is the collision resolution strategy. There are three common collision resolution strategies, namely Linear Probing, Quadratic Probing, and Double Hashing.

96 Linear Probing In linear probing, F is a linear function of i, typically F(i)=i. This amounts to trying cells sequentially (with wraparound) in search of an empty cell.

97 Linear Probing Example: Keys are {89, 18, 49, 58, 69}. Empty After 89
49 1 58 2 69 3 4 5 6 7 8 18 9 89

98 Quadratic Probing Quadratic probing is a collision resolution method that eliminates the primary clustering problem of linear probing. Quadratic probing the collision function is quadratic. The popular choice is F(i)=i2.

99 Quadratic Probing Empty After 89 After 18 After 49 After 58 After 69
49 1 2 58 3 69 4 5 6 7 8 18 9 89

100 Quadratic Probing For linear probing it is a bad idea to let the hash table get nearly full, because performance degrades. For quadratic probing, the situation is even more drastic: There is no guarantee of finding an empty cell once the table gets more than half full, or even before the table gets half full if the table size is not prime. This is because at most half of the table can be used as alternative locations to resolve collisions.

101 Double Hashing The last collision resolution method we will examine is double hashing. For double hashing, one popular choice is F(i)=i*hash2(X) This formula says that we apply a second hash function to X and probe at a distance hash2(X), 2hash2(X), …, and so on. A function such as hash2(X)=R-(X mod R), with R a prime smaller than TableSize, will work well. Here, R is set to 7.

102 Double Hashing Empty After 89 After 18 After 49 After 58 After 69 69 1
69 1 2 3 58 4 5 6 49 7 8 18 9 89

103 Data Structures and Algorithm Analysis Priority Queues (Heaps)
Lecturer: Jing Liu Homepage:

104 General Idea Example: Suppose there are a sequence of jobs are sent to a printer. Although jobs sent to a printer are generally placed on a queue, this might not always be the best thing to do. For instance, if, when the printer becomes available, there are several 1-page jobs and one 100-page job, it might be reasonable to make the long job go last, even if it is not the last job submitted. This particular application seems to require a special kind of queue, known as a priority queue.

105 Model A priority queue is a data structure that allows at least the following two operations: (1) Insert: is the equivalent of Enqueue (2) DeleteMin: finds, returns, and removes the minimum element in the priority queue. Insert(H) DeleteMin(H) Priority Queue H

106 Simple Implementations
There are several obvious ways to implement a priority queue. We could use a simple linked list, performing insertions at the front and traversing the list to delete the minimum. Alternatively, we could insist that the list be kept always sorted. Another way of implementing priority queues would be to use a binary search tree. Recall that the only element we ever delete is the minimum. Repeatedly removing a node that is in the left subtree would seem to hurt the balance of the tree by making the right subtree heavy.

107 Binary Heap The implementation we will use is known as a binary heap.
Like binary search trees, binary heaps have two properties, namely, a structure property and a heap order property.

108 Structure Property Structure property: A heap is a binary tree that is completely filled, with the possible exception of the bottom level, which is filled from left to right. Such a tree is known as a complete binary tree. A B C D E F G H I J

109 Structure Property An important observation is that because a complete binary tree is so regular, it can be represented in an array and no pointers are necessary. For any element in array position i, the left child is in position 2i, the right child is in the cell after that left child (2i+1), and the parent is in position i/2.

110 Structure Property A B C D E F G H I J A B C D E F G H I J 1 2 3 4 5 6
1 2 3 4 5 6 7 8 9 10 11 12 13

111 Structure Property Thus, not only are pointers not required, but the operations required to traverse the tree are extremely simple and likely to be very fast on most computers. The only problem with this implementation is that an estimate of the maximum heap size is required in advance. A heap data structure will then consist of an array (of whatever type the key is) and an integer representing the maximum and current heap sizes. Figure 6.4 shows a typical priority queue declaration.

112 Structure Property Struct HeapStruct { int Capacity; int Size;
elementType *Elements; } Initialize(int MaxElements) { Line 3: H=malloc(sizeof(struct HeapStruct)); Line 6: H->Elements= malloc((MaxElements+1)*sizeof(ElementType)); }

113 Heap Order Property The property that allows operations to be performed quickly is the heap order property. Since we want to be able to find the minimum quickly, it makes sense that the smallest element should be at the root. If we consider that any subtree should also be a heap, then any node should be smaller than all of its descendants.

114 Heap Order Property Applying this logic, we arrive at the heap order property: In a heap, for every node X, the key in the parent of X is smaller than (or equal to) the key in X, with the exception of the root (which is has no parent). Analogously, we can declare a (max) heap, which enables us to efficiently find and remove the maximum element, by changing the heap order property. Thus, a priority queue can be used to find either a minimum or a maximum, but this needs to be decided ahead of time.

115 Basic Heap Operations It is easy (both conceptually and practically) to perform the two required operations, namely Insert and DeleteMin. All the work involves ensuring that the heap order property is maintained.

116 Basic Heap Operations Insert: To insert an element X into the heap, we create a hole in the next available location, since otherwise the tree will not be complete. If X can be placed in the hole without violating heap order, then we do so and are done. Otherwise we slide the element that is in the hole’s parent node into the hole, thus bubbling the hole up towards the root. We continue this process until X can be placed in the hole.

117 Basic Heap Operations Example: 13 21 16 Insert 14 24 31 19 68 65 26 32
Original Tree

118 Basic Heap Operations 13 13 21 16 21 16 24 31 19 24 31 19 68 68 65 26 32 65 26 32

119 Basic Heap Operations 13 13 21 16 21 16 24 31 19 24 19 68 68 65 26 32 65 26 32 31

120 Basic Heap Operations 13 13 21 16 16 24 19 24 21 19 68 68 65 26 32 31 65 26 32 31

121 Basic Heap Operations 13 13 16 14 16 24 21 19 24 21 19 68 68 65 26 32 65 26 31 32 31

122 Basic Heap Operations DeleteMin: DeleteMins are handled in a similar manner as insertions. Finding the minimum is easy; the hard part is removing it. When the minimum is removed, a hole is created at the root. Since the heap now becomes one smaller, it follows that the last element X in the heap must move some where in the heap.

123 Basic Heap Operations If X can be placed in the hole, then we are done. This is unlikely, so we slide the smaller of the hole’s children into the hole, thus pushing the hole down one level. We repeat this step until X can be placed in the hole. Thus, our action is to place X in its correct spot along a path from the root containing minimum children.

124 Basic Heap Operations 13 Example: Delete the minimum key 14 16 19 21
68 65 26 32 31 Original Tree

125 Basic Heap Operations 13 14 16 14 16 19 21 19 68 19 21 19 68 65 26 32 31 65 26 32 31

126 Basic Heap Operations 14 14 16 16 19 21 19 68 19 21 19 68 65 26 32 31 65 26 32 31

127 Basic Heap Operations 14 14 16 19 16 19 21 19 68 21 19 68 65 26 32 31 65 26 32 31

128 Basic Heap Operations 14 14 19 16 19 16 21 19 68 26 21 19 68 65 26 32 31 65 32 31

129 Basic Heap Operations 14 14 19 16 19 16 26 21 19 68 26 21 19 68 65 32 31 65 31 32

130 d-Heap Binary heaps are so simple that they are almost always used when priority queues are needed. A simple generalization is a d-heap, which is exactly like a binary heap except that all nodes have d children (thus, a binary heap is a 2-heap).

131 d-Heap 1 2 3 5 4 7 10 13 15 6 8 17 9 11 9 An example of 3-heap

132 Data Structures and Algorithm Analysis Disjoint Set ADT
Lecturer: Jing Liu Homepage:

133 The Disjoint Set ADT In this part, we describe an efficient data structure to solve the equivalence problem. Relations: A relation R is defined on a set S if for every pair of elements (a, b), a, bS, a R b is either true or false. If a R b is true, then we say that a is related to b.

134 The Disjoint Set ADT Equivalence Relations: An equivalence relation is a relation R that satisfies three properties: Reflexive: a R a, for all aS; Symmetric: a R b if and only if b R a; Transitive: a R b and b R c implies that a R c; The  relationship is not an equivalence relationship. Two cities are related if they are in the same country. This relation is an equivalence relation if all the roads are two-way.

135 The Disjoint Set ADT Given an equivalence relation ~, the natural problem is to decide, for any a and b, if a~b. The equivalence class of an element aS is the subset of S that contains all the elements that are related to a. Notice that the equivalence classes from a partition of S: Every member of S appears in exactly one equivalence class.

136 The Disjoint Set ADT To decide if a~b, we need only to check whether a and b are in the same equivalence class. This provides our strategy to solve the equivalence problem. The input is initially a collection of N sets, each with one element. Each set has a different element. There are two permissible operations.

137 The Disjoint Set ADT The first is Find, which returns the name of the set (that is, the equivalence class) containing a given element. The second operation adds relations. If we want to add the relation a~b, then we first see if a and b are already related. This is done by performing Finds on both a and b and checking whether they are in the same equivalence class. If they are not, then we apply Union. This operation merges the two equivalence classes containing a and b into a new equivalence class.

138 The Disjoint Set ADT Notice that we do not perform any operations comparing the relative values of elements, but merely require knowledge of their location. For this reason, we can assume that all the elements have been numbered sequentially from 1 to N. Thus, initially we have Si={i} for i=1 through N.

139 The Disjoint Set ADT Our second observation is that the name of the set returned by Find is actually fairly arbitrary. All that really matters is that Find(a)=Find(b) if and only if a and b are in the same set. Thus, one idea might be to use a tree to represent each set, since each element in a tree has the same root. Therefore, the root can be used to name the set.

140 The Disjoint Set ADT We will represent each set by a tree. Initially, each set contains one element. The representation of the trees is easy, because the only information we will need is a parent pointer. Since only the name of the parent is required, we can assume that this tree is stored implicitly in an array: each entry P[i] in the array represents the parent of element i. If i is a root, then P[i]=0.

141 Eight elements, initially in different sets
The Disjoint Set ADT 1 2 3 4 5 6 7 8 Eight elements, initially in different sets 1 2 3 4 5 6 7 8

142 The Disjoint Set ADT To perform a Union of two sets, we merge the two trees by making the root pointer of one node point to the root node of the other tree. We have adopted the convention that the new root after the Union(X, Y) is X. After Union(5,6) 5 1 2 3 4 5 7 8 1 2 3 4 5 6 7 8 6

143 The Disjoint Set ADT After Union(7,8) After Union(5,7) 5 7 1 2 3 4 5 7
5 7 1 2 3 4 5 7 1 2 3 4 5 6 7 8 6 8 After Union(5,7) 1 2 3 4 5 5 5 7 1 2 3 4 5 6 7 8 6 7 8

144 The Disjoint Set ADT typedef int DisjSet[NumSets+1]; A Find(X) on element X is performed by returning the root of the tree containing X. The implementation of the basic algorithm is as follows: void Initialize(DisjSet S) { int i; for(i=NumSets; i>0; i--) S[i]=0; } void SetUnion(DisjSet S, int Root1, int Root2) { S[Root2]=Root1; } int Find(int X, DisjSet S) { if (S[X]<=0) return X; else return Find(S[X], S); }

145 The Disjoint Set ADT Smart Union Algorithms: The unions above were performed rather arbitrarily, by making the second tree a subtree of the first. A simple improvement is always to make the smaller tree a subtree of the larger, breaking ties by any method; we call this approach union-by-size. Had the size heuristic not been used, a deeper tree would have been formed.

146 The Disjoint Set ADT 1 2 3 4 5 6 7 8 Original Trees 1 2 3 5 1 2 3 4 4
Union(4,5): Union-by-size Union(4,5): Arbitrary Union 8

147 The Disjoint Set ADT An alternative implementation is union-by-height. We keep track of the height, instead of the size, of each tree and perform Unions by making the shallow tree a subtree of the deeper tree. This is an easy algorithm, since the height of a tree increases only when two equally deep trees are joined (and then the height goes up by one). Thus, union-by-height is a trivial modification of union of union-by-size.

148 The Disjoint Set ADT An application: We have a network of computers and a list of bidirectional connections; each of these connections allows a file transfer from one computer to another. Is it possible to send a file from any computer on the network to any other? An extra restriction is that the problem must be solved on-line. Thus, the list of connections is presented one at a time, and the algorithm must be prepared to given an answer at any point.

149 The Disjoint Set ADT An algorithm to solve this problem can initially put every computer in its own set. Our invariant is that two computers can transfer files if and only if they are in the same set. We can see that the ability to transfer files forms an equivalence relation. We then read connections one at a time. When we read some connection, say (u, v), we test to see whether u and v are in the same set and do nothing if they are. If they are in different sets, we merge their sets. At the end of the algorithm, the graph is connected if and only if there is exactly one set.

150 Data Structures and Algorithm Analysis Algorithm Analysis and Sorting
Lecturer: Jing Liu Homepage:

151 What’s Algorithms? An algorithm is a procedure that consists of a finite set of instructions which, given an input from some set of possible inputs, enables us to obtain an output if such an output exists or else obtain nothing at all if there is no output for that particular input through a systematic execution of the instructions.

152 Instructions Inputs (Problems) Outputs (Answers) Computers

153 Software Systems Programming Languages Data Structure Algorithms

154 Binary Search Let A[1…n] be a sequence of n elements. Consider the problem of determining whether a given element x is in A.

155 Binary Search Example: A[1…14]=1 4 5 7 8 9 10 12 15 22 23 27 32 35
Does x exist in A? How many comparisons do you need to give the answer?

156 Binary Search Inputs: (1) An array A[1…n] of n elements sorted in nondecreasing order; (2) x; Output: j if x=A[j], and 0 otherwise; 1. low1; highn; j0; 2. while (lowhigh) and (j=0) mid(low+high)/2; if x=A[mid] then jmid; else if x<A[mid] then highmid–1; else lowmid+1; 7. end while; 8. return j;

157 Binary Search What’s the performance of the algorithm Binary Search on a sorted array of size n? What’s the minimum number of comparisons we need and in which case it will happen? What’s the maximum number of comparisons we need and in which case it will happen?

158 Binary Search The number of comparisons performed by Algorithm BINARYSEARCH on a sorted array of size n is at most log n+1.

159 Binary Search Can you implement the Binary Search in a certain kind of computer language?

160 Merging Two Sorted Lists
Suppose we have an array A[1…m] and three indices p, q, and r, with 1pq<rm, such that both the subarrays A[p…q] and A[q+1…r] are individually sorted in nondecreasing order. We want to rearrange the elements in A so that the elements in the subarray A[p…r] are sorted in nondecreasing order.

161 Merging Two Sorted Lists
Example: A[1…7]= p=1, q=3, r=7 A[1…3] and A[4…7] are already sorted nondecreasingly, please sort A nondecreasingly? How many comparisons do you need to give the answer?

162 Merging Two Sorted Lists
Inputs: (1) A[1…m]; (2) p, q, and r, and 1pq<rm, such that both A[p…q] and A[q+1…r] are sorted individually in nondecreasing order; Output: A[p…r] contains the result of merging the two subarrays A[p…q] and A[q+1…r];

163 Merging Two Sorted Lists
1. sp; tq+1; kp; 2. while sq and tr if A[s]A[t] then B[k]A[s]; ss+1; else B[k]A[t]; tt+1; end if; 10. kk+1; 11. end while; 12. if s=q+1 then B[k…r]A[t…r] 13. else B[k…r]A[s…q] 14. end if 15. A[p…r]B[p…r]

164 Merging Two Sorted Lists
What’s the performance of the algorithm Merging Two Sorted Lists? What’s the minimum number of comparisons we need and in which case it will happen? What’s the maximum number of comparisons we need and in which case it will happen? What’s the minimum number of element assignments we need and in which case it will happen? What’s the maximum number of element assignments we need and in which case it will happen?

165 Merging Two Sorted Lists
The number of element comparisons performed by Algorithm MERGE to merge two nonempty arrays of sizes n1 and n2, respectively, where n1n2, into one sorted array of size n=n1+n2 is between n1 and n-1.

166 Merging Two Sorted Lists
The number of element assignments performed by Algorithm MERGE to merge two arrays into one sorted array of size n is exactly 2n.

167 Problems and Algorithms
Each algorithm is designed to solve one problem, but for one problem, we can design many different algorithms. What’s the difference of these algorithms? Why we design different algorithms for the same problem? Example Problem: Sort Selection Sort Insertion Sort Bottom-Up Merge Sorting

168 Selection Sort Let A[1…n] be an array of n elements.
A simple and straightforward algorithm to sort the entries in A works as follows. First, we find the minimum element and store it in A[1]. Next, we find the minimum of the remaining n-1 elements and store it in A[2]. We continue this way until the second largest element is stored in A[n-1].

169 Selection Sort Input: A[1…n];
Output: A[1…n] sorted in nondecreasing order; 1. for i1 to n-1 ki; for ji+1 to n if A[j]<A[k] then kj; end for; if ki then interchange A[i] and A[k]; 7. end for;

170 Selection Sort What’s the performance of the algorithm Selection Sort?
What’s the minimum number of comparisons we need and in which case it will happen? What’s the maximum number of comparisons we need and in which case it will happen? What’s the minimum number of element assignments? What’s the maximum number of element assignments?

171 Selection Sort The number of element comparisons performed by Algorithm SELECTIONSORT to sort an array with n elements is n(n-1)/2.

172 Selection Sort The number of element assignments performed by Algorithm SELECTIONSORT to sort an array with n elements is between 0 and 3(n-1).

173 Insertion Sort We begin with the subarray of size 1, A[1], which is already sorted. Next, A[2] is inserted before or after A[1] depending on whether it is smaller than A[1] or not. Continuing this way, in the i th iteration, A[i] is inserted in its proper position in the sorted subarray A[1 … i-1]. This is done by scanning the elements from index i-1 down to 1, each time comparing A[i] with the element at the current position.

174 Insertion Sort In each iteration of the scan, an element is shifted one position up to a higher index. This process of scanning, performing the comparison and shifting continues until an element less than or equal to A[i] is found, or when all the sorted sequence so far is exhausted. At this point, A[i] is inserted in its proper position, and the process of inserting element A[i] in its proper place is complete.

175 Insertion Sort Example: A[1 … 4]=

176 Insertion Sort Input: An array A[1…n] of n elements;
Output: A[1…n] sorted in nondecreasing order; 1. for i2 to n xA[i]; ji-1; while (j>0) and (A[j]>x) A[j+1]A[j]; jj-1; end while; A[j+1]x; 9. end for;

177 Insertion Sort What’s the performance of the algorithm Insertion Sort?
What’s the minimum number of comparisons we need and in which case it will happen? What’s the maximum number of comparisons we need and in which case it will happen? What’s the minimum number of element assignments? What’s the maximum number of element assignments?

178 Insertion Sort The number of element comparisons performed by Algorithm INSERTIONSORT is between n-1 and n(n-1)/2.

179 Insertion Sort The number of element assignments performed by Algorithm INSERTIONSORT is equal to the number of element comparisons plus n-1.

180 Bottom-Up Merge Sorting
Example: A[1 … 8]=

181 Bottom-Up Merge Sorting
Example: A[1 … 11]=

182 Bottom-Up Merge Sorting
Let A be an array of n elements that is to be sorted. We first merge n/2 consecutive pairs of elements to yield n/2 sorted sequences of size 2. If there is one remaining element, then it is passed to the next iteration. Next, we merge n/4 pairs of consecutive 2-element sequences to yield n/4 sorted sequences of size 4. If there are one or two remaining elements, then they are passed to the next iteration. If there are three elements left, then two (sorted) elements are merged with one element to form a 3-element sorted sequence.

183 Bottom-Up Merge Sorting
Continuing this way, in the jth iteration, we merge n/2j pairs of sorted sequences of size 2j-1 to yield n/2j sorted sequences of size 2j. If there are k remaining elements, where 1k 2j-1, then they are passed to the next iteration. If there are k remaining elements, where 2j-1<k<2j, then these are merged to form a sorted sequence of size k.

184 Bottom-Up Merge Sorting
Input: An array A[1…n] of n elements; Output: A[1…n] sorted in nondecreasing order; 1. t1; 2. while t<n st; t2s; i0; while i+tn MERGE(A, i+1, i+s, i+t); ii+t; end while; if i+s<n then MERGE(A, i+1, i+s, n); 9. end while;

185 Bottom-Up Merge Sorting
What’s the performance of the algorithm Bottom-Up Merge Sorting? What’s the minimum number of comparisons we need and in which case it will happen? What’s the maximum number of comparisons we need and in which case it will happen? What’s the minimum number of element assignments? What’s the maximum number of element assignments?

186 Bottom-Up Merge Sorting
The total number of element comparisons performed by Algorithm BOTTOMUPSORT to sort an array of n element, where n is a power of 2, is between (nlog n)/2 and nlog n-n+1.

187 Bottom-Up Merge Sorting
The total number of element assignments performed by Algorithm BOTTOMUPSORT to sort an array of n element, where n is a power of 2, is exactly 2nlog n.

188 Time Complexity Selection Sort: n(n-1)/2 Merge Sort: nlogn-n+1
If each comparison needs 10-6 second, n=128: Merge= seconds Selection=0.008 seconds n=220: Merge=20 seconds Selection=6.4 days

189 Time Complexity When analyzing the running time,
(1) We usually compare its behavior with another algorithm that solves the same problem. (2) It is desirable for an algorithm to be not only machine independent, but also capable of being expressed in any language. (3) It should be technology independent, that is, we want our measure of the running time of an algorithm to survive technological advances. (4) Our main concern is not in small input sizes; we are mostly concerned with the behavior of the algorithm on large input instances.

190 Time Complexity Therefore, usually only Elementary Operations are used to evaluate the time complexity: Elementary Operation: Any computational step whose cost is always upperbounded by a constant amount of time regardless of the input data or the algorithm used.

191 Time Complexity Examples of elementary operations:
(1) Arithmetic operations: addition, subtraction, multiplication and division (2) Comparisons and logical operations (3) Assignments, including assignments of pointers

192 Time Complexity Usually, we care about how the elementary operations increase with the size of input, namely the rate of growth or the order of growth of the running time. This can be expressed by a function, for example: f(n)=n2logn+10n2+n Once we dispose of lower order terms and leading constants from a function that expresses the running time of an algorithm, we say that we are measuring the asymptotic running time of the algorithm, namely time complexity.

193 Time Complexity Functions that are widely used to represent the running times of algorithms: (1) Sublinear: nc, nclogkn, 0<c<1 (2) Linear: cn (3) Logarithmic: logkn (4) Subquadratic: nlogn, n1.5 (5) Quadratic: cn2 (6) Cubic: cn3

194 Time Complexity In order to formalize the notions of time complexity, special mathematical notations have been widely used. (1) O-notation: An upper bound on the running time. The running time of an algorithm is O(g(n)), if whenever the input size is equal to or exceeds some threshold n0, its running time can be bounded above by some positive constant c times g(n). (2) -notation: A lower bound on the running time. The running time of an algorithm is (g(n)), if whenever the input size is equal to or exceeds some threshold n0, its running time can be bounded below by some positive constant c times g(n).

195 Time Complexity (3) -notation: An exact bound. The running time of an algorithm is of order (g(n)) if whenever the input size is equal to or exceeds some threshold n0, its running time can be bounded below by c1g(n) and above by c2g(n), where 0<c1c2.

196 Time Complexity (1) f(n) is (g(n)) if and only if g(n) is O(f(n))
(2) f(n)=(g(n)) if and only if f(n)=O(g(n)) and f(n)= (g(n))

197 Time Complexity It may be helpful to think of O as similar to ,  as similar to , and  as similar to =. Don’t confuse the exact relations with the asymptotic notations. 100nn, but c, s.t. when n>n0, 100ncn  100n=O(n) n100n, but c, s.t. when n>n0, nc100n  n=(100n), such as c0.01 n100n, but c1, c2, s.t. when n>n0, c1100nnc2100n => n=(100n). Such as c10.01, c20.01

198 Time Complexity How to use the notations O, ,  to represent the time complexity of the three previous sorting algorithms?

199 Time Complexity How to use the notations O, ,  to represent the following functions? Any constant functions logn2 log nk log n!

200 Space Complexity We define the space used by an algorithm to be the number of memory cells (or words) needed to carry out the computational steps required to solve an instance of the problem excluding the space allocated to hold the input. That is, it is only the work space required by the algorithm.

201 Space Complexity The work space cannot exceed the running time of an algorithm, as writing into each memory cell requires at least a constant amount of time. Let T(n) and S(n) denote, respectively, the time and space complexities of an algorithm, then S(n)=O(T(n))

202 Space Complexity Selection Sort and Insertion Sort: (1) Merge: (n)
Merge Sort: (n)

203 Radix Sort Let L={a1, a2, …, an} be a list of n numbers each consisting of exactly k digits. That is, each number is of the form dkdk-1…d1, where each di is a digit between 0 and 9. If the numbers are first distributed into the lists by their least significant digit, then a very efficient algorithm results. Suppose that the numbers are sorted lexicographically according to their least k-1 digits, i.e., digits dk-1, dk-2, …, d1. After sorting them on their kth digits, they will eventually be sorted.

204 Radix Sort First, distribute the numbers into 10 lists L0, L1, …, L9 according to digit d1 so that those numbers with d1=0 constitute list L0, those with d1=1 constitute list L1 and so on. Next, the lists are coalesced in the order L0, L1, …, L9. Then, they are distributed into 10 lists according to digit d2, coalesced in order, and so on. After distributing them according to dk and collecting them in order, all numbers will be sorted.

205 Radix Sort Example: Sort A nondecreasingly.

206 Radix Sort Input: A linked list of numbers L={a1, a2, …, an} and k, the number of digits. Output: L sorted in nondecreasing order. 1. for j1 to k Prepare 10 empty lists L0, L1, …, L9; while L is not empty anext element in L; Delete a from L; ijth digit in a; Append a to list Li; end while; L L0; 10. for i 1 to 9 LL, Li //Append list Li to L 12. end for; 13. end for; 14. return L;

207 Radix Sort What’s the performance of the algorithm Radix Sort?
Time Complexity? Space Complexity?

208 Radix Sort Time Complexity: (n) Space Complexity: (n)

209 Radix Sort Write codes to implement the Radix Sort.

210 Quicksort Let A[low…high] be an array of n numbers, and x=A[low].
We consider the problem of rearranging the elements in A so that all elements less than or equal to x precede x which in turn precedes all elements greater than x. After permuting the element in the array, x will be A[w] for some w, lowwhigh. The action of rearrangement is also called splitting or partitioning around x, which is called the pivot or splitting element.

211 Quicksort We say that an element A[j] is in its proper position or correct position if it is neither smaller than the elements in A[low…j-1] nor larger than the elements in A[j+1…high]. After partitioning an array A using xA as a pivot, x will be in its correct position.

212 Quicksort Input: An array of elements A[low…high];
Output: (1) A with its elements rearranged, if necessary; (2) w, the new position of the splitting element A[low]; 1. ilow; 2. xA[low]; 3. for jlow+1 to high if A[j]x then ii+1; if ij then interchange A[i] and A[j]; end if; 8. end for; 9. interchange A[low] and A[i]; 10.wi; 11.return A and w;

213 Quicksort Example: Adjust 5 to e the correct position.

214 Quicksort The number of element comparisons performed by Algorithm SPLIT is exactly n-1. Thus, its time complexity is (n). The only extra space used is that needed to hold its local variables. Therefore, the space complexity is (1).

215 Quicksort Input: An array A[1…n] of n elements;
Output: The elements in A sorted in nondecreasing order; 1. quicksort(A, 1, n); quicksort(A, low, high) 1. if low<high then SPLIT(A[low…high], w) \\w is the new position of A[low]; quicksort(A, low, w-1); quicksort(A, w+1, high); 5. end if;

216 Quicksort The average number of comparisons performed by Algorithm QUICKSORT to sort an array of n elements is (nlogn).

217 Quicksort Write codes to implement the Quicksort.

218 Data Structures and Algorithm Analysis Graph Algorithms
Lecturer: Jing Liu Homepage:

219 Definitions A graph G=(V, E) consists of a set of vertices, V, and a set of edges, E. Each edge is a pair (v, w), where v, wV. Edges are sometimes referred to as arcs. If the pair is ordered, then the graph is directed. Vertex w is adjacent to v if and only if (v, w)E. In an undirected graph with edge {v, w}, w is adjacent to v and v is adjacent to w. Some times an edge has a third component, known as either a weight or a cost.

220 Definitions A path in a graph is a sequence of vertices w1, w2, w3, …, wN such that (wi, wi+1)E for 1i<N. The length of such a path is the number of edges on the path, which is equal to N-1. We allow a path from a vertex to itself; if this path contains no edges, then the path length is 0. If the graph contains an edge (v, v) from a vertex to itself, then the path v, v is sometime referred to as a loop. The graphs we will consider will generally be loopless. A simple path is a path such that all vertices are distinct, except that the first and the last could be the same.

221 Definitions A cycle in a directed graph is a path of length at least 1 such that w1=wn; this cycle is simple if the path is simple. For undirected graphs, we require that the edges be distinct; that is, the path u, v, u in an undirected graph should not be considered a cycle, because (u, v) and (v, u) are the same edge. In a directed graph, these are different edges, so it makes sense to call this a cycle. A directed graph is acyclic if it has no cycles.

222 Definitions An undirected graph is connected if there is a path from every vertex to every other vertex. A directed graph with this property is called strongly connected. If a directed graph is not strongly connected, but the underlying graph (without direction to the arcs) is connected, then the graph is said to be weakly connected. A complete graph is a graph in which there is an edge between every pair of vertices.

223 Definitions In an undirected graph, the degree of a vertex is the number of edges connected to this vertex. In an directed graph, the outdegree of a vertex is the number of edges that start from this vertex, and the indegree is the number of edges that end at this vertex. Examples of graphs: airport system, traffic flow, friendship.

224 Representation of Graphs
Suppose, for now, that we can number the vertices, starting at 1; that is, V={1, 2, …, n} One simple way to represent a graph is to use a two-dimensional array this is known as a adjacency matrix representation. For each edge (u, v), we set A[u][v]=1; otherwise the entry in the array is 0. If the edge has a weight associated with it, then we can set A[u][v] equal to the weight and use either a very large or a very small weight as a sentinel to indicate nonexistent edges.

225 Representation of Graphs
If the number of edges in a graph is very small, a better solution is an adjacency list representation. For each vertex, we keep a list of all adjacent vertices: The leftmost structure is merely an array of header cells. If the edges have weights, then this additional information is also stored in the cells.

226 Topological Sort A topological sort is an ordering of vertices in a directed acyclic graph, such that if there is a path from vi to vj, then vj appears after vi in the ordering. See Figure 9.3: A directed edge (v, w) indicates that course v must be completed before course w may be attempted. A topological ordering of these courses is any course sequence that does not violate the prerequisite requirement.

227 Topological Sort It is clear that a topological ordering is not possible if the graph has a cycle, since for two vertices v and w on the cycle, v precedes w and w precedes v. The ordering is not necessarily unique; any legal ordering will do. A simple algorithm to find a topological ordering is first to find any vertex with no incoming edges. We can then print this vertex, and remove it, along with this edge, from the graph. Then we apply this same strategy to the rest of the graph. Thus, we compute the indegrees of all vertices in the graph, and look for a vertex with indegree 0 that has not already been assigned a topological number.

228 Topological Sort Example: 1 2 3 4 5 6 7

229 The All-Pairs Shortest Path Problem
Let G=(V, E) be a directed graph in which each edge (i, j) has a non-negative length l[i, j]. If there is no edge from vertex i to vertex j, then l[i, j]=. The problem is to find the distance from each vertex to all other vertices, where the distance from vertex x to vertex y is the length of a shortest path from x to y. For simplicity, we will assume that V={1, 2, …, n}. Let i and j be two different vertices in V. Define to be the length of a shortest path from i to j that does not pass through any vertex in {k+1, k+2, …, n}.

230 The All-Pairs Shortest Path Problem
is the length of a shortest path from i to j that does not pass through any vertex except possibly vertex 1 is the length of a shortest path from i to j that does not pass through any vertex except possibly vertex 1 or vertex 2 or both is the length of a shortest path from i to j, i.e. the distance from i to j

231 The All-Pairs Shortest Path Problem
We can compute recursively as follows:

232 The All-Pairs Shortest Path Problem
Floyd Algorithm: use n+1 matrices D0, D1, D2, …, Dn of dimension nn to compute the lengths of the shortest constrained paths. Initially, we set D0[i, i]=0, D0[i, j]=l[i, j] if ij and (i, j) is an edge in G; otherwise D0[i, j]=. We then make n iterations such that after the kth iteration, Dk[i, j] contains the value of a shortest length path from vertex i to vertex j that does not pass through any vertex numbered higher than k.

233 The All-Pairs Shortest Path Problem
Thus, in the kth iteration, we compute Dk[i, j] using the formula Dk[i, j]=min{Dk-1[i, j], Dk-1[i, k]+Dk-1[k, j]}

234 The All-Pairs Shortest Path Problem
Example: 1 1 2 8 9 3 2 6

235 The All-Pairs Shortest Path Problem
Input: An nn matrix l[1…n, 1…n] such that l[i, j] is the length of the edge (i, j) in a directed graph G=({1, 2, …, n}, E); Output: A matrix D with D[i, j]=the distance from i to j; 1. Dl; 2. for k1 to n for i1 to n for j1 to n D[i, j]=min{D[i, j], D[i, k]+D[k, j]); end for; end for; 8. end for;

236 The All-Pairs Shortest Path Problem
What is the time and space complexity of the FLOYD algorithm?

237 The All-Pairs Shortest Path Problem
The running time of FLOYD algorithm is (n3) and its space complexity is (n2).

238 The Shortest Path Problem
Let G=(V, E) be a directed graph in which each edge has a nonnegative length, and a distinguished vertex s called the source. The single-source shortest path problem, or simply the shortest path problem, is to determine the distance from s to every other vertex in V, where the distance from vertex s to vertex x is defined as the length of a shortest path from s to x. For simplicity, we will assume that V={1, 2, …, n} and s=1. This problem can be solved using a greedy technique known as Dijkstra’s algorithm.

239 The Shortest Path Problem
The set of vertices is partitioned into two sets X and Y so that X is the set of vertices whose distance from the source has already been determined, while Y contains the rest vertices. Thus, initially X={1} and Y={2, 3, …, n}. Associated with each vertex y in Y is a label [y], which is the length of a shortest path that passes only through vertices in X. Thus, initially

240 The Shortest Path Problem
At each step, we select a vertex yY with minimum  and move it to X, and  of each vertex wY that is adjacent to y is updated indicating that a shorter path to w via y has been discovered. The above process is repeated until Y is empty. Finally,  of each vertex in X is the distance from the source vertex to this one.

241 The Shortest Path Problem
Example: 3 2 4 1 15 9 4 1 6 13 4 12 5 3 5

242 The Shortest Path Problem
Input: A weighted directed graph G=(V, E), where V={1, 2, …, n}; Output: The distance from vertex 1 to every other vertex in G; 1. X={1}; YV-{1}; [1]0; 2. for y2 to n if y is adjacent to 1 then [y]length[1, y]; else [y]; end if; 6. end for; 7. for j1 to n-1 Let yY be such that [y] is minimum; XX{y}; //add vertex y to X YY-{y}; //delete vertex y from Y for each edge (y, w) if wY and [y]+length[y, w]<[w] then [w][y]+length[y, w]; end for; 15. end for;

243 The Shortest Path Problem
What’s the performance of the DIJKSTRA algorithm? Time Complexity?

244 The Shortest Path Problem
Given a directed graph G with nonnegative weights on its edges and a source vertex s, Algorithm DIJKSTRA finds the length of the distance from s to every other vertex in (n2) time.

245 The Shortest Path Problem
Write codes to implement the Dijkstra algorithm.

246 Minimum Cost Spanning Trees (Kruskal’s Algorithm)
Let G=(V, E) be a connected undirected graph with weights on its edges. A spanning tree (V, T) of G is a subgraph of G that is a tree. If G is weighted and the sum of the weights of the edges in T is minimum, then (V, T) is called a minimum cost spanning tree or simply a minimum spanning tree.

247 Minimum Cost Spanning Trees (Kruskal’s Algorithm)
Kruskal’s algorithm works by maintaining a forest consisting of several spanning trees that are gradually merged until finally the forest consists of exactly one tree. The algorithm starts by sorting the edges in nondecreasing order by weight.

248 Minimum Cost Spanning Trees (Kruskal’s Algorithm)
Next, starting from the forest (V, T) consisting of the vertices of the graph and none of its edges, the following step is repeated until (V, T) is transformed into a tree: Let (V, T) be the forest constructed so far, and let eE-T be the current edge being considered. If adding e to T does not create a cycle, then include e in T; otherwise discard e. This process will terminate after adding exactly n-1 edges.

249 Minimum Cost Spanning Trees (Kruskal’s Algorithm)
Example: 11 2 4 1 3 6 9 1 6 7 4 2 13 3 5

250 Minimum Cost Spanning Trees (Kruskal’s Algorithm)
Write codes to implement the Kruskal algorithm.

251 Graph Traversal In some cases, what is important is that the vertices are visited in a systematic order, regardless of the input graph. Usually, there are two methods of graph traversal: Depth-first search Breadth-first search

252 Depth-First Search Let G=(V, E) be a directed or undirected graph.
First, all vertices are marked unvisited. Next, a starting vertex is selected, say vV, and marked visited. Let w be any vertex that is adjacent to v. We mark w as visited and advance to another vertex, say x, that is adjacent to w and is marked unvisited. Again, we mark x as visited and advance to another vertex that is adjacent to x and is marked unvisited.

253 Depth-First Search This process of selecting an unvisited vertex adjacent to the current vertex continues as deep as possible until we find a vertex y whose adjacent vertices have all been marked visited. At this point, we back up to the most recently visited vertex, say z, and visit an unvisited vertex that is adjacent to z, if any. Continuing this way, we finally return back to the starting vertex v. The algorithm for such a traversal can be written using recursion.

254 Depth-First Search Example: d a i c b g h e f j

255 Depth-First Search When the search is complete, if all vertices are reachable from the start vertex, a spanning tree called the depth-first search spanning tree is constructed whose edges are those inspected in the forward direction, i.e., when exploring unvisited vertices. As a result of the traversal, the edges of an undirected graph G are classified into the following two types: Tree edges: edges in the depth-first search tree. Back edges: all other edges.

256 Depth-First Search Input: An undirected graph G=(V, E);
Output: Preordering of the vertices in the corresponding depth-first search tree. 1. predfn0; 2. for each vertex vV Mark v unvisited; 4. end for; 5. for each vertex vV if v is marked unvisited then dfs(v); 7. end for; dfs(v) 1. Mark v visited; 2. predfnpredfn+1; 3. for each edge (v, w)E if w is marked unvisited then dfs(w); 5. end for;

257 Depth-First Search Write codes to implement the Depth-First Search on graph.

258 Breadth-First Search When we visit a vertex v, we next visit all vertices adjacent to v. This method of traversal can be implemented by a queue to store unexamined vertices.

259 Breadth-First Search Example: d a i c b g h e f j

260 Finding Articulation Points in a Graph
A vertex v in an undirected graph G with more than two vertices is called an articulation point if there exist two vertices u and w different from v such that any path between u and w must pass through v. If G is connected, the removal of v and its incident edges will result in a disconnected subgraph of G. A graph is called biconnected if it is connected and has no articulation points.

261 Finding Articulation Points in a Graph
To find the set of articulation points, we perform a depth-first search traversal on G. During the traversal, we maintain two labels with each vertex vV: [v] and [v]. [v] is simply predfn in the depth-first search algorithm. [v] is initialized to [v], but may change later on during the traversal.

262 Finding Articulation Points in a Graph
For each vertex v visited, we let [v] be the minimum of the following: [v] [u] for each vertex u such that (v, u) is a back edge [w] for each vertex w such that (v, w) is a tree edge Thus, [v] is the smallest  that v can reach through back edges or tree edges.

263 Finding Articulation Points in a Graph
The articulation points are determined as follows: The root is an articulation point if and only if it has two or more children in the depth-first search tree. A vertex v other than the root is an articulation point if and only if v has a child w with [w][v].

264 Finding Articulation Points in a Graph
Input: A connected undirected graph G=(V, E); Output: Array A[1…count] containing the articulation points of G, if any. 1. Let s be the start vertex; 2. for each vertex vV Mark v unvisited; 4. end for; 5. predfn0; count0; rootdegree0; 6. dfs(s);

265 dfs(v) 1. Mark v visited; artpointfalse; predfnpredfn+1; 2. [v]predfn; [v]predfn; 3. for each edge (v, w)E if (v, w) is a tree edge then dfs(w); if v=s then rootdegreerootdegree+1; if rootdegree=2 then artpointtrue; else [v]min{[v], [w]}; if [w][v] then artpointtrue; end if; else if (v, w) is a back edge then [v]min{[v], [w]}; else do nothing; //w is the parent of v end if; 16. end for; 17. if artpoint then countcount +1; A[count]v; 20. end if;

266 Finding Articulation Points in a Graph
Example: d a i c b g h e f j

267 Finding Articulation Points in a Graph
Write codes to implement the above algorithm.

268 Data Structures and Algorithm Analysis Algorithm Design Techniques
Lecturer: Jing Liu Homepage:

269 The Greedy Approach Greedy algorithms are usually designed to solve optimization problems in which a quantity is to be minimized or maximized. Greedy algorithms typically consist of a n iterative procedure that tries to find a local optimal solution. In some instances, these local optimal solutions translate to global optimal solutions. In others, they fail to give optimal solutions.

270 The Greedy Approach A greedy algorithm makes a correct guess on the basis of little calculation without worrying about the future. Thus, it builds a solution step by step. Each step increases the size of the partial solution and is based on local optimization. The choice make is that which produces the largest immediate gain while maintaining feasibility. Since each step consists of little work based on a small amount of information, the resulting algorithms are typically efficient.

271 The Fractional Knapsack Problem
Given n items of sizes s1, s2, …, sn, and values v1, v2, …, vn and size C, the knapsack capacity, the objective is to find nonnegative real numbers x1, x2, …, xn that maximize the sum subject to the constraint

272 The Fractional Knapsack Problem
This problem can easily be solved using the following greedy strategy: For each item compute yi=vi/si, the ratio of its value to its size. Sort the items by decreasing ratio, and fill the knapsack with as much as possible from the first item, then the second, and so forth. This problem reveals many of the characteristics of a greedy algorithm discussed above: The algorithm consists of a simple iterative procedure that selects that item which produces that largest immediate gain while maintaining feasibility.

273 File Compression Suppose we are given a file, which is a string of characters. We wish to compress the file as much as possible in such a way that the original file can easily be reconstructed. Let the set of characters in the file be C={c1, c2, …, cn}. Let also f(ci), 1in, be the frequency of character ci in the file, i.e., the number of times ci appears in the file.

274 File Compression Using a fixed number of bits to represent each character, called the encoding of the character, the size of the file depends only on the number of characters in the file. Since the frequency of some characters may be much larger than others, it is reasonable to use variable length encodings.

275 File Compression Intuitively, those characters with large frequencies should be assigned short encodings, whereas long encodings may be assigned to those characters with small frequencies. When the encodings vary in length, we stipulate that the encoding of one character must not be the prefix of the encoding of another character; such codes are called prefix codes. For instance, if we assign the encodings 10 and 101 to the letters “a” and “b”, there will be an ambiguity as to whether 10 is the encoding of “a” or is the prefix of the encoding of the letter “b”.

276 File Compression Once the prefix constraint is satisfied, the decoding becomes unambiguous; the sequence of bits is scanned until an encoding of some character is found. One way to “parse” a given sequence of bits is to use a full binary tree, in which each internal node has exactly two branches labeled by 0 an 1. The leaves in this tree corresponding to the characters. Each sequence of 0’s and 1’s on a path from the root to a leaf corresponds to a character encoding.

277 File Compression The algorithm presented is due to Huffman.
The algorithm consists of repeating the following procedure until C consists of only one character. Let ci and cj be two characters with minimum frequencies. Create a new node c whose frequency is the sum of the frequencies of ci and cj, and make ci and cj the children of c. Let C=C-{ci, cj}{c}.

278 File Compression Example: C={a, b, c, d, e} f (a)=20 f (b)=7 f (c)=10
f (d)=4 f (e)=18

279 The Greedy Approach Other examples:
Dijkstra’s algorithm for the shortest path problem Kruskal’s algorithm for the minimum cost spanning tree problem

280 Divide and Conquer A divide-and-conquer algorithm divides the problem instance into a number of subinstances (in most cases 2), recursively solves each subinsance separately, and then combines the solutions to the subinstances to obtain the solution to the original problem instance.

281 Divide and Conquer Consider the problem of finding both the minimum and maximum in an array of integers A[1…n] and assume for simplicity that n is a power of 2. Divide the input array into two halves A[1…n/2] and A[(n/2)+1…n], find the minimum and maximum in each half and return the minimum of the two minima and the maximum of the two maxima.

282 Divide and Conquer Input: An array A[1…n] of n integers, where n is a power of 2; Output: (x, y): the minimum and maximum integers in A; 1. minmax(1, n); minmax(low, high) 1. if high-low=1 then if A[low]<A[high] then return (A[low], A[high]); else return(A[high], A[low]); end if; 5. else mid(low+high)/2; (x1, y1)minmax(low, mid); (x2, y2)minmax(mid+1, high); xmin{x1, x2}; 10. y max{y1, y2}; 11. return(x, y); 12. end if;

283 Divide and Conquer How many comparisons does this algorithm need?

284 Divide and Conquer Given an array A[1…n] of n elements, where n is a power of 2, it is possible to find both the minimum and maximum of the elements in A using only (3n/2)-2 element comparisons.

285 The Divide and Conquer Paradigm
The divide step: the input is partitioned into p1 parts, each of size strictly less than n. The conquer step: performing p recursive call(s) if the problem size is greater than some predefined threshold n0. The combine step: the solutions to the p recursive call(s) are combined to obtain the desired output.

286 The Divide and Conquer Paradigm
(1) If the size of the instance I is “small”, then solve the problem using a straightforward method and return the answer. Otherwise, continue to the next step; (2) Divide the instance I into p subinstances I1, I2, …, Ip of approximately the same size; (3) Recursively call the algorithm on each subinstance Ij, 1jp, to obtain p partial solutions; (4) Combine the results of the p partial solutions to obtain the solution to the original instance I. Return the solution of instance I.

287 Selection: Finding the Median and the kth Smallest Element
The media of a sequence of n sorted numbers A[1…n] is the “middle” element. If n is odd, then the middle element is the (n+1)/2th element in the sequence. If n is even, then there are two middle elements occurring at positions n/2 and n/2+1. In this case, we will choose the n/2th smallest element. Thus, in both cases, the median is the n/2th smallest element. The kth smallest element is a general case.

288 Selection: Finding the Median and the kth Smallest Element
If we select an element mm among A, then A can be divided in to 3 parts:

289 Selection: Finding the Median and the kth Smallest Element
According to the number elements in A1, A2, A3, there are following three cases. In each case, where is the kth smallest element?

290 Selection: Finding the Median and the kth Smallest Element
Input: An array A[1…n] of n elements and an integer k, 1kn; Output: The kth smallest element in A; 1. select(A, 1, n, k);

291 Selection: Finding the Median and the kth Smallest Element
select(A, low, high, k) 1. phigh-low+1; 2. if p<44 then sort A and return (A[k]); 3. Let q=p/5. Divide A into q groups of 5 elements each. If 5 does not divide p, then discard the remaining elements; 4. Sort each of the q groups individually and extract its media. Let the set of medians be M. 5. mmselect(M, 1, q, q/2); 6. Partition A[low…high] into three arrays: A1={a|a<mm}, A2={a|a=mm}, A3={a|a>mm}; 7. case |A1|k: return select (A1, 1, |A1|, k); |A1|+|A2|k: return mm; |A1|+|A2|<k: return select(A3, 1, |A3|, k-|A1|-|A2|); 8. end case;

292 Selection: Finding the Median and the kth Smallest Element
What is the time complexity of this algorithm?

293 Selection: Finding the Median and the kth Smallest Element
The kth smallest element in a set of n elements drawn from a linearly ordered set can be found in (n) time.

294 Selection: Finding the Median and the kth Smallest Element
Write the codes for the above algorithm.

295 Divide and Conquer Other example: Quicksort

296 Dynamic Programming Dynamic programming resorts to evaluating the recurrence in a bottom-up manner, saving intermediate results that are used later on to compute the desired solution. This technique applies to many combinatorial optimization problems to derive efficient algorithms.

297 Dynamic Programming Fibonacci sequence: f1=1 f2=1 f3=2 f(n) f4=3
…… f(n) 1. if (n=1) or (n=2) then return 1; 2. else return f(n-1)+f(n-2); This algorithm is far from being efficient, as there are many duplicate recursive calls to the procedure.

298 Dynamic Programming Time: n-2 additions  (n) Space: (1) f(n)
1. if (n=1) or (n=2) then return 1; 2. else 3. { fn_1=1; fn_2=1; for i3 to n fn=fn_1+fn_2; fn_2=fn_1; fn_1=fn; end for; 11. } 12. Return fn; Time: n-2 additions  (n) Space: (1)

299 The Longest Common Subsequence Problem
Given two strings A and B of lengths n and m, respectively, over an alphabet , determine the length of the longest subsequence that is common to both A and B. A subsequence of A=a1a2…an is a string of the form ai1ai2…aik, where each ij is between 1 and n and 1i1<i2<…<ikn.

300 The Longest Common Subsequence Problem
Let A=a1a2…an and B=b1b2…bm. Let L[i, j] denote the length of a longest common subsequence of a1a2…ai and b1b2…bj. 0in, 0jm. When i or j be 0, it means the corresponding string is empty. Naturally, if i=0 or j=0; the L[i, j]=0

301 The Longest Common Subsequence Problem
Suppose that both i and j are greater than 0. Then If ai=bj, L[i, j]=L[i-1, j-1]+1 If aibj, L[i, j]=max{L[i, j-1], L[i-1, j]} We get the following recurrence for computing the length of the longest common subsequence of A and B:

302 The Longest Common Subsequence Problem
We use an (n+1)(m+1) table to compute the values of L[i, j] for each pair of values of i and j, 0in, 0jm. We only need to fill the table L[0…n, 0…m] row by row using the previous formula.

303 The Longest Common Subsequence Problem
Example: A=zxyxyz, B=xyyzx What is the longest common subsequence of A and B?

304 The Longest Common Subsequence Problem
Input: Two strings A and B of lengths n and m, respectively, over an alphabet ; Output: The length of the longest common subsequence of A and B. 1. for i0 to n L[i, 0]0; 3. end for; 4. for j0 to m L[0, j]0; 6. end for; 7. for i1 to n for j1 to m if ai=bj then L[i, j]L[i-1, j-1]+1; else L[i, j]max{L[i, j-1], L[i-1, j]}; end if; end for; 13. end for; 14. return L[n, m];

305 The Longest Common Subsequence Problem
What’s the performance of this algorithm? Time Complexity? Space Complexity?

306 The Longest Common Subsequence Problem
An optimal solution to the longest common subsequence problem can be found in (nm) time and (min{m, n}) space.

307 The Dynamic Programming Paradigm
The idea of saving solutions to subproblems in order to avoid their recomputation is the basis of this powerful method. This is usually the case in many combinatorial optimization problems in which the solution can be expressed in the form of a recurrence whose direct solution causes subinstances to be computed more than once.

308 The Dynamic Programming Paradigm
An important observation about the working of dynamic programming is that the algorithm computes an optimal solution to every subinstance of the original instance considered by the algorithm. This argument illustrates an important principle in algorithm design called the principle of optimality: Given an optimal sequence of decisions, each subsequence must be an optimal sequence of decisions by itself.

309 The Knapsack Problem Let U={u1, u2, …, un} be a set of n items to be packed in a knapsack of size C. for 1jn, let sj and vj be the size and value of the jth item, respectively, where C and sj, vj, 1jn, are all positive integers. The objective is to fill the knapsack with some items for U whose total size is at most C and such that their total value is maximum. Assume without loss of generality that the size of each item does not exceed C.

310 The Knapsack Problem More formally, given U of n items, we want to find a subset SU such that is maximized subject to the constraint This version of the knapsack problem is sometimes referred to in the literature as the 0/1 knapsack problem. This is because the knapsack cannot contain more than one item of the same type.

311 The Knapsack Problem Let V[i, j] denote the value obtained by filling a knapsack of size j with items taken from the first i items {u1, u2, …, ui} in an optimal way. Here the range of i is from 0 to n and the range of j is from 0 to C. Thus, what we seek is the value V[n, C]. Obviously, V[0, j] is 0 for all values of j, as there is nothing in the knapsack. On the other hand, V[i, 0] is 0 for all values of i since nothing can be put in a knapsack of size 0.

312 The Knapsack Problem V[i, j], where i>0 and j>0, is the maximum of the following two quantities: V[i-1, j]: The maximum value obtained by filling a knapsack of size j with items taken from {u1, u2, …, ui-1} only in an optimal way. V[i-1, j-si]+vi: The maximum value obtained by filling a knapsack of size j-si with items taken from {u1, u2, …, ui-1} in an optimal way plus the value of item ui. This case applies only if jsi and it amounts to adding item ui to the knapsack.

313 The Knapsack Problem Then, we got the following recurrence for finding the value in an optimal packing: Using dynamic programming to solve this integer programming problem is now straightforward. We use an (n+1)(C+1) table to evaluate the values of V[i, j]. We only need to fill the table V[0…n, 0…C] row by row using the above formula.

314 The Knapsack Problem Example: C=9 U={u1, u2, u3, u4} Si=2, 3, 4, 5
Vi=3, 4, 5, 7

315 The Knapsack Problem Input: A set of items U={u1, u2, …, un} with sizes s1, s2, …, sn and values v1, v2, …, vn and a knapsack capacity C. Output: The maximum value of the function subject to for some subset of items SU. 1. for i0 to n V[i, 0]0; 3. end for; 4. for j0 to C V[0, j]0; 6. end for; 7. for i1 to n for j1 to C V[i, j]V[i-1, j]; if sij then V[i, j] max{V[i, j], V[i-1, j-si]+vi}; end for; 12. end for; 13. return V[n, C];

316 The Knapsack Problem What is the time and space complexity of this algorithm?

317 The Knapsack Problem An optimal solution to the Knapsack problem can be found in (nC) time and (C) space.

318 Dynamic Programming Other example:
Floyd Algorithm for the All-Pairs Shortest Path Problem

319 Backtracking In many real world problems, a solution can be obtained by exhaustively searching through a large but finite number of possibilities. Hence, the need arose for developing systematic techniques of searching, with the hope of cutting down the search space to possibly a much smaller space. Here, we present a general technique for organizing the search known as backtracking. This algorithm design technique can be described as an organized exhaustive search which often avoids searching all possibilities.

320 The 3-Coloring Problem Given an undirected graph G=(V, E), it is required to color each vertex in V with one of three colors, say 1, 2, and 3, such that no two adjacent vertices have the same color. We call such a coloring legal; otherwise, if two adjacent vertices have the same color, it is illegal. A coloring can be represented by an n-tuple (c1, c2, …, cn) such that ci{1, 2, 3}, 1in. For example, (1, 2, 2, 3, 1) denotes a coloring of a graph with five vertices.

321 The 3-Coloring Problem There are 3n possible colorings (legal and illegal) to color a graph with n vertices. The set of all possible colorings can be represented by a complete ternary tree called the search tree. In this tree, each path from the root to a leaf node represents one coloring assignment. An incomplete coloring of a graph is partial if no two adjacent colored vertices have the same color. Backtracking works by generating the underlying tree one node at a time. If the path from the root to the current node corresponds to a legal coloring, the process is terminated (unless more than one coloring is desired).

322 The 3-Coloring Problem If the length of this path is less than n and the corresponding coloring is partial, then one child of the current node is generated and is marked as the current node. If, on the other hand, the corresponding path is not partial, then the current node is marked as a dead node and a new node corresponding to another color is generated. If, however, all three colors have been tried with no success, the search backtracks to the parent node whose color is changed, and so on.

323 The 3-Coloring Problem Example: a b c d e

324 The 3-Coloring Problem There are two important observations to be noted, which generalize to all backtracking algorithms: (1) The nodes are generated in a depth-first-search manner. (2) There is no need to store the whole search tree; we only need to store the path from the root to the current active node. In fact, no physical nodes are generated at all; the whole tree is implicit. We only need to keep track of the color assignment.

325 The 3-Coloring Problem Recursive Algorithm
Input: An undirected graph G=(V, E). Output: A 3-coloring c[1…n] of the vertices of G, where each c[j] is 1, 2, or 3. 1. for k1 to n c[k]0; 3. end for; 4. flagfalse; 5. graphcolor(1); 6. if flag then output c; 7. else output “no solution”; graphcolor(k) 1. for color=1 to 3 c[k]color; if c is a legal coloring then set flag true and exit; else if c is partial then graphcolor(k+1); 5. end for;

326 The 3-Coloring Problem Iterative Algorithm
Input: An undirected graph G=(V, E). Output: A 3-coloring c[1…n] of the vertices of G, where each c[j] is 1, 2, or 3. 1. for k1 to n c[k]0; 3. end for; 4. flagfalse; 5. k1; 6. while k1 while c[k]2 c[k]c[k]+1; if c is a legal coloring then set flagtrue and exit from the two while loops; else if c is partial then kk+1; end while; c[k]0; kk-1; 14. end while; 15. if flag then output c; 16. else output “no solution”;

327 The 8-Queens Problem How can we arrange 8 queens on an 88 chessboard so that no two queens can attack each other? Two queens can attack each other if they are in the same row, column or diagonal. The n-queens problem is defined similarly, where in this case we have n queens and an nn chessboard for an arbitrary value of n1.

328 The 8-Queens Problem Consider a chessboard of size 44. Since no two queens can be put in the same row, each queen is in a different row. Since there are four positions in each row, there are 44 possible configurations. Each possible configuration can be described by a vector with four components x=(x1, x2, x3, x4). For example, the vector (2, 3, 4, 1) corresponds to a configuration.

329 The 8-Queens Problem Input: none;
Output: A vector x[1…4] corresponding to the solution of the 4-queens problem. 1. for k1 to 4 x[k]0; 3. end for; 4. flagfalse; 5. k1; 6. while k1 while x[k]3 x[k]x[k]+1; if x is a legal placement then set flagtrue and exit from the two while loops; else if x is partial then kk+1; end while; x[k]0; kk-1; 14. end while; 15. if flag then output x; 16. else output “no solution”;

330 The General Backtracking Method
The general backtracking algorithm can be described as a systematic search method that can be applied to a class of search problems whose solution consists of a vector (x1, x2, … xi) satisfying some predefined constraints. Here, i is dependent on the problem formulation. In 3-Coloring and the 8-queens problems, i was fixed. In some problems, i may vary from one solution to another.

331 The General Backtracking Method
Consider a variant of the PARTITION problem defined as follows. Given a set of n integers X={x1, x2, …, xn} and an integer y, find a subset Y of X whose sum is equal to y. For instance if X={10, 20, 30, 40, 50, 60}, and y=60, then there are three solutions of different lengths: {10, 20, 30}, {20, 40}, and {60}. Actually, this problem can be formulated in another way so that the solution is a boolean vector of length n in the obvious way. The above three solutions may be expressed by the boolean vectors {1, 1, 1, 0, 0, 0}, {0, 1, 0, 1, 0, 0}, and {0, 0, 0, 0, 0, 1}.

332 The General Backtracking Method
In backtracking, each xi in the solution vector belongs to a finite linearly ordered set Xi. Thus, the backtracking algorithm considers the elements of the cartesian product X1X2…Xn in lexicographic order. Initially, the algorithm starts with the empty vector. It then chooses the least element of X1 as x1. If (x1) is a partial solution, then algorithm proceeds by choosing the least element of X2 as x2. If (x1, x2) is a partial solution, then the least element of X3 is included; otherwise x2 is set to the next element in X2. In general, suppose that the algorithm has detected the partial solution (x1, x2, …, xj). It then considers the vector v=(x1, x2, …, xj, xj+1). We have the following cases:

333 The General Backtracking Method
(1) If v represents a final solution to the problem, the algorithm records it as a solution and either terminates in case only one solution is desired or continues to find other solutions. (2) If v represents a partial solution, the algorithm advances by choosing the least element in the set Xj+2. (3) If v is neither a final nor a partial solution, we have two subcases: (a) If there are still more elements to choose from in the set Xj+1, the algorithm sets xj+1 to the next member of Xj+1. (b) If there are no more elements to choose from in the set Xj+1, the algorithm backtracks by setting xj to the next member of Xj. If again there are no more elements to choose form in the set Xj, the algorithm backtracks by setting xj-1 to the next member of Xj-1, and so on.


Download ppt "Data Structures and Algorithm Analysis"

Similar presentations


Ads by Google