13
? 0, 1, 2,..., 1000 1 2 3 4 6 7 ? n

Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

  • Upload
    others

  • View
    5

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

Data Structures � Brett Bernstein

Lecture 12: Implicit, N-ary, and Binary Search Trees

Exercises

1. (?) Famous interview problem: A list initially has the 1001 numbers 0, 1, 2, . . . , 1000.Then a single random number is removed from the list, and then it is shu�ed in to arandom order. Design an algorithm to �nd which number was deleted.

2. Implement the following function that computes the number of leaves of a tree.

public static <T> int numLeaves(BinaryTreeNode<T> root)

You can asssume BinaryTreeNode has getLeft and getRight methods.

3. Give the inorder, preorder, and postorder traversals of the following tree.

1

2 3

4 6 7

4. Evaluate the following post�x expression: 1 2 + 3 * 4 5 6 - - /.

5. (?) As in homework 5, compute the number ways to color (red or black) a row of boxesof length n where you cannot have 3 red boxes in a row. Use recursion.

Solutions

1. We have 3 solutions below:

FindMissing.java

import java.util.Arrays;

public class FindMissing {public static int missing(int[] arr){

Arrays.sort(arr);for (int i = 0; i < arr.length; ++i)

if (arr[i] != i) return i;return arr.length;

}public static int missing2(int[] arr){

1

Page 2: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

long N = arr.length;long sum = N*(N+1)/2;for (int i = 0; i < arr.length; ++i)

sum −= arr[i];return (int)sum;

}public static int missing3(int[] arr){

int tmp = 0;for (int i = 0; i < arr.length; ++i)

tmp ^= arr[i];for (int i = 1; i < arr.length; ++i)

tmp ^= i;return tmp;

}}

The �rst one uses sorting and is the slowest. In the second one we know what thesum of all the numbers 0 − 1000 should be, and we subtract the elements of the list.In the last one we use the fact that x^x=0, x^0=x, and that xor is associative andcommutative (i.e., you can rearrange the xors in any order you like).

2. public static <T> int numLeaves(BinaryTreeNode<T> root) {if (root == null) return 0;int cs = numLeaves(root.getLeft()) + numLeaves(root.getRight());return cs == 0 ? 1 : cs;

}

3. (a) preOrder: 1 2 4 3 6 7

(b) inOrder: 4 2 1 6 3 7

(c) postOrder: 4 2 6 7 3 1

4. Applying the algorithm from last class we obtain 9/5.

5. Our code follows:

Boxes.java

import java.util.Arrays;

public class Boxes{

static long[][] cache = new long[3][61];

public static long countValidRows(int n){

2

Page 3: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

return cVRHelpMemo(n, 0);}public static long cVRHelp(int n, int red){

if (red > 2) return 0;if (n == 0) return 1;return cVRHelp(n−1,0) + cVRHelp(n−1,red+1);

}public static long cVRHelpMemo(int n, int red){

if (red > 2) return 0;if (n == 0) return 1;if (cache[red][n] > −1) return cache[red][n];cache[red][n] = cVRHelpMemo(n−1,0)

+ cVRHelpMemo(n−1,red+1);return cache[red][n];

}public static void main(String[] args){

for (int i = 0; i < 3; ++i) Arrays.�ll(cache[i], −1);System.out.println(countValidRows(1));System.out.println(countValidRows(3));System.out.println(countValidRows(6));System.out.println(countValidRows(20));System.out.println(countValidRows(45));System.out.println(countValidRows(60));

}}

In cVRHelp we consider for each box whether to color it red or black. The variable redstores the number of red boxes in a row we currently have. The variable n stores thenumber of remaining boxes to color. As cVRHelp traverses the implicit tree (at eachnode we choose red or black), it covers at most 2n nodes. That said, there are only 3npossible inputs to cVRHelp. Using a technique called memoization we build a tablethat caches the results of cVRHelp so we never have to recompute it. This reduces thetotal runtime of using cVRHelpMemo to Θ(n).

Implicit Trees

As we saw in the countSums problem above, and when we were looking at binary searchearlier in the semester, there is sometimes an implicit tree lurking in the background. Inother words, our recursive code is traversing a tree that doesn't explicitly exist as a datastructure in our code.

