Top Banner
+ * - 8 + 1 2 9 * 4 3 n ?? ?
13

Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

May 27, 2018

Download

Documents

truongkiet
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

Data Structures � Brett Bernstein

Lecture 11: Binary Trees

Exercises

1. Below is an expression tree with numbers as leaves and operators as internal nodes(non-leaves). Each internal node's value is obtained by applying the operator to thevalues of its left and right children. What is the value of the tree below?

+

* -

8+

1 2

9 *

4 3

2. A rooted binary tree is a tree where each node has at most 2 children, and one of thenodes is called the root. Suppose you have such a tree with n nodes.

(a) What is the maximum height of such a tree? The height is the maximum numberof steps needed to get from the root to a leaf.

(b) (??) What is the minimum possible height of such a tree?

3. Consider the following folder structure on a �le system:

Stu�/Docs/MathDocs/

Calc.pdfLinAlg.pdf

CSDocs/DS.pdfAlgo.pdf

Other/hw1.pdfhw2.pdfhw3.pdf

Draw the above as a tree with each folder or �le in a node, and Stu�/ as the root.

4. (?) In java.io there is a class File that represents a �le or directory (folder) on a �lesystem. Here are some of its many methods:

� �le.getName(): Returns the name (a String) of the �le/directory.

1

Page 2: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

� �le.isDirectory(): Returns (a boolean) if �le is a directory.

� �le.listFiles(): Returns a File[]. If it is a directory, it returns all of the contained�les/directories. Otherwise returns null;

� �le.length(): If it is a �le, returns its length (a long) in bytes.

(a) Implement the following method which computes the total size of all �les con-tained within the given folder (directly, or indirectly). If the argument f is just a�le, simply return its size.

public static long totalSize(File f)

(b) Implement the following method which prints out all �les/directories beneath thegiven folder. Each time you descend into a subdirectory indent by 4 spaces. Usethe example in Problem 3 above as a guideline.

public static void printDirectoryTree(File f)

Solutions

1. ((1 + 2) ∗ 8) + (9− (4 ∗ 3)) = 24 + (−3) = 21

2. (a) n− 1 (every non-leaf node has 1 child)

(b) blg(n)c. To see this, note that you can �t 2n− 1 nodes into a tree of height n− 1by making every non-leaf node have 2 children (a complete binary tree). If youhave more than 2n − 1 nodes, you need a tree of height at least n. Thus if youhave between 2n and 2n+1− 1 nodes the minimum possible height is n giving ourformula.

3. Note that the tree below is not binary since Other has 3 children.

Stuff/

Docs/ Other/

MathDocs/ CSDocs/

Calc.pdf LinAlg.pdf DS.pdf Algo.pdf

hw1.pdf hw2.pdf hw3.pdf

4. Below is our code for both parts.

FileUtils.java

import java.io.File;import java.util.Scanner;

2

Page 3: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

public class FileUtils{

public static long totalSize(File f){

if (!f.isDirectory()) return f.length();long ret = 0;for (File � : f.listFiles()) ret += totalSize(�);return ret;

}

public static void printDirectoryTree(File f){

printDirHelp(f,"");}public static void printDirHelp(File f, String tab){

String name =f.getName() + (f.isDirectory() ? "/" : "");

System.out.println(tab+name);if (f.isDirectory())

for (File � : f.listFiles())printDirHelp(�,tab+" ");

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

Scanner in = new Scanner(System.in);File f = new File(in.nextLine());System.out.println("Total size = "+totalSize(f));printDirectoryTree(f);

}}

Both are implemented recursively. We will see this type of code is common when youneed to visit all of the nodes of a tree. We haven't optimized the code at all, especiallyin our String handling.

Data Structure: Binary Tree

In a binary tree each node has up to 2 children, which we will label as left and right. As inthe case of linked lists, our trees will be composed of nodes which look as follows.

TreeNode.java

public class TreeNode<T>{

private T value;

3

Page 4: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

private TreeNode<T> left, right; //Children//private TreeNode<T> parent; //optional

public TreeNode(T t, TreeNode<T> l, TreeNode<T> r){

value = t;left = l;right = r;

}

public T getValue() { return value; }public void setValue(T t) { value = t; }public TreeNode<T> getLeft() { return left; }public TreeNode<T> getRight() { return right; }public void setLeft(TreeNode<T> t) { left = t; }public void setRight(TreeNode<T> t) { right = t; }

}

