Basic Data Structures CS 583 Analysis of Algorithms 7/1/2016 CS583 Fall'06: Basic Data Structures 1 Outline • HW1 analysis • Basic Data Structures – – – – Stacks Queues Linked lists Rooted trees • Binary Search Trees – Tree walk – Querying 7/1/2016 CS583 Fall'06: Basic Data Structures 2 HW1 • HW1 answers are posted on the web site • Check results by e-mail to the TA, or with the instructor in the class – The grade can be changed within 1 week, by October 9. • Submitting HW in the future: – Attach the submission in a separate file, and double-check that it’s not empty. – Prefix the file name with the student’s last name, e.g. JonesHw1.doc. – Indent a pseudocode properly. 7/1/2016 CS583 Fall'06: Basic Data Structures 3 Stacks • Stack is a dynamic data set in which the element deleted from the set is the latest inserted: last-infirst-out, or LIFO policy. – The insert operation on stack is often called PUSH – The delete operation is often called POP. • A stack can be implemented by an array S[1..n] with an attribute top[S] that indexes the most recently inserted element. – When top[S] = 0, the stack is empty. – If an empty stack is popped, we say the stack underflows. – When top[S] > n, the stack overflows. 7/1/2016 CS583 Fall'06: Basic Data Structures 4 Stack Algorithms Stack-Empty(S) 1 if top[S] = 0 2 return TRUE 3 else 4 return FALSE Push(S, x) 1 top[S]++ 2 S[top[S]] = x Pop(S) 1 if Stack-Empty(S) 2 throw "underflow" 3 else 4 top[S]— 5 return S[top[S]+1] Each of above algorithms runs in O(1) time. 7/1/2016 CS583 Fall'06: Basic Data Structures 5 Queues • The queue implements a first-in-first-out, or FIFO policy. – The insert operation on a queue is called ENQUEUE – The delete operation is called DEQUEUE. • A queue of at most (n-1) elements can be implemented using an array Q[1..n]. – The queue has an attribute head[Q] that points to the index of its head. – The attribute tail[Q] indexes the next location at which the new element will be inserted. – The order is circular, i.e. the location 1 immediately follows the location n. 7/1/2016 CS583 Fall'06: Basic Data Structures 6 Queues: Example 1 13 2 3 tail 4 11 head 5 12 4 5 12 head Q[1..5] 5 12 head Q[1..5] Q[1..5] 11 = Dequeue(Q): 1 13 2 3 tail Enqueue(Q,14): 1 13 2 14 3 tail 7/1/2016 4 CS583 Fall'06: Basic Data Structures 7 Queue Algorithms Enqueue(Q,x) 1 /* head and tail cannot point to the same location,hence only (n-1) elements can be stored */ 2 if tail[Q] + 1 = head[Q] 3 throw "overflow" 4 Q[tail[Q]] = x 5 tail[Q] = tail[Q] % n + 1 // wrap around 7/1/2016 CS583 Fall'06: Basic Data Structures 8 Queue Algorithms (cont.) Dequeue(Q) 1 if head[Q]=tail[Q] 2 throw "underflow" 3 x = Q[head[Q]] 4 head[Q] = head[Q] % n + 1 5 return x // wrap around Both operations run in O(1) time. 7/1/2016 CS583 Fall'06: Basic Data Structures 9 Linked Lists • A linked list is a data structure in which objects are arranged in linear order. – Each object points to the location of the next object. – In a doubly linked list each object also points to the location of the previous object. • More formally, for a doubly linked list L: – head[L] points to the first element of the list. – Given an element x from L: • • • • 7/1/2016 prev[x] points to its predecessor. next[x] points to its successor. If next[x] = NIL, x is the last element in L. If prev[x] = NIL, x is the first element in L. CS583 Fall'06: Basic Data Structures 10 Linked Lists Algorithms The following algorithm finds the first element with key k in the list L: List-Search(L,k) 1 x = head[L] 2 while x <> NIL and key[x] <> k 3 x = next[x] 4 return x The above algorithm runs in (n) time in the worst case, since it may have to search the entire list. 7/1/2016 CS583 Fall'06: Basic Data Structures 11 Linked Lists Algorithms (cont.) The following algorithm inserts an element x with a key set to the front of the list. List-Insert(L,x) 1 next[x] = head[L] 2 if head[L] <> NIL 3 prev[head[L]] = x 4 head[L] = x 5 prev[x] = NIL The running time of the insert algorithm is O(1). 7/1/2016 CS583 Fall'06: Basic Data Structures 12 Linked Lists Algorithms (cont.) The following algorithm deletes an element x from the linked list. (It assumes the pointer to x is known, otherwise use List-Search to find x according to some key). List-Delete(L,x) 1 if prev[x] <> NIL 2 next[prev[x]] = next[x] 3 else 4 head[L] = next[x] 5 if next[x] <> NIL 6 prev[next[x]] = prev[x] This algorithm runs in O(1), however it would require (n) time if it needs to search for an element. 7/1/2016 CS583 Fall'06: Basic Data Structures 13 Rooted Trees • Trees can be represented using linked data structures. – For example, in case of binary trees we use fields p, left, and right to store pointers to the parent, left child, and right child of each node of the binary tree T. – The root of the tree is root[T] (x: p[x] = NIL). • To represent trees with unbounded branching, we use binary tree representation. – In this scheme, each node still contains a parent pointer, and root[T] points to the root of T. Instead of having a pointer for each child of a node, each node x has only two pointers: • • • • 7/1/2016 left-child[x] points to the leftmost child of node x. right-sibling[x] points to the sibling of x immediately to the right. If node x has no children, then left-child[x] = NIL. If node x is the rightmost child of its parent, then right-sibling[x] = NIL. CS583 Fall'06: Basic Data Structures 14 Binary Search Tree • Binary search tree is a binary tree that stores keys and satellite data. The keys are stored in such a way to satisfy the following property: – Let x be a node in a BST. If y is a node in left subtree of x, then key[y] <= key[x]. If y is a node in the right subtree of x, then key[x] <= key[y]. • The above property allows us to print out all keys in a BST in sorted order by an inorder tree walk algorithm (left, root, right). – Similar algorithms include preorder tree walk (root, left, right), and postorder tree walk (left, right, root). 7/1/2016 CS583 Fall'06: Basic Data Structures 15 Inorder Tree Walk Inorder-Tree-Walk (x) 1 if x <> NIL 2 Inorder-Tree-Walk(left[x]) 3 print key[x] 4 Inorder-Tree-Walk(right[x]) It takes (n) to walk an n-node BST, since the procedure is called exactly twice for each node of the tree (left and right child) + printing the node. We prove it formally as follows. Theorem 12.1 If x is the root of an n-node subtree, then Inorder-Tree-Walk(x) takes (n) time. 7/1/2016 CS583 Fall'06: Basic Data Structures 16 Inorder Tree Walk: Performance Proof. On an empty subtree Inorder-Tree-Walk(x) takes a constant time (to compare to NIL), so T(0) = c for some positive constant c. For n>0, suppose the procedure is called on a node x, whose left subtree has k nodes, and the right subtree has (n-k-1) nodes. The time to perform the algorithm is: T(n) = T(k) + T(n-k-1) + d, where d is time to print x. 7/1/2016 CS583 Fall'06: Basic Data Structures 17 Inorder Tree Walk: Performance (cont.) We use the substitution method to show that T(n) = (c+d)n + c. For n=0: T(0) = c. For n>0 we have: T(n) = T(k) + T(n-k-1) + d = ((c+d)k + c) + ((c+d)(n-k-1) + c) + d = ck + dk + c +cn –ck –c + dn –dk –d + c + d = cn + dn + c = (c+d)n + c 7/1/2016 CS583 Fall'06: Basic Data Structures 18 BST: Querying We use the following procedure to search for a node in BST: Tree-Search(x,k) Input: root x and key k Output: pointer to a node with key k, or NIL if not found 1 if x = NIL or k=key[x] 2 return x 3 if k < key[x] 4 return Tree-Search(left[x],k) 5 else 6 return Tree-Search(right[x],k) The nodes encountered form a path downward from the root of the tree, hence the running time of search is O(h), where h is the height of the tree. 7/1/2016 CS583 Fall'06: Basic Data Structures 19 BST: Minimum and Maximum Minimum and maximum elements in BST can be found by following the left, or the right subtree respectively: Tree-Minimum(x) 1 while left[x] <> NIL 2 x = left[x] 3 return x Tree-Maximum(x) 1 while right[x] <> NIL 2 x = right[x] 3 return x 7/1/2016 CS583 Fall'06: Basic Data Structures 20 BST: Finding Successor If all keys are distinct, the successor of the node x is the smallest key greater than key[x]. Tree-Successor(x) 1 if right[x] <> NIL 2 return Tree-Minimum(right[x]) 3 y = parent[x] 4 while y <> NIL and x = right[y] 5 x = y 6 y = parent[y] 7 return y When the right subtree is empty, the successor of x is the lowest ancestor of x, whose left child is also ancestor of x (lines 3-7). To find such ancestor, we find the first one that is not on the "right subtree" path to x. The running time of the above algorithm is O(h) since we either follow a path down or up from the node. 7/1/2016 CS583 Fall'06: Basic Data Structures 21