Here is a famous problem that we will solve using recursion, and observe the implicittree in the background. Consider the problem of placing 8 non-attacking queens on a chess

3

Page 4: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

board. Recall that a chess board is 8× 8, and that a queen can move any number of squareshorizontally, vertically, or diagonally. To solve this problem we will recursively try all possiblequeen placements. One naive method is to try to put a queen on every square of the board,but this will require us to look at 264 possible placements, an infeasible task. Instead we willproceed one row at a time, since there must be exactly one queen in every row. Each timewe place a queen we will make sure it doesn't attack any of the previously placed queens.In this way we are pruning our tree by cutting o� subtrees we know will not yield solutions.Code follows:

Queens.java

public class Queens{

//Can be made signi�cantly fasterpublic static int placeQueens(int currRow, int[] columns){

int SIZE = columns.length;if (currRow == SIZE) //Leaf of implicit tree{

print(columns);return 1;

}int s = 0;for (int c = 0; c < SIZE; ++c)

//c is the potential column of the new queen{

boolean ok = true;for (int i = 0; i < currRow && ok; ++i)

//looping through previously placed queens{

if (columns[i] == c) ok = false;//Check if slope of line through both queens is 1 or −1if (Math.abs(c − columns[i]) == Math.abs(currRow−i))

ok = false;}if (ok){

columns[currRow] = c;s += placeQueens(currRow+1,columns);

}}return s;

}public static void print(int[] columns){

4

Page 5: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

int SIZE = columns.length;for (int r = 0; r < SIZE; ++r){

for (int c = 0; c < SIZE; ++c){

System.out.print(columns[r] == c ? 'Q' : '.');}System.out.println();

}System.out.println();

}public static void main(String[] args){

System.out.println(placeQueens(0,new int[8]));}

}

Implicit Tree Exercises

1. Write a program that computes the nth Fibonacci number for 0 ≤ n ≤ 90 (�ts in along). The Fibonacci numbers an are de�ned by the following recurrence:

a0 = 0, a1 = 1, an = an−1 + an−2,

for n > 1.

2. (Knapsack Problem) (??) Suppose you have k items you want to return to the store.The array weights[] stores the weight of each item. The array value[] stores how muchmoney each item is worth. You cannot carry more than weightCap total weight. Deter-mine the maximum total value you can return without violating your weight restriction.You cannot bring a fraction of an item. You can assume k ≤ 25.

public static int maxValue(int[] weights, int[] values, int weightCap)

3. Consider the following two di�erent ways of make a tree that allows for more than 2kids per node.

ArrayTreeNode.java

import java.util.ArrayList;

public class ArrayTreeNode<T> {private T value;private ArrayList<ArrayTreeNode<T>> children = new ArrayList<>();public T getValue() { return value; }public void setValue(T t) { value = t; }public ArrayList<ArrayTreeNode<T>> getChildren() { return children; }

}

5

Page 6: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

ListTreeNode.java

public class ListTreeNode<T> {private T value;private ListTreeNode<T> �rstChild;private ListTreeNode<T> nextSibling;public ListTreeNode(T v, ListTreeNode<T> f, ListTreeNode<T> n) {

value = v;�rstChild = f;nextSibling = n;

}public T getValue() { return value; }public void setValue(T t) { value = t; }public ListTreeNode<T> getFirstChild() { return �rstChild; }public ListTreeNode<T> getNextSibling() { return nextSibling; }public void setFirstChild(ListTreeNode<T> f) { �rstChild = f; }public void setNextSibling(ListTreeNode<T> n) { nextSibling = n; }

}

The ArrayTreeNode is self-explanatory. For the ListTreeNode, each node only pointsto its �rst child. Then each node also has a reference to the next sibling. Given these,implement the following functions which compute the number of nodes in the tree.

(a)

public static int <T> getSize(ArrayTreeNode<T> root)

(b)

public static int <T> getSize(ListTreeNode<T> root)

Implicit Tree Solutions

1. Below we have several implementations.

Fibonacci.java

