22nd Feb: 刷题笔记 Binary Tree & Binary Search Tree 专题

今天早上起来收到了BB的拒信。没想到题都答对了,却还是挂掉面试。痛定思痛,下次电面需要注意以下的几点:


1/ 说话要咬字发音清楚,不卑不亢,不要太过于紧张,给人一种你很傻的感觉;

2/ 你可以思考,思考的时候最好先说:Let me think for a moment. 

3/ 复杂度分析的时候,如果不明显的题目,思考最差的情况的复杂度


其它的就是运气的问题了,去TM的烙印面试官,极其不负责任,迟到还问些那么弱智的问题,问完还挂。这种垃圾都能混进去BB。


闲话不说,进入今天的专题,Binary Search Tree。二叉搜索树具有的特殊性质:当前节点的左子树的所有节点的值都是小于等于当前节点。右子树的所有节点的值都是大于等于当前节点。


满二叉数和完全二叉树,见这篇文章:http://blog.csdn.net/firehotest/article/details/52695399


需要记住的性质是:如果一颗二叉树的高度是h,那么其最多(满二叉树)的时候有:2^h - 1个节点。


题目:


222. Count Complete Tree Nodes


Given a complete binary tree, count the number of nodes.


Definition of a complete binary tree from Wikipedia:
In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.


Thinking: 


We should use the feature of full binary tree. If a full binary tree’s height is h, then there are 2^(h) - 1 nodes in this tree. Then this problem can divide into two sub situations. 


If the most left height is equal to the right height, it is a full binary tree. We can use the 2^(h) - 1 to return the height. Otherwise, recursively call the same function for left sub tree and right sub tree. 


Code: 


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    private int leftHeight(TreeNode root) {
        int count = 1;
        
        while (root.left != null) {
            count++;
            root = root.left;
        }
        
        return count;
    }
    
    private int rightHeight(TreeNode root) {
        int count = 1;
        
        while (root.right != null) {
            count++;
            root = root.right;
        }
        
        return count;
    }
    
    public int countNodes(TreeNode root) {
        if (root == null) {
            return 0;
        }
        
        int height = 0;
        
        if ((height = leftHeight(root)) == rightHeight(root)) {// full binary tree
            return (1 << height) - 1;
        } else {
            return 1 + countNodes(root.left) + countNodes(root.right);
        }
    }
}


Feedback: Math.pow(x, n)返回的是double,而且慢。如果想表示2^n可以直接1 << n即可。1 << n等于 2^0 * 2^n = 2^n. 


复杂度分析:O(logn). 


------------------------------------------------Binary Search Tree专题------------------------------------------------


270. Closest Binary Search Tree Value


Given a non-empty binary search tree and a target value, find the value in the BST that is closest to the target.


Note:
Given target value is a floating point.
You are guaranteed to have only one unique value in the BST that is closest to the target.


Thinking: Binary Search Tree. Therefore, we can use recursive mean or non-recursive mean to do this. 


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public int closestValue(TreeNode root, double target) {
        int closeVal = root.val;
        double distance = Math.abs(root.val - target);
        
        while (root != null) {
            double tmpDistance = Math.abs(root.val - target);
            if (tmpDistance < distance) {
                closeVal = root.val;
                distance = tmpDistance;
            }
            if (root.val <= target) {
                root = root.right;
            } else {
                root = root.left;
            }
        }
        
        return closeVal;
    }
}

Feedback:这题需要返回最近的节点值。由于给的是double的target,所以你必须设两个记录变量:目前最近的节点Int值以及最小的距离值。


复杂度分析:O(logn),n是二叉树的节点数。


94. Binary Tree Inorder Traversal


Given a binary tree, return the inorder traversal of its nodes' values.


For example:
Given binary tree [1,null,2,3],
   1
    \
     2
    /
   3
return [1,3,2].


Note: Recursive solution is trivial, could you do it iteratively?


Thinking: 