As can be seen above, we sometimes include a reference to the parent node. One node inour tree will be designated as the root, and it will not be the child of any other node (has noparent). Not having a left or right child is indicated with null. Leaves of the tree will havenull for both their left and right children.

Tree Terminology

The size of a tree is the number of nodes in it. The height is the longest number of steps(i.e., edges) from the root to any leaf. Given a node, its depth is the number of steps fromit to the root. The set of all nodes with a �xed depth is called a level. If we �x a node ina tree, and look at all of its descendents we get a subtree of the original tree that is rootedat the given node. In many of our algorithms, we will decompose a tree into the subtreeswhich we act on recursively.

Tree Exercises

1. Suppose you have a binary tree where each node is a TreeNode<T>. Implement thefollowing static methods. For each give the runtime and space usage.

(a) static <T> int size(TreeNode<T> root): Gives the size of the tree with the givenroot.

(b) static <T> int height(TreeNode<T> root): Gives the height of the tree with thegiven root.

(c) static int sum(TreeNode<Integer> root): Gives the sum of the values of the nodesin the tree with the given root. Here we assume the node values are Integers.

(d) (?)

4

Page 5: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

static <T> ArrayList<T> getLevel(TreeNode<T> root, int d)

Returns all values at depth d in an ArrayList. Order the ArrayList left-to-rightin how the nodes would appear in a diagram of the tree.

(e) (??)

static <T> void printLevelOrder(TreeNode<T> root)

Prints each value in the tree on a separate line. Go level-by-level with lower depthlevels coming earlier. Order each level left-to-right as in the previous problem.Your implementation should have Θ(n) worst-case performance, where n is thenumber of nodes in the tree.

2. Consider the following 3 functions.

public static <T> void preOrder(TreeNode<T> root) {if (root == null) return;System.out.print(root.getValue()+" ");preOrder(root.getLeft());preOrder(root.getRight());

}public static <T> void inOrder(TreeNode<T> root) {if (root == null) return;inOrder(root.getLeft());System.out.print(root.getValue()+" ");inOrder(root.getRight());

}public static <T> void postOrder(TreeNode<T> root) {if (root == null) return;postOrder(root.getLeft());postOrder(root.getRight());System.out.print(root.getValue()+" ");

}

Using the following tree show what the output is when each of the above methods isrun.

+

* -

8+

1 2

9 *

4 3

Tree Solutions

1. Below is the source code for all of the parts.

5

Page 6: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

TreeUtils.java

import java.util.ArrayDeque;import java.util.ArrayList;import java.util.Queue;

public class TreeUtils{

public static <T> int size(TreeNode<T> root){

if (root == null) return 0;return 1 + size(root.getLeft()) + size(root.getRight());

}

public static <T> int height(TreeNode<T> root){

if (root == null) return −1;return 1 + Math.max(height(root.getLeft()), height(root.getRight()));

}

public static int sum(TreeNode<Integer> root){

if (root == null) return 0;return root.getValue() + sum(root.getLeft()) + sum(root.getRight());

}

public static <T> ArrayList<T> getLevelSlow(TreeNode<T> root,int d)

{ArrayList<T> ret = new ArrayList<>();if (root == null) return ret;if (d == 0){

ret.add(root.getValue());return ret;

}ret.addAll(getLevelSlow(root.getLeft(),d−1));ret.addAll(getLevelSlow(root.getRight(),d−1));return ret;

}

public static <T> ArrayList<T> getLevel(TreeNode<T> root,int d) {

ArrayList<T> ret = new ArrayList<>();getLevelHelp(root, d, ret);return ret;

6

Page 7: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

}public static <T> void getLevelHelp(

TreeNode<T> root, int d, ArrayList<T> al) {if (root == null) return;if (d == 0) {

al.add(root.getValue());return;

}getLevelHelp(root.getLeft(),d−1,al);getLevelHelp(root.getRight(),d−1,al);

}

public static <T> void printLevelOrder(TreeNode<T> root){

Queue<TreeNode<T>> q = new ArrayDeque<>();q.add(root);while(!q.isEmpty()){

TreeNode<T> node = q.poll();System.out.println(node.getValue());if (node.getLeft() != null)

q.add(node.getLeft());if (node.getRight() != null)

q.add(node.getRight());}

}//Creates an expression tree from a post�x expressionpublic static TreeNode<String> makeExpressionTree(String post�x){

//Used as a stackArrayDeque<TreeNode<String>> s = new ArrayDeque<>();String[] toks = post�x.split("\\s+");String operator = "+*−/";for (String tok : toks){

if (operator.indexOf(tok) != −1){

TreeNode<String> right = s.pop();TreeNode<String> left = s.pop();s.push(new TreeNode<>(tok,left,right));

} else s.push(new TreeNode<>(tok,null,null));}return s.pop();

}

