1. Write a method isbst(node t) that checks that the binary subtree at t is a binary search tree. The method should return true if t is the root of a binary search tree and false otherwise. Assume Node has a field 'key' of Comparable class type Key. You may use these private methods. private Node min(node t) // returns the left-most Node in the subtree t private Node max(node t) // returns the right-most Node in the subtree t (Each of these methods returns null if t is null.) Note that an empty binary tree should be considered as a binary search tree.. private boolean isbst(node t) if (t == null) return true; if (!isbst(t.left)) return false; if (t.left!= null && max(t.left).key.compareto(t.key) >= 0) return false; if (!isbst(t.right)) return false; if (t.right!= null && t.key.compareto(min(t.right).key) >= 0) return false; return true; 2. Design a data structure that supports the following API for a generalized queue. public class GQ<Item item> --------------------------------------------------- GQ() // create an empty generalized queue Item get(int i) // return the ith item from queue void add(item item) // append item to the end of the queue Item remove(int i) // remove the ith item from the queue Your data structure should implement all operations in logarithmic time (or better) as a function of the size of the queue. Here is a sample client, showing the contents of the queue after each insertion / deletion. GQ<String> gq = new GQ<String>(); gq.add("a"); // A gq.add("b"); // A B gq.add("c"); // A B C gq.add("d"); // A B C D String s1 = gq.get(2); // s1 = "C" gq.remove(2); // A B D String s2 = gq.get(2); // s2 = "D" Hint: GQ can store items in a private (ordered) symbol table with integer keys and values of type Item. When an item is added to GQ, put a (key, item) pair in the symbol table with key 0 if the symbol table is empty or with maximum key + 1, if not empty. The GQ get(i) method cannot simply use the symbol table get. a. Which of the following ordered symbol table implementations will you choose in order to met the performance requirement? private BinarySearchST<Integer, String> st; private BST<Integer, String> st; private RedBlackBST<Integer, String> st;. RedBlackBST
b. Give the implementation of add: public void add(item item) public void add(item x) int k; if (st.isempty()) k = 0; else k = st.max() + 1; st.put(k, x); c. Give the implementation of get. public Item get(int i) public Item get(int i) if (i < 0 i >= size()) throw new NoSuchElementException(); int key = st.select(i); return st.get(key); d. Give the implementation of remove. public Item remove(int i) public Item remove(int i) if (i < 0 i >= size()) throw new NoSuchElementException(); int key = st.select(i); Item y = st.get(key); st.delete(key); return y; 3. Consider the 4-sum problem: Given N integers, do any 4 of them sum up to exactly 0? a. Consider the following brute-force solution (ignoring integer overflow). public static foursum(int[] a) int N = a.length; for (int i = 0; i < N; i++) for (int j = i+1; j < N; j++) for (int k = j+1; k < N; k++) for (int l = k+1; l < N; l++) if (a[i] + a[j] + a[k] + a[l] == 0) return true; return false; What is the order of growth of the worst-case running time? N, Nlog(N), N 2, N 3, N 4, 2 N N 4
b. Describe an algorithm for 4-sum that runs in O(N 2 ) time. Assume you have a hash-based symbol table and that put and get for integer keys are each O(1). public class FourSum private static int cnt; private static class Pair private int first; private int second; private Pair(int f, int s) first = f; second = s; public static boolean hasfoursum(int[] x) LinearProbingHashST<Integer, LinkedList<Pair>> st = new LinearProbingHashST<Integer, LinkedList<Pair>> int n = x.length; int[][] sum = new int[n][n]; for(int i = 0; i < n; i++) for(int j = i + 1; j < n; j++) sum[i][j] = x[i] + x[j]; LinkedList<Pair> lst = st.get(sum[i][j]); if (lst == null) lst = new LinkedList<Pair>(); lst.add(new Pair(i,j)); if (sum[i][j] >= 0) st.put(sum[i][j], lst); for(int i = 0; i < n; i++) for(int j = i + 1; j < n; j++) int s = -sum[i][j]; if (s < 0) continue; LinkedList<Pair> lst = st.get(s); if (lst!= null) for(pair p: lst) if (p.first == i p.first == j ) continue; if (p.second == i p.second == j) continue; return true; return false;
4. Below is the result of inserting a set of strings (and associated integer values) into search trie. The integer value is the order in which the string was inserted. a. List (in alphabetical order) the set of strings that were inserted. be 8 bear 9 beer 7 he 5 hear 6 hello 1 her 3 here 4 hero 2
b. Add the string "hat" with value 10 and the string "happy" with value 11 to the trie and draw the new nodes required in the figure above. 5. Given the digraph below as an adjacency list and starting vertex 0, list the vertices in preorder and in postorder. 0: 1, 2 1: 2 2: 3, 4 3: 4: 3 preorder : 0 1 2 3 4 For preorder vertices are inserted into a queue at the call to dfs
postorder : 3 4 2 1 0 For postorder vertices are inserted into a queue at the return from dfs. Does this graph have a topological sort order? If so, give one such order. If not, give a directed cycle. A directed graph has a topological sort if and only if it has no cycles (DAG - directed acyclic graph). In that case reverse postorder is a topological sort. topological sort = reverse postorder : 0 1 2 4 3 (just the reverse of postorder) 6. For the digraph in the previous problem, calculate the edgeto array for the breadth first search method starting at 0; that is parameter s is 0. private void bfs(digraph G, int s) Queue<Integer> q = new Queue<Integer>(); marked[s] = true; q.enqueue(s); while (!q.isempty()) int v = q.dequeue(); for (int w : G.adj(v)) if (!marked[w]) edgeto[w] = v; marked[w] = true; q.enqueue(w); Note: edgeto[s] for the starting vertex s = 0, is not assigned. Give the path from 0 to 3 determined by the edgeto array.
0 -> 2 -> 3 (edgeto[3] = 2; edgeto[2] = 0) 7. Consider the following binary search tree method. public Key mystery(key key) Node best = mystery(root, key, null); if (best == null) return null; return best.key; private Node mystery(node x, Key key, Node best) if (x == null) return best; int cmp = key.compareto(x.key); if (cmp < 0) return mystery(x.left, key, x); else if (cmp > 0) return mystery(x.right, key, best); else return x; a. What does mystery(key) return? i. predecessor of key ii. floor of key iii. ceiling of key iv. successor of key v. the key in the symbol table that is equal to key if it is there or null otherwise. : ceiling of key b. What is the worst-case number of compares (the compareto statement) for mystery? (Assume that the binary search tree is balanced.) i. O(1) ii. O(log(N)) iii. O(N) iv. O(N 2 ) v. O(2 N ) : O(log(N)) 8. For each of the operations on the left below, list which of the symbol table implementations on the right can be used to efficiently implement it. By efficient, we mean O(log(N)) or better on typical ASCII strings (in random order) where N is the number of keys in the data structure. Assume that the data is ``random enough'' to avoid pathological cases. You may also assume uniform hashing. For each operation, you should write between one and five letters (A-E). B, C, D, E C, D, E C, D, E Get the value associated with a given key from the ST. Put a key (and associated value) into the ST; the key may already be in the ST, in which case the value is overwritten. Delete a key (and associated value) from the ST. A. Unordered array B. Ordered array C. RedBlackBST D. Separate-Chaining hashed symbol table E. Trie B, C, E Find the smallest key in the ST. B, C, E E E Find the smallest key in the ST that is greater than or equal to a given string. Find the longest prefix of a given string that is also a key. How many keys in the ST start with a given prefix?
9. This problem is to trace the LSD algorithm for sorting the following array of strings of length 2: i a[i] 0 f b 1 d a 2 e f 3 d c 4 a e 5 b f 6 e a 7 d b Assume the alphabet is a,b,c,d,e,f of size 6. Below show the steps for the right most (least significant) character. a. Step 1: Key index counting. Fill in the count array. char index count[index] a 0 0 b 1 2 c 2 2 d 3 1 e 4 0 f 5 1 6 2 b. Step 2: Convert the count array to indices char index count[index] a 0 0 b 1 2 c 2 4 d 3 5 e 4 5 f 5 6 6 8 At which index in the auxilliary array should the first string "fb" at index 0 in the original array a be placed? c. Use the count array from step 2 to distribute the strings from array a to the auxilliary array aux and show the count array AFTER all strings have been inserted in aux. i aux[i] 0 d a 1 e a 2 f b 3 d b 4 d c 5 a e 6 e f char index count[index] a 0 012 b 1 234 c 2 45 d 3 5 e 4 56 f 5 678 6 8 7 b f 10. AVL tree insertions. Starting with an empty AVL tree show the tree after the keys below are inserted in the order given. Give the rotations required, if any. a. 10, 20, 30 : The Node variable referencing the Node holding key k will be denoted t k.
b. 10, 30, 20 : c. 20, 10, 50, 40, 60, 30 : 11. The following keys are inserted into an empty AVL tree in this order: 40 20 50 10 30 a. Draw the tree b. Delete 40 and show the result. What rotations are required if any? : a. x is first set to the minimum node in the right subtree of the node to be deleted. (50 in this case) b. Next that Node is deleted (recursively) from the right subtree. (In this case that leaves an empty right subtree.) c. Finally, x replaces the node to be deleted by setting its right link to the (modified) right subtree of t and its left link to the left link of t, where t is the node to be deleted. (The modified right subtree is empty in this case. So 50's right link is null.) The tree is now unbalanced to the left at 50. Since the height of 20's left child is not smaller than the height of 20's right child, a single rotation to the right of Node 50 will rebalance the tree.