public class Fibonacci {

public static long �b(int n) {if (n <= 1) return n;return �b(n−1) + �b(n−2);

}static long[] cache = new long[91];public static long �bMemo(int n) {

if (n <= 1) return n;if (cache[n] > 0) return cache[n];

6

Page 7: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

return cache[n] = �bMemo(n−1) + �bMemo(n−2);}static long[] dpTable = new long[91];public static void buildTable() {

dpTable[0] = 0;dpTable[1] = 1;for (int i = 2; i < dpTable.length; ++i)

dpTable[i] = dpTable[i−1] + dpTable[i−2];}public static long �bLoop(int n) {

if (n <= 1) return n;long a = 0, b = 1;for (int i = 2; i <= n; ++i) {

long c = b + a;a = b;b = c;

}return b;

}public static void main(String[] args) {

long time = System.nanoTime();System.out.println(�b(40));long time2 = System.nanoTime();System.out.printf("Elapsed = %fs\n",(time2−time)*1e−9);System.out.println(�bMemo(40));long time3 = System.nanoTime();System.out.printf("Elapsed = %fs\n",(time3−time2)*1e−9);buildTable();System.out.println(dpTable[40]);long time4 = System.nanoTime();System.out.printf("Elapsed = %fs\n",(time4−time3)*1e−9);System.out.println(�bLoop(40));long time5 = System.nanoTime();System.out.printf("Elapsed = %fs\n",(time5−time4)*1e−9);

}}

The method �b uses a standard recursion. It uses Θ(n) space but has an runtime

of Θ(�b(n)) which grows like(

1+√5

2

)n

. Using the same memoization technique as

earlier we get �bMemo which uses Θ(n) space and Θ(n) runtime. We can also makean iterative version which computes each Fibonacci number from 0 up to n and storesthe result in dpTable. This also uses Θ(n) space and Θ(n) runtime. Finally we have a�bLoop method which uses Θ(1) space and Θ(n) runtime. This uses the fact that weonly need to track the previous two values. There is a faster method that uses Θ(1)space and Θ(log n) runtime using matrix exponentiation.

7

Page 8: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

2. Code follows:

Knapsack.java

public class Knapsack{

public static int maxValue(int[] weights, int[] values, int weightCap){

return maxValueHelp(weights,values,weightCap,0);}public static int maxValueHelp(int[] weights, int[] values,

int weightCap, int pos){

if (pos == weights.length) return 0;//don't take itemint best = maxValueHelp(weights,values,weightCap, pos+1);if (weights[pos] <= weightCap) {

//take itemint newCap = weightCap−weights[pos];int rec = maxValueHelp(weights, values, newCap, pos+1);best = Math.max(best, values[pos]+rec);

}return best;

}public static void main(String[] args){

int[] weights = {7, 7, 7, 15};int[] values = {8, 8, 8, 23};System.out.println(maxValue(weights,values,21));System.out.println(maxValue(weights,values,20));System.out.println(maxValue(weights,values,40));

}}

Here we simply explore the implicit tree which either takes or doesn't take everyelement. If the weightCap isn't too large we can also apply memoization here toe�ciently solve the problem.

3. Below we have the two implementations.

SizeUtils.java

public class SizeUtils {public static <T> int size(ArrayTreeNode<T> root) {

if (root == null) return 0;int ret = 1;for (int i = 0; i < root.getChildren().size(); ++i)

8

Page 9: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

ret += size(root.getChildren().get(i));return ret;

}public static <T> int size(ListTreeNode<T> root) {

if (root == null) return 0;return 1 + size(root.getFirstChild()) + size(root.getNextSibling());

}}

Note that in the ListTreeNode case no looping is necessary.

Data Structure: Binary Search Trees

Until this point trees have been used to express hierarchical structure in our data. For exam-ple, the tree structure of an expression tree conveys information about how the arithmeticshould be carried out. We now exploit the binary tree structure to facilitate faster datastorage and retrieval. As a result, we will �nd an e�cient implementation of maps, sets.