7

Page 8: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

public static void main(String[] args){

TreeNode<String> n = makeExpressionTree("1 2 + 8 * 9 4 3 * − +");System.out.printf("Size = %d\n", size(n));System.out.printf("Height = %d\n", height(n));System.out.printf("getLevel(0) = %s\n", getLevel(n,0));System.out.printf("getLevel(1) = %s\n", getLevel(n,1));System.out.printf("getLevel(2) = %s\n", getLevel(n,2));System.out.printf("getLevel(3) = %s\n", getLevel(n,3));System.out.printf("getLevel(4) = %s\n", getLevel(n,4));printLevelOrder(n);

}}

As you can see above, for all but the last one we solved the required task by solvingthe same problem on the left and right subtrees. This type of problem decompositionoccurs frequently when writing recursive functions. In all cases the worst-case runtimeis Θ(n), the number of nodes in the tree. The worst-case space usage is determinedby the height of the tree, which is also Θ(n) in the worst case. We also include aslower getLevelSlow method which must perform extra copying at each node. This hasruntime Θ(n2) in the worst case.

For printLevelOrder we traverse the tree level by level using a technique called breadth-�rst search which we will see again later with graphs. This is implemented using a queueto keep track of each new node. The worst-case runtime space are again both Θ(n).Here the space doesn't depend directly on the height of the tree, but on how large thequeue becomes. If we instead call getLevel repeatedly to get each level we would havea Θ(n2) runtime in the worst-case.

2. (a) preOrder: + * + 1 2 8 - 9 * 4 3

(b) inOrder: 1 + 2 * 8 + 9 - 4 * 3

(c) postOrder: 1 2 + 8 * 9 4 3 * - +

Here is a slightly altered version of the traversals where we include appropriately placedparentheses:

(a) preOrderParen: +( *( +( 1, 2 ), 8 ), -( 9, *( 4, 3 ) ) )

(b) inOrderParen: ( ( ( 1 + 2 ) * 8 ) + ( 9 - ( 4 * 3 ) ) )

(c) postOrderParen: ( ( ( 1 2 + ) 8 * ) ( 9 ( 4 3 * ) - ) + )

The code to generate both of these can be found in TreeTraversals.java.

8

Page 9: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

Tree Traversals and Expressions

We have already seen several examples of how to process all of the nodes of a tree (calledtraversing a tree). Some examples are preorder, inorder, postorder, and level-by-level (orbreadth-�rst) traversals.

When processing an expression tree, the output of the traversals are called pre�x, in�xand post�x notation. In�x corresponds to the standard method of placing the operatorsbetween their operands. For in�x the parentheses are important since otherwise it is unclearwhich order to perform the operations in. Pre�x puts the operator name before the operands.This is more common when the operators are named functions, like exp or max. For example,in the parenthesized pre�x above we write +(1, 2) instead of 1 + 2. In post�x the operatorappears after the operands. It turns out that pre�x and post�x do not require parenthesessince they fully specify the order of operations (assuming the arity of all the operators is�xed).

Expressions in post�x or pre�x notation are very easy to evaluate using a stack. Herewe outline an algorithm for evaluating a post�x expression (we include code below with oursolutions). Every time you see a number you push it on the stack. When you process anoperator pop the top two elements o� the stack, and then push the result of the operation(the �rst pop gives the right operand, the second pop gives the left operand). When donethe value remaining on the stack is the result.

Pre�x/Post�x Exercises

1. Determine if the following post�x expressions are valid, and evaluate them if so.

(a) 1 2 + 3 *

(b) 1 2 - 3 4 +

(c) 1 2 3 4 5 - - - +

(d) + 1 2

(e) 1 2 + +

2. Show how to determine if a post�x expression is valid.

3. Evaluate the following valid pre�x expression: + * 1 2 * + 1 3 - 4 5.

4. (?) Suppose a binary tree has distinct Integer values in all of its nodes. The preOrdertraversal is 1 2 4 5 6 3 7 8 9. The inOrder traversal is 5 4 2 6 1 7 8 3 9. Draw the tree.

5. (??) Describe an algorithm for evaluating a pre�x expression.

6. Implement the following function:

public static int countSums(int[] arr, long s)

9

Page 10: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