基本功题目,熟悉递归和非递归的写法。


先上个递归的写法 (用分治):


/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        if (root == null) {
            return new ArrayList<Integer>();
        }
        
        List<Integer> result = new ArrayList<Integer>();
        
        List<Integer> left = inorderTraversal(root.left);
        List<Integer> right = inorderTraversal(root.right);
        
        result.addAll(left);
        result.add(root.val);
        result.addAll(right);
        
        return result;
    }
}

再写一个非递归的方法(用栈):


注意中序遍历的递归模板和前序是不同的。前序的是到哪里就把当前点放进去结果。接着根据栈的顺序,先放右孩子,再放左孩子。然后从栈当中取头继续。


但是中序遍历就是先把所有的左边孩子都放进栈当中,然后拿一个出来加个结果中,然后将当前节点变为右孩子,接着右孩子继续当做新的根重复加左孩子的过程。


代码如下:


public class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<Integer>();
        LinkedList<TreeNode> stack = new LinkedList<TreeNode>();
        TreeNode tmp = root;
        
        while (tmp != null || !(stack.isEmpty())) {//注意这里是或不是与
            while (tmp != null) {
                stack.addFirst(tmp);
                tmp = tmp.left;
            }
            
            tmp = stack.removeFirst();
            result.add(tmp.val);
            tmp = tmp.right;
        }
        
        return result;
    }
}


285. Inorder Successor in BST


Given a binary search tree and a node in it, find the in-order successor of that node in the BST.


Note: If the given node has no in-order successor in the tree, return null.


Thinking: In this problem, we can take advantage of the feature of BST. The successor of inorder traversal must be the value bigger than this node. Therefore, if current.val < target.val,we can directly go to the right sub tree. However, if the current.val > target.val, current value is likely to be value of answer. Therefore, we should save current node and then to see if its left child equal to target. If it is yes, then return the save node. 


public class Solution {
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        if (root == null) {
            return null;
        }
        
        TreeNode tmp = root;
        TreeNode store = null;
        
        while (tmp != null) {
            if (tmp.val > p.val) {
                store = tmp;
                tmp = tmp.left;
            } else {
                tmp = tmp.right;
            }
        }
        
        return store;
    }
}

501. Find Mode in Binary Search Tree


Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in the given BST.


Assume a BST is defined as follows:


The left subtree of a node contains only nodes with keys less than or equal to the node's key.


The right subtree of a node contains only nodes with keys greater than or equal to the node's key.


Both the left and right subtrees must also be binary search trees.


For example:
Given BST [1,null,2,2],
   1
    \
     2
    /
   2
return [2].


Note: If a tree has more than one mode, you can return them in any order. 

Thinking: One is to use the hash map. Which has the O(n) Space Complexity. The other one is to use the traversal recursion with a List in the parameter. 


用O(1)实现的方法:Take advantage of the feature of Binary Searach Tree. A mode number will appear in the consequence of inorder traversal. Therefore, we can use only one variable named prev to record each number's occurrence.  


public class Solution {
    private Integer prev = null;
    private int max = 0;
    private int count = 1;
    
    public int[] findMode(TreeNode root) {
        List<Integer> ans = new ArrayList<Integer>();
        
        traversal(root, ans);
        
        int[] res = new int[ans.size()];
        
        for (int i = 0; i < res.length; i++) {
            res[i] = ans.get(i);
        }
        
        return res;
    }
    
    private void traversal(TreeNode root, List<Integer>ans) {
        if (root == null) {
            return;
        }
        
        traversal(root.left, ans);
        
        if (prev != null) {
            if (prev == root.val) {
                count ++;
            } else {
                count = 1;
            }
        } 
        if (count > max) {
            max = count;
            ans.clear();
            ans.add(root.val);
        } else if (count == max) {
            ans.add(root.val);
        }
        
        prev = root.val;
        
        traversal(root.right, ans);
    }
}


230. Kth Smallest Element in a BST


Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.