In a binary search tree we only store nodes that are Comparable or have a Comparator(i.e., they are ordered). For simplicity, we will force all tree nodes to have distinct values.All values in the left subtree of a node must have values strictly less than the node itself.Every value in the right subtree must be strictly larger than the node. This rule must holdat every node. As you will show below, this gives us an algorithm for �nding values in thetree that depends only on the height of the tree. Although searches do not require it, tofacilitate iteration and removal we will assume that binary search tree nodes each have aparent node reference.

When adding a value to a binary search tree, we �rst look for it. If we �nd it, we donothing. Otherwise, we add it in the leaf position where our search terminates. You will�esh this out in a moment. As with many other data structures, the trickiest operation ona binary search tree is removal. To remove a value we �rst �nd the node in the tree holdingthat value. If the node is a leaf we simply remove it. Otherwise, we break removal into casesas we will see below.

Binary Search Tree Exercises

1. Consider the numbers 1, 2, 3, 4, 5, 6, 7.

(a) Give two examples of binary search trees of height 6 using the above values.

(b) Give a binary search of height 2 using the above values.

2. How can you print the values of a binary search tree in ascending order recursively(very short answer)?

3. Use the binary search tree below in the following questions. Apply each operation tothe given tree (not in sequence).

9

Page 10: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

5

1 9

-4 6 13

7

(a) Add the value 3 to the tree.

(b) Add the value 11 to the tree.

(c) Remove the value 13 from the tree.

(d) Remove the value 1 from the tree.

(e) Remove the value 5 from the tree.

4. Implement the following BST functions.

(a) Implement the following function that �nds a node with the given value, or returnsnull.

static BSTNode<Integer> �ndNode(BSTNode<Integer> root, int value)

You can assume that a BSTNode has getLeft, getRight, and getValue.

(b) Give an implementation of the following static function that adds a value to thegiven BST.

static void add(BSTNode<Integer> root, int value)

5. (??) Describe an algorithm for removing a value from a BST. Break your algorithminto the following cases:

(a) Value isn't in the tree.

(b) Value is in a leaf node.

(c) Value is in a node with 1 child.

(d) (??) Value is in a node with 2 children (and thus, not the largest node in thetree). [Hint: Find next largest value.]

Binary Search Tree Solutions

1. (a)

10

Page 11: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

7

6

5

4

3

2

1

1

2

3

4

5

6

7

(b)

4

2 6

1 3 5 7

2. Perform an inOrder traversal.

3. (a)

5

1 9

-4 3 6

7

13

(b)

5

1 9

-4 6 13

7 11

(c)

5

1 9

-4 6

7

11

Page 12: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

(d)

5

-4 9

6

7

13

(e)

6

1 9

-4 7 13

4. The solutions to both parts are given below.

BSTUtils.java

public class BSTUtils{

public static BSTNode<Integer> �ndNode(BSTNode<Integer> root, int value){

if (root == null) return null;if (root.getValue() == value) return root;if (value < root.getValue()) return �ndNode(root.getLeft(),value);return �ndNode(root.getRight(),value);

}

public static void add(BSTNode<Integer> root, int value){

if (root.getValue() == value) return;if (value < root.getValue()){

if (root.getLeft() == null)root.setLeft(new BSTNode<>(value,null,null,root));

else add(root.getLeft(), value);}else

{if (root.getRight() == null)

root.setRight(new BSTNode<>(value,null,null,root));else add(root.getRight(), value);

}

12

Page 13: Data Structures Brett Bernstein Lecture 12: Implicit, N ...brettb/dsSum2016/Lecture12.pdf · Data Structure: Binary Search rees Until this point trees have been used to express hierarchical

}}

Both methods have Θ(h) worst-case runtime, where h is the height of the tree. As-suming nothing about the BST, this is Θ(n) when the tree degenerates to a linkedlist.

5. (a) Do nothing.

(b) Remove the leaf node.

(c) Remove the node and replace with the child.

(d) Find the node s containing the next largest value (the successor). Note that thesuccessor will be in the right subtree of the node being removed, and will not havea left child. Let r denote the node we are removing. If s is the right child of rwe can just replace r with s. Otherwise, �rst replace s with the right child of s(freeing it up), and then replace r with s.

13