Elementary Data Structures Jeff Chastine Stacks and Queues • • • • The element removed is prespecified A stack implements last-in, first-out (LIFO) A queue implements first-in, first-out (FIFO) What about FOFI, or LILO? Jeff Chastine Stacks • • • • • • Implemented using an array Use PUSH and POP operations Has attribute top[S] Contains elements S[1..top[S]] When top[S] = 0, the stack is empty If top[S] > n, then we have an overflow Jeff Chastine 1 2 3 15 6 2 4 5 6 7 5 6 7 9 top[S] = 4 1 2 3 15 6 2 4 9 17 3 top[S] = 4 1 2 3 15 6 2 4 5 9 17 top[S] = 4 Jeff Chastine 6 3 7 STACK-EMPTY (S) 1 if top[S] = 0 2 then return TRUE 3 else return FALSE PUSH (S, x) 1 top[S] top[S] + 1 2 S[top[S]] x POP (S) 1 if STACK-EMPTY (S) 2 then error “underflow” 3 else top[S] top[S] – 1 4 return S[top[S]+1] Note: All operations performed in O(1) Jeff Chastine Queues • • • • • • • Supports ENQUEUE and DEQUEUE operations Has a head and tail New elements are placed at the tail Dequeued element is always at head Locations “wrap around” When head[Q] = tail[Q], Q is empty When head[Q] = tail[Q] + 1, Q is full Jeff Chastine 1 2 3 4 5 6 7 8 9 10 11 15 6 9 8 4 head[Q] = 7 1 2 3 5 3 4 5 tail[Q] = 7 1 2 3 5 3 tail[Q] = 7 6 12 tail[Q] = 7 7 8 9 10 11 12 15 6 9 8 4 17 7 8 9 10 11 12 15 6 9 8 4 17 head[Q] = 7 4 5 6 head[Q] = 7 Jeff Chastine ENQUEUE (Q, x) 1 Q[tail[Q]] x 2 if tail[Q] = length[Q] 3 then tail[Q] 1 4 else tail[Q] tail[Q] + 1 DEQUEUE (Q) 1 x Q[head[Q]] 2 if head[Q] = length[Q] 3 then head[Q] 1 4 else head[Q] head[Q] + 1 5 return x Jeff Chastine Linked Lists • A data structure where the objects are arranged linearly according to pointers – Each node has a pointer to the next node • Can be a doubly linked list – Each node has a next and previous pointer • • • • Could also be circularly linked If prev[x] = NIL, we call that node the head If next[x] = NIL, we call that node the tail The list may or may not be sorted! Jeff Chastine Example head[L] / 9 16 4 Jeff Chastine 1 / Searching a Linked List LIST-SEARCH (L, k) 1 x head[L] 2 while x NIL and key[x] k 3 do x next[x] 4 return x Worst case, this takes (n) Jeff Chastine Inserting into a Linked List LIST-INSERT (L, x) 1 next[x] head[L] 2 if head[L] NIL 3 then prev[head[L]] x 4 head[L] x 5 prev[x] NIL Note: runs in O(1) Jeff Chastine Example head[L] / 9 16 4 1 / head[L] / 25 9 16 Jeff Chastine 4 1 / Deleting from a Linked List LIST-DELETE (L, x) 1 if prev[x] NIL 2 then next[prev[x]] next[x] 3 else head[L] next[x] 4 if next[x] NIL 5 then prev[next[x]] prev[x] Jeff Chastine Example head[L] / 9 16 4 1 / head[L] / 25 9 16 4 1 head[L] / 25 9 16 Jeff Chastine 1 / / Sentinel Nodes • Used to simplify checking of boundary conditions • Here is a circular, doubly-linked list 9 16 nil[L] Jeff Chastine 4 1 Binary Search Trees BSTs • Very important data structure • Can support many dynamic-set operations: – SEARCH – MINIMUM/MAXIMUM – PREDECESSOR/SUCCESSOR – INSERT/DELETE • Operations take time proportional to the height of the tree, often O(lg n) Jeff Chastine Binary Search Trees • Each node has a key, as well as left, right and parent pointers • Thus, the root has a parent = NIL • Binary search tree property: – Let x be a node. If y is a node in the left subtree, key[y] key[x]. If y is a node in the right subtree, key[y] > key[x]. Jeff Chastine 5 3 2 In-Order: 2, 3, 5, 5, 7, 8 Pre-Order: 5, 3, 2, 5, 7, 8 Post-Order: 2, 5, 3, 8, 7, 5 7 5 8 INORDER-TREE-WALK (x) 1 if x NIL 2 then INORDER-TREE-WALK(left[x]) 3 print key[x] 4 INORDER-TREE-WALK(right[x]) Jeff Chastine Proof: In-Order Walk Takes (n) • Let c be the time for the test x NIL, and d be the time to print • Suppose tree T has k left nodes and n-k right nodes. • Using substitution, we assume T(n)=(c+d)n+c T(n) = T(k) + T(n-k-1) + d = ((c+d)k+c) + ((c+d)(n-k-1)+c) + d = (c+d)n + c – (c+d) + c + d = (c+d)n + c Jeff Chastine Querying a Binary Search Tree • Tree should support the following: – SEARCH – determine if an element exists in T – MINIMUM – return the smallest element in T – MAXIMUM – return the largest element in T – SUCCESSOR – given a key, return the next largest element, if any – PREDECESSOR – given a key, return the next smallest element, if any Jeff Chastine TREE-SEARCH (x, k) 1 if x = NIL or k = key[x] 2 then return x 3 if k < key[x] 4 then return TREE-SEARCH(left[x], k) 5 else return TREE-SEARCH(right[x], k) Note: run time is O(h), where h is the height of the tree Jeff Chastine Tree Minimum/Maximum TREE-MINIMUM(x) 1 while left[x] NIL 2 do x left[x] 3 return x TREE-MAXIMUM (x) 1 while right[x] NIL 2 do x right[x] 3 return x Note: binary search tree property guarantees this is correct Jeff Chastine Successor/Predecessor • Successor is the node with the smallest key greater than key[x]. • Not necessary to compare keys! • Two cases: – If right subtree is non-empty, find its minimum – If right subtree is empty, find lowest ancestor of x whose left child is also an ancestor of x Jeff Chastine TREE-SUCCESSOR (x) 1 if right[x] NIL 2 then return TREE-MINIMUM(right[x]) 3 y p[x] 4 while y NIL and x = right[y] 5 do x y 6 y p[y] 7 return y Note: simply go up the tree until we encounter a node that is the left child. Runs in O(h) Jeff Chastine Inserting into a BST 50 30 27 56 71 43 88 Jeff Chastine Inserting into a BST 50 30 27 71 43 56 88 Jeff Chastine Inserting into a BST 50 74 30 27 71 43 56 Jeff Chastine 88 Inserting into a BST 50 30 27 71 43 56 Jeff Chastine 74 88 Inserting into a BST 50 30 27 71 43 56 Jeff Chastine 88 74 Inserting into a BST 50 30 27 71 43 56 88 74 Jeff Chastine Deleting • Three cases, given node z: – z has no children. Delete it, and update the parent. – If z has only one child, splice it out by making a link between its child and parent. – If z has two children, splice out z’s successor y and replace z with y Jeff Chastine 15 5 15 16 3 12 10 5 20 13 18 16 3 12 23 10 6 6 7 7 Case 1: z has no children Jeff Chastine 20 18 23 15 5 15 16 3 12 10 20 13 18 20 5 3 12 23 10 6 6 7 7 Case 2: z has one child Jeff Chastine 18 13 23 z 15 5 15 16 3 12 10 20 13 18 3 12 23 10 6 y 20 6 7 7 Case 3: z has two children Jeff Chastine 18 13 23