Note: 
You may assume k is always valid, 1 ≤ k ≤ BST's total elements.


Follow up:
What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?


Hint:


Try to utilize the property of a BST.
What if you could modify the BST node's structure?
The optimal runtime complexity is O(height of BST).


Thinking: There are two ways to do this problem. 


The easiest one is to traversal this BST with a counting number. 


The harder but preferer one is the one whose runtime complexity is O(height of BST). It utilize the property that all the nodes' value of left subtree always smaller than their root and right subtree. Therefore, we can count the number of nodes which are at the left of root. If the number is smaller than K, then we should find the Kst Smallest element from the right subtree. Otherwise, we should find it from the left subtree. This recursion should stop when the number of left subtree is equal to k - 1. (kst one is the current root). 


public class Solution {
    private int countNodes(TreeNode root) {
        if (root == null) {
            return 0;
        }
        
        return 1 + countNodes(root.left) + countNodes(root.right);
    }
    
    public int kthSmallest(TreeNode root, int k) {
        if (countNodes(root) < k) {
            return -1;
        }
        
        int leftCounts = 0;
        
        if ((leftCounts = countNodes(root.left)) < k - 1) {
            return kthSmallest(root.right, k - 1 - leftCounts);
        } else if (leftCounts > k - 1){
            return kthSmallest(root.left, k);
        }
        
        return root.val;
    }
}


173. Binary Search Tree Iterator


Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST.


Calling next() will return the next smallest number in the BST.


Note: next() and hasNext() should run in average O(1) time and uses O(h) memory, where h is the height of the tree.

Thinking: This problem is about inorder traversal of Binary Search Tree. Nothing special. 


public class BSTIterator {
    private LinkedList<TreeNode> stack;
    
    public BSTIterator(TreeNode root) {
        stack = new LinkedList<TreeNode>();
        pushAll(root);
    }

    /** @return whether we have a next smallest number */
    public boolean hasNext() {
        return !(stack.isEmpty());
    }

    /** @return the next smallest number */
    public int next() {
        TreeNode tmp = stack.removeFirst();
        pushAll(tmp.right);
        return tmp.val;
    }
    
    private void pushAll(TreeNode root) {
        while (root != null) {
            stack.addFirst(root);
            root = root.left;
        }
    }
}


98. Validate Binary Search Tree


Given a binary tree, determine if it is a valid binary search tree (BST).


Assume a BST is defined as follows:


The left subtree of a node contains only nodes with keys less than the node's key.
The right subtree of a node contains only nodes with keys greater than the node's key.
Both the left and right subtrees must also be binary search trees.
Example 1:
    2
   / \
  1   3
Binary tree [2,1,3], return true.
Example 2:
    1
   / \
  2   3
Binary tree [1,2,3], return false.


Thinking: The same question as bloomberg. The most frequently mistake is using the three criteria above directly in the recursion. However, we need to consider whether the child’s child’s value is smaller than current root. 


If we use the way state above, this code will be extremely complex. Therefore, we should think in term of child. Every time, I arrive one node, I compare myself with the biggest value I should be bigger than and the smallest value I shouldn’t be smaller than. 


This will be more easy. The tricky part of this question is how about the left child of right subtree's root. I need to confirm that it is bigger than current root. This can be made sure by passing the min and max values. 


public class Solution {
    private boolean lookFromCur(TreeNode cur, int max, int min) {
        if (cur == null) {
            return true;
        }
        
        //look from the current node, I should not bigger than max or smaller than min
        if (cur.val >= max || cur.val <= min) {
            return false;
        }
        return (lookFromCur(cur.left, cur.val, min) && lookFromCur(cur.right, max, cur.val));
    }
    
    public boolean isValidBST(TreeNode root) {
        return lookFromCur(root, Integer.MAX_VALUE, Integer.MIN_VALUE);
    }
}

Feedback: Don't forget to pass the max and min values. 


O(n). 




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值