which counts the number of subsequences of arr that sum to s. A subsequence isobtained by omitting 0 or more elements of arr. If you omit all of arr then the sum is0. You can assume arr has at most 25 elements.

Pre�x/Post�x Solutions

1. (a) 9

(b) Not valid.

(c) -1

(d) Not valid.

(e) Not valid.

2. Use the stack algorithm mentioned earlier. If pop ever fails when processing an opera-tor, or if the stack doesn't have size 1 at the end the expression isn't valid. Otherwise,it is valid. If you are testing validity without also computing the expression, you don'teven need the stack. Simply keep track of the size of the stack after each operation inan int.

3. -2

4. The preOrder traversal starts with 1 so the root is 1. The inOrder traversal then tellsus the left subtree of the root has 4 nodes and the right subtree has 4 nodes. We canproceed in this fashion.

1

2 3

4 6 7 9

5 8

5. We will use an algorithm that is almost identical to that for the post�x expression, butwe process the expression right-to-left. The only di�erence is the order of the operandswhen processing an operator. Now the �rst pop yields the left operand, and the secondpop yields the right operand. We include code for both post�x and pre�x processingbelow.

ExpressionUtils.java

import java.util.ArrayDeque;

public class ExpressionUtils{

static String operators = "+*−/";

10

Page 11: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

public static int eval(char op, int left, int right){

if (op == '+') return left + right;else if (op == '−') return left − right;else if (op == '*') return left * right;else return left/right;

}public static int processPost�x(String expr){

String[] toks = expr.split("\\s++");ArrayDeque<Integer> stack = new ArrayDeque<>();for (String s : toks){

if (operators.indexOf(s) != −1){

int right = stack.pop();int left = stack.pop();stack.push(eval(s.charAt(0),left,right));

} else stack.push(new Integer(s));}if (stack.size() != 1) throw new IllegalStateException();return stack.pop();

}public static int processPre�x(String expr){

String[] toks = expr.split("\\s++");ArrayDeque<Integer> stack = new ArrayDeque<>();for (int i = toks.length−1; i >= 0; −−i){

String s = toks[i];if (operators.indexOf(s) != −1){

int left = stack.pop();int right = stack.pop();stack.push(eval(s.charAt(0),left,right));

} else stack.push(new Integer(s));}if (stack.size() != 1) throw new IllegalStateException();return stack.pop();

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

System.out.println(processPost�x("1 2 + 3 *"));System.out.println(processPost�x("1 2 3 4 5 − − − +"));System.out.println(processPost�x("1 2 + 8 * 9 4 3 * − +"));

11

Page 12: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

System.out.println(processPre�x("+ * 1 2 * + 1 3 − 4 5"));}

}

6. One way to solve this problem is by looping through bitmasks. Here we will give arecursive solution. First the code:

CountSums.java

public class CountSums{

public static int countSums(int[] arr, long s){

return countSumsHelp(arr, s, 0, 0);}public static int countSumsHelp(int[] arr, long s, int pos, long sum){

if (pos == arr.length) return s == sum ? 1 : 0;int ret = 0;ret += countSumsHelp(arr,s,pos+1,sum+arr[pos]);ret += countSumsHelp(arr,s,pos+1,sum);return ret;

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

int[] arr0 = {2,3,5};int[] arr1 = {0,0,0,0,0,0};int[] arr2 = {1,2,3,4,5,6,7,8};int[] arr3 = {1,1,1,1,1,1,1,1,1,1};System.out.println(countSums(arr0,5));System.out.println(countSums(arr1,0)); //2^6System.out.println(countSums(arr1,1)); //0System.out.println(countSums(arr2,8));System.out.println(countSums(arr2,10));System.out.println(countSums(arr3,3)); //10 choose 3

}}

The idea is that for each element of the array we have two choices: use the elementin our sum or not. Below is a binary tree representing our sequence of choices. Eachnode contains the corresponding sum when run with arr = {2,3,5} and s = 5.

12

Page 13: Data Structures Brett Bernstein Lecture 11: Binary Treesbrettb/dsSum2016/Lecture11.pdf ·  · 2016-07-08Data Structures Brett Bernstein Lecture 11: Binary Trees Exercises 1.Below

0

0

use 2

2

0

use 3

3 2

use 3

5

0

use 5

5 3

use 5

8 2

use 5

7 5

use 5

10

In the above implementation pos tracks what level of the tree we are on, and sumtracks the value in the node. The leaves represent all possible subsequence sums.

13