about BinarySearchTree 3

Thu, Sep 9, 2021 8-minute read

master BST in algo

Recover Binary Search Tree 99

Given a Binary Search Tree with only two nodes swapped. Try to find them and recover the binary search tree.

Input: 

               4

              / \

             2   6  

            / \   / \

           1  5 3  7

Output:       4

                  / \

                2   6

               /  \   / \

            1  3   5  7

 
image

BST

TreeNode prev = null;
  TreeNode first = null;
  TreeNode second = null;

  public TreeNode recover(TreeNode root) {
    
    helper(root);

    int tmp = first.key;
    first.key = second.key;
    second.key = tmp;
    
    return root;
  }

  private void helper(TreeNode root) {
    if( root == null) {
      return ;
    } 
    helper(root.left);
    if(prev != null) {
      if(prev.key > root.key) {
        if(first == null) {
          first = prev;
        }
        second = root;
      }
    }
     
  
    prev = root;
    helper(root.right);

时间复杂度:最坏情况下(即待交换节点为二叉搜索树最右侧的叶子节点)我们需要遍历整棵树,时间复杂度为 O(N)O(N), 其中 NN 为二叉搜索树的节点个数。

空间复杂度:O(H)O(H),其中 HH 为二叉搜索树的高度。中序遍历的时候栈的深度取决于二叉搜索树的高度。

中序遍历有一个特色,比如

. 10 . /
. 5 15 . / \ /
. 3 8 13 16

inOrder: 3 –> 5 -> 8 -> 10 –> 13 –> 15 –> 16

越来越大,这也是 if(prev.key > root.key) 是错误的原因

. 10 . /
. 15 5 . / \ /
. 3 8 13 16

inOrder: 3 –> 15 -> 8 -> 10 –> 13 –> 5 –> 16

. 10 . /
. 3 15 . / \ /
. 5 8 13 16

inOrder: 5 –> 3 -> 8 -> 10 –> 13 –> 5 –> 16

前小后大,就是中序遍历

Construct Binary Search Tree from Preorder Traversal 1008

Given an array of integers preorder, which represents the preorder traversal of a BST (i.e., binary search tree), construct the tree and return its root.

It is guaranteed that there is always possible to find a binary search tree with the given requirements for the given test cases.

A binary search tree is a binary tree where for every node, any descendant of Node.left has a value strictly less than Node.val, and any descendant of Node.right has a value strictly greater than Node.val.

A preorder traversal of a binary tree displays the value of the node first, then traverses Node.left, then traverses Node.right.

 

Example 1:


Input: preorder = [8,5,1,7,10,12]
Output: [8,5,10,1,7,null,12]
Example 2:

Input: preorder = [1,3]
Output: [1,null,3]

二分法, 无语,二分又混乱了,需要的 return 左边的值,mid 如果压在左边, 用两个值 一大一小,就能发现容易错过,所以压在右边

public TreeNode bstFromPreorder(int[] preorder) {
  if (preorder.length == 0) {
    return null;
  }
  return helper(preorder, 0, preorder.length - 1);
}
    
    
private TreeNode helper(int[] pre, int start, int end) {
  if (start > end) {
    return null;
  }
   
  TreeNode root = new TreeNode(pre[start]);
  if (start == end) {
    return root;
  }

  int i = start, j = end;
        
  while (i < j) {
    int mid = i + (j - i + 1) / 2;
    if (pre[mid] < pre[start]) {
      i = mid ;
    } else {
      j = mid -1;
    }
  }
   
    
    
  root.left = helper(pre, start + 1, i);
  root.right = helper(pre, i + 1, end);

  return root;
}
    
   
    

时间: O(N log N), 寻找分界线复杂度是 O(log N)

空间: O (n)

用一个的方式,上标 时间: O(N )

空间: O (n)

这里 i 是 index, 为了避免全局变量

public TreeNode bstFromPreorder(int[] preorder) {
  return  bstFromPreorder(preorder, Integer.MAX_VALUE, new int[]{0});
}

public TreeNode bstFromPreorder(int[] pre, int bound, int[] i) {
  if (i[0] == pre.length || pre[i[0]] > bound) {
    return null;
  }

  TreeNode root = new TreeNode(pre[i[0]++]);
  root.left = bstFromPreorder(pre, root.val, i);
  root.right = bstFromPreorder(pre, bound, i);
  return root;
}

Reconstruct Binary Search Tree With Postorder Traversal

Given the postorder traversal sequence of a binary search tree, reconstruct the original tree.

Assumptions

The given sequence is not null
There are no duplicate keys in the binary search tree
Examples

postorder traversal = {1, 4, 3, 11, 8, 5}

the corresponding binary search tree is

        5

      /    \

    3        8

  /   \        \

1      4        11

这两题对比看,上一题是 index 从 0 开始,然后往左边开始,左边需要一个 bound, 那就是 root 值,所以是最大值。

这题是 index 从后面开始,然后右边开始赋值,右边需要一个 bound, 就是 root 值作为它的最小值。

 public TreeNode reconstruct(int[] post) {
    int len = post.length;
    if (len == 0) {
      return null;
    }
    
    return helper(post, Integer.MIN_VALUE, new int[]{len - 1});
  }

  private TreeNode helper(int[] post, int min, int[] index) {
    if (index[0] < 0 || post[index[0]] <= min) {
      return null;
    }
    TreeNode root = new TreeNode(post[index[0]--]);
 

    root.right =  helper(post, root.key, index);
       root.left =  helper(post, min, index);

  return root;
  }

Reconstruct Binary Search Tree With Level Order Traversal

Given the levelorder traversal sequence of a binary search tree, reconstruct the original tree.

Assumptions

The given sequence is not null
There are no duplicate keys in the binary search tree
Examples

levelorder traversal = {5, 3, 8, 1, 4, 11}

the corresponding binary search tree is

        5

      /    \

    3        8

  /   \        \

1      4        11

public TreeNode reconstruct(int[] level) {   // Write your solution here   TreeNode root = null;   for (int i = 0; i < level.length; i++) {   root = helper(root, level[i]);   }   return root;   }   private TreeNode helper(TreeNode root, int data) {   if (root == null) return new TreeNode(data);   if (data <= root.key) root.left = helper(root.left, data);   if (data > root.key) root.right = helper(root.right, data);   return root;   }

其实就是forloop 里面嵌套bst的insert,insert in bst 有iteration 和 recursion 的解法。其中 recursion tree 画出来, 所有node的时间复杂度总和是 o n,n 是bst 的所有node 个数 空间是call stack 最深时所有node 空间复杂度总和,是o h。 所以我写的recursion 和你的 iteration ,时间复杂度一样,空间不用recursion更省啊 ~ An instructor (Paul) endorsed this answer  ~

想问一下老师这道题我做的时间复杂度分析,因为每次做一次循环都是从root节点出发去寻找能插进去的位置,是不是就是n*O(height), 然后worst case会不会就是一个糖葫芦串,然后就变成n^2了呢?

Valid Post-order Traversal Of Binary Search Tree

Given an array with integers, determine whether the array contains a valid postorder traversal sequence a BST.

Assumptions:

The given postorder traversal array is not null.
Examples:

For example, a1=[1, 3, 4, 2, 7, 6, 5] is a BST 

a2=[1, 3, 4, 6, 7, 2, 5] is not a BST.

Each item in left subtree a[0..i-1] is less than a[n-1]

Each item in right subtree a[i..n-2] is greater than a[n-1]

Both left subtree a[0..i-1] and right subtree a[i..n-2] are BSTs

So, for the given root a[n-1] we can find the start of a potential right subtree by scanning the array from left to right and check where the element is bigger than root. left side of this point is a left subtree. We also need to do sanity check whether the right subtree contains elements greater than root.

{1, 3, 4, 2, 7, 6, 5}

. 5 /
{1, 3, 4, 2} {7, 6} On / \
{1} {3, 4} {7} On / {3} On

runs in O(ngln) time in average case where the tree is a balanced binary tree and O(n) space. The worst case complexity however is O(n^2) in case of a tall binary tree such as a=[1,2,3,4,5].

我自己是分析 balanced binary tree 空间复杂度是 Oh,但是存疑

public boolean validPostOrder(int[] post) {
    if (post.length == 0) {
      return true;
    }
    return helper(post, 0, post.length - 1);
  }

  private boolean helper(int[] post, int left, int right) {
    if (left >= right) {
      return true;
    }
   
    int root = post[right];
    // find left subtree's root, traverse backwards
    int leftRoot = right - 1;
    while (leftRoot >= left) {
      if (post[leftRoot] < root) {
        break;
      }
      leftRoot--;
    }
    //traver left subtree, if there is an element > root, return false
    for (int i = left; i <= leftRoot; i++) {
      if (post[i] > root) {
        return false;
      }
    }
    return helper(post, left,  leftRoot) && helper(post, leftRoot + 1, right -1);
  }

用一个 stack, O(n)

其次, 如果是 pre-order of BST, 根据BST 左小右大,则pre order 一路向左下的过程是 decreasing ;那么 post -order ,反过来看则为一路右下为 increasing 。 

用 stack 维护 对pre-order  linear scan 先 left subtree 的 降序性 , 或者 post -order 反过来 先 right subtree 的 升序性, 从而实现对 given array 的一遍扫描, 时间是O(n) , 也可以处理为 online - algorithm 。 

public class Solution { public boolean validPostOrder(int[] post) { if (post == null || post.length <= 1) { return true; } return validPostOrder(post, post.length); // Write your solution here } private boolean validPostOrder(int[] post, int n) { // Creat a stack to maintain increasing order Deque stack = new ArrayDeque();

// Initialize current root as maximum possible
int root = Integer.MAX_VALUE;

// Traverse post array
for (int i = n - 1; i >= 0; i--) {
  // if we find a node who in left subtree and 
  // larger than root, return false;
  if (post[i] > root) {
    return false;
  }

  // If post[i] is in left subtree of stack top, 
  // Keeping removing items larger than post[i] 
  // and make the last removed item as the new 
  // root.
  while (!stack.isEmpty() && stack.peek() > post[i]) {
    root = stack.peek();
    stack.pop();
  }

  // At this pointer either stack is empty or 
  // post[i] is larger than root, which is 
  // increasing order in stack, push post[i]
  stack.push(post[i]);
}
return true;

} }

[1, 3, 4, 2, 7, 6, 5]

 int i = nums.length; // 7
        int max = Integer.MAX_VALUE;
        for (int j = nums.length - 1; j >= 0; j--)
        {
            if (nums[j] > max) return false; 
            while (i <= nums.length - 1 && nums[j] > nums[i])
                max = nums[i++];
            nums[--i] = nums[j];
        }
        return true;

LaiCode 461. Count of Range Sum https://app.laicode.io/app/problem/461

老师好,这道题我的第一想法是类似于max subarray equals to k 的解法,维持一个整体的sum然后把从0开始的partial sum存进map里。由于我们需要求一个范围,我打算用TreeMap,这样看下来TreeMap 的增查应该是O(lgn). 然后submap是O(1),这样看来整体的时间复杂度应该是O(nlogn). 但是由于我们要遍历从upper 到lower的所所有value,这样如果upper 和 lower差的比较大的话 可能最差情况还是O(n), 所以分析下来时间复杂度是O(n^2)对吗

public int countRangeSum(int[] nums, int lower, int upper) { if (nums == null || nums.length == 0) { return 0; } NavigableMap<Long, Integer> map = new TreeMap<>(); map.put(0L, 1); int result = 0; long sum = 0L; for (int i = 0; i < nums.length; i++) { sum += nums[i]; NavigableMap<Long, Integer> subMap = map.subMap(sum - upper, true, sum - lower, true); for (int count : subMap.values()) { result += count; } map.put(sum, map.getOrDefault(sum, 0) + 1); } return result; }

Count of Range Sum 372

  Given an integer array nums, return the number of range sums that lie in [lower, upper] inclusive.
Range sum S(i, j) is defined as the sum of the elements in nums between indices i and j (i ≤ j), inclusive.

Note:
A naive algorithm of O(n2) is trivial. You MUST do better than that.

Example:
Given nums = [-2, 5, -1], lower = -2, upper = 2,
Return 3.
The three ranges are : [0, 0], [2, 2], [0, 2] and their respective sums are: -2, -1, 2.