From 06e9adaba4ab5983541c2cb27401569e79cb0266 Mon Sep 17 00:00:00 2001 From: anebz Date: Tue, 15 Oct 2019 09:52:18 +0200 Subject: [PATCH] 4.2. create minimal BST from array --- 04. Trees and graphs/4.2. minimal_tree.md | 134 ++++++++++++++++++++++ README.md | 3 + 2 files changed, 137 insertions(+) create mode 100644 04. Trees and graphs/4.2. minimal_tree.md diff --git a/04. Trees and graphs/4.2. minimal_tree.md b/04. Trees and graphs/4.2. minimal_tree.md new file mode 100644 index 0000000..cad8d1e --- /dev/null +++ b/04. Trees and graphs/4.2. minimal_tree.md @@ -0,0 +1,134 @@ +# 4.2. Minimal tree + +> Given a sorted (increasing order) array with unique integer elements, create a binary search tree with minimal height. + +From the README.md: + +```bash +# This inequality must be true for all of a node's descendents, not just its immediate children. This is a binary search tree: + +* 8 + * 4 + * 2 + * 6 + * 20 + * 10 + * 30 +``` + +If I have an array [2, 4, 6, 8, 10, 20, 30], how do I put it in tree form? + +* 2 on the left +* 4 as parent +* 6 as children of 4 +* 8 as parent of 4 (but how do we know this?) +* 20 as child of 8 +* 10 as child of 20 +* 30 as child of 30 + +## Hints + +> A minimal binary tree has about the same number of nodes on the left of each node as on the right. Focusing on just the root, how do we ensure that about the same number of nodes are on the left of the root as on the right? + +Picking the middle number of the array? root = nums[len(nums)/2] + +> Find an 'ideal' next element to add and repeatedly call `insertValue`. This will be inefficient as you would have to repeatedly traverse the tree, do recursion instead. Can you divide the problem into subproblems? + +First create uppermost root, then recursively divide the array into 2, find the root of that (the element in the half of the half of the array), and add those roots as children of the uppermost root. + +```java +import java.util.List; + +class Node { + public int value; + public Node[] children; +} + +class Tree { + public Node root; +} + +void createMinimalTree(List nums) { + + if (nums.size() == 1) { + return nums[0]; + } + + Node root = new Node(); + root.value = nums[int(nums.size()/2)]; // is supposed to do round down + + // https://stackoverflow.com/a/21587978/4569908 + int chunkSize = nums.size() % 2 == 0 ? nums.size() / 2 : (nums.size() / 2) + 1; + List firstHalf = nums.subList(0, chunkSize - 1); // -1 to exclude the root + List secondHalf = nums.subList(chunkSize + 1, nums.size()); + + root.children.add(createMinimalTree(firstHalf)); + root.children.add(createMinimalTree(secondHalf)); +} +``` + +Something like this should work: + +* input = [2, 4, 6, 8, 10, 20, 30] root.value = 8, firstHalf = [2, 4, 6], secondHalf = [10, 20, 30] + * input = [2, 4, 6] root.value = 4, firstHalf = [2], secondHalf = [6] + * input = [2], return 2 + * 4 has 2 as child + * input = [6], return 6 + * 4 has 2 and 6 as children +* 8 has 4 as child + * etc. + +> Imagine having a `createMinimalTree` method that returns a minimal tree for a given array, but doesn't operate on the root of the tree. Can you use this to operate on the root of the tree? Can you write the base case for the function? + +Isn't that the function I've just done? Maybe the root is missing. + +```java +void createMinimalTree(Node root, List nums) { + + if (nums.size() == 1) { + return nums[0]; + } + + root.value = nums.at(int(nums.size()/2)); // is supposed to do round down + + // https://stackoverflow.com/a/21587978/4569908 + int chunkSize = nums.size() % 2 == 0 ? nums.size() / 2 : (nums.size() / 2) + 1; + List firstHalf = nums.subList(0, chunkSize - 1); // -1 to exclude the root + List secondHalf = nums.subList(chunkSize + 1, nums.size()); + + root.children.add(root.at(chunkSize), createMinimalTree(firstHalf)); + root.children.add(root.at(chunkSize), createMinimalTree(secondHalf)); +} + +Node superroot = new Node(); +createMinimalTree(superroot, nums); +``` + +`.at()` is better than `[]` in arrays I think. + +## Solution + +We want the root to be the middle of the array. The middle of each subsection of the array becomes the root of the node. Left half of array is left subtree, and the right half of the array is the right subtree. + +We could just use `root.insertNode(int v)` inserting the value `v` recursively, but we'd need to traverse the tree -> O(nlogn). Or we can cut out the extra traversals by recursively using the `createMinimalBST`. This is passed just a subsection of the array and returns the root of a minimal tree for that array. Algorithm: + +1. Insert into the tree the middle element of the array +2. Insert (into left subtree) the left subarray elements +3. Insert (into right subtree) the right subarray elements +4. Recurse + +```java +TreeNode createMinimalBST(int[] array) { + return createMinimalBST(array, 0, array.length - 1) +} + +TreeNode createMinimalBST(int arr[], int start, int end) { + if (end < start) return; + + int mid = (start + end) / 2; + TreeNode n = new TreeNode(arr[mid]); + n.left = createMinimalBST(arr, start, mid - 1); + n.right = createMinimalBST(arr, mid + 1, end); + return n; +} +``` diff --git a/README.md b/README.md index 9b6f828..96a3f58 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ If you can't afford to buy the book, you can find a free pdf [here](https://leon ## Chapter 4 Trees and graphs +* Find route between nodes in graph +* Create minimal binary search tree from array + ## Chapter 7 Object-oriented design * Hash tables