Algorithm ladder V

Topic: Binary Tree, BST, recursion/iteration.

// array to tree.

  • leetcode 109. Convert Sorted List to Binary Search Tree -- To do
      1. Convert Sorted Array to Binary Search Tree
  • LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal -- (好题!)
  • leetcode 297.binary-tree-serialization -- done 1
  • leetcode 449. Serialize and Deserialize BST -- done 1
  • Leetcode 331. Verify Preorder Serialization of a Binary Tree (好题)-- done 1

BFS

  • leetcode 102 Binary Tree Level Order Traversal

  • leetcode 107. Binary Tree Level Order Traversal II (除了与102一样的解法外还可以用dfs); -- TO DO

  • leetcode 116. Populating Next Right Pointers in Each Node

  • leetcode 116 Populating Next Right Pointers in Each Node -- To Do 分治法做最简单。

  • leetcode 285. Inorder Successor in BST

leetcode 102 Binary Tree Level Order Traversal

image.png

要点: (1) queue保证一层traverse完了再traverse下一层。 (2) 额外参数N用来分割layer

class Solution {
    private int N = 0; // number of nodes in current layer
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if (root == null) return res;
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        N = 1;
        List<Integer> layer = new ArrayList<Integer>();
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            layer.add(cur.val);
            N--;
            
            if (cur.left != null) queue.offer(cur.left);
            if (cur.right != null) queue.offer(cur.right);
            
            if (N == 0) {
                res.add(new ArrayList<Integer>(layer));
                layer.clear();
                N = queue.size();
            }
        }
        return res;
    }
}

leetcode 116. Populating Next Right Pointers in Each Node

public class Solution {
    public void connect(TreeLinkNode root) {
        if(root == null || root.left == null) return;
        connectNodes(root.left, root.right);
    }
    
    public void connectNodes(TreeLinkNode node1, TreeLinkNode node2) {
        node1.next = node2;
        if(node1.left != null) {
            connectNodes(node1.right, node2.left);
            connectNodes(node1.left, node1.right);
            connectNodes(node2.left, node2.right);
        }
    } 
}

leetcode 102 Binary Tree Level Order Traversal

image.png
public class BinaryTreeLevelOrderTraversal {
    
    private int N = 0; // number of nodes in current layer
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if (root == null) return res;
        
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        N = 1;
        List<Integer> layer = new ArrayList<Integer>();
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            layer.add(cur.val);
            N--;
            
            if (cur.left != null) queue.offer(cur.left);
            if (cur.right != null) queue.offer(cur.right);
            
            if (N == 0) {
                res.add(new ArrayList<Integer>(layer));
                layer.clear();
                N = queue.size();
            }
        }
        return res;
    }
}

leetcode 297. Serialize and Deserialize Binary Tree

image.png

非递归的解法:http://codenuggets.com/2016/07/16/binary-tree-serialization
非递归遍历(使用queue -- 注意此处是bfs遍历!preorder是用stack)
反序列化:仍然维护一个队列,进队时创建节点,出队时从String array里面拿两个(if possible)出来。<-> 分析:考虑bfs遍历时,每次出队列都会把2个节点(左右children)加入队列,

递归解法很好用

    1. 使用了preorder traversal的序列
    1. 反序列化较难。这里用了一个queue,使得递归可以顺利实现。
    • 2.1 除了queue,可以直接操作String array(需要一个额外的field来给array计数)
    • 2.2另外可以用StringTokenizer
public class SerializeAndDeserializeBinaryTree {
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
        StringBuilder res = new StringBuilder();
        serializeHelper(root, res);
        return res.toString();
    }
    
    /*  
     * Result is: 1,4,#,#,2,3,#,#,#  <-> 1 + ,4  + ,# + ,# + ,2 + ,3 + ,# + ,# + ,#
     * Notice that if comma is put after integer then there is no way to eliminate the last comma.
     */
    private void serializeHelper(TreeNode root, StringBuilder res) {
    
            if (res.length() > 0) res.append(","); // the root has not comma ahead.
            
            if (root == null) {  
                res.append("#");
                return;
                }
            
            res.append(root.val);
            serializeHelper(root.left, res);
            serializeHelper(root.right, res);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
            String[] vals = data.split(",");
            System.out.println(vals.length);
            LinkedList<String> queue = new LinkedList<String>();
            for (String s : vals) 
                queue.offer(s);
                
            return deserializeHelper(queue);
    }
    
    private TreeNode deserializeHelper( LinkedList<String> queue) {
            if (queue.isEmpty()) return null;
            
            String val = queue.poll();
            if (val.equals("#")) return null;
            
            // else val is an integer
            TreeNode root = new TreeNode(Integer.parseInt(val));
            root.left = deserializeHelper(queue);
            root.right = deserializeHelper(queue);
            
            return root;
    }
}

leetcode 449. Serialize and Deserialize BST

image.png

与 297(Serialize and Deserialize BT)的区别在于:
BST的特性可以让序列化更compacti.e. 不需要“#”来表示null.

public class SerializeAndDeserializeBST {
    // Encodes a tree to a single string.
    public String serialize(TreeNode root) {
            // 用前序遍历来做序列化。string中不包含null
        StringBuilder res = new StringBuilder();
        serializeHelper(root, res);
        return res.toString();
    }
    
    private void serializeHelper(TreeNode root, StringBuilder res) {
            if (root == null) return;
            if (res.length() != 0) res.append(",");
            res.append(root.val);
            serializeHelper(root.left, res);
            serializeHelper(root.right, res);
    }

    // Decodes your encoded data to tree.
    public TreeNode deserialize(String data) {
            // 注意这个corner case
            if (data.equals("")) return null;
            
            String[] vals_string = data.split(",");
            int[] vals = new int[vals_string.length]; 
            for (int i = 0; i< vals_string.length; i++) {
                vals[i] = Integer.parseInt(vals_string[i]);
            }
        return deserializeHelper(vals, 0, vals.length-1);
    }
    
    private TreeNode deserializeHelper(int[] vals, int start, int end) {
            if (start > end) return null;
            
            int rootVal = vals[start];
            TreeNode root = new TreeNode(rootVal);
            
            // find the FIRST value that larger than rootVal -- index is rightStart
            // applies to start == end as well
            int rightStart, i = start + 1;
            while (i <= end) {
                if (vals[i] > rootVal) break;
                I++;
            }
            rightStart = I; // 如果没有找到的话, rightStart = i = end+1 

            root.right = deserializeHelper(vals, rightStart, end);
            root.left = deserializeHelper(vals, start+1, rightStart-1);
            return root;
    }

解析: 递归反序列化
如下bst的preorder traversal是2,1,4,3
----2
--1 --- 4
-----3
2必然是root节点,下一步要找到第一个比2大的数(也就是4).一旦找到:
1)则[1]构成了root的左子树 (需要反序列化)
2)[4,3]构成了root的右子树(也需要反序列化)
从而将原问题转化成递归求解子问题。

除了这种递归解法,geekforgeek还有一种非递归的解法,很巧妙地用到了stack。可以参考。


image.png

Leetcode 331. Verify Preorder Serialization of a Binary Tree (好题)

image.png

这题和Leetcode 297(Serialize and Deserialize BT)非常相似
要求是不重构树

public class VerifyPreorderSerializationOfBinaryTree {
    public boolean isValidSerialization(String preorder) {
        if (preorder.equals("")) return false; // corner case
        
        String[] tokens = preorder.split(",");
        LinkedList<String> queue = new LinkedList<String>();
        queue.addAll(Arrays.asList(tokens));
        
        return checkSerialization(queue) && queue.isEmpty();
    }
    
    private boolean checkSerialization(LinkedList<String> queue) {
        if (queue.isEmpty()) return false;
        
        String val = queue.poll();
        if (val.equals("#")) 
            return true;
        // else integer
        return checkSerialization(queue) && checkSerialization(queue); // check left and right;
    }
}

解析:注意到这题实际给出的序列化就是preorder serialization,所以思路和297的deserialization是完全一样的。
要点:用queue每次pop出一个string;
重构失败只会因为两种情况:

  1. 该有next string的时候没有了,i.e. "1,#" -- 此时会在checkSerialization中出现queue为空。
  2. 不该有next string的时候仍有next string, i.e., 在isValidSerialization中要检查调用checkSerialization完毕后queue已经全部排空。

285. Inorder Successor in BST

image.png
    // 参考range的解法 
    public TreeNode successor(TreeNode root, int target) {
        if (root == null) return null;
        
        if (target < root.val)  { //a. 左子树中没有更小的话那就是root啦!
            TreeNode temp = successor(root.left, target);
            return temp == null ? root : temp;
            }
        else if (target > root.val) { //b. 显然要在右子树中找。
            return successor(root.right, target);
        } else { //c.  target == root.val 走到这一步说明上面(a步暂时)还没找到
            // 右子树中有的话就是结果,没有的话对上层(a)判断也有用。
            return successor(root.right, target);
        } 
    }

解析: 这题是道非常经典的BST递归解法题。
要点: 整个BST的操作#全部# 都可以用递归简洁地完成!
下面列了一些(Robert Sedgewick)常见的BST操作(floor和ceiling跟这个successor很相似)

// recursive solution
public class SearchInBST {
    // 左子树找不到就在右子树找。
    public TreeNode search(TreeNode root, int target) {
        if (root == null) return null;
        if (root.val == target) return root;
        else if (root.val < target)
            return search(root.right, target);
        else 
            return search(root.left, target);
    }
    
    public Iterable<TreeNode> range(TreeNode root, int lo, int hi) {
        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();
        rangeHelper(root, queue, lo, hi);
        return queue;
    }
    private void rangeHelper(TreeNode node, LinkedList<TreeNode> queue, int lo, int hi) {
        if (node == null) return;
        if (lo < node.val) rangeHelper(node.left, queue, lo, hi);
        if (lo <= node.val && node.val <= hi) queue.offer(node);
        if (node.val < hi) rangeHelper(node.right, queue, lo, hi);
    }
    
    // 参考range的解法
    public TreeNode successor(TreeNode root, int target) {
        if (root == null) return null;
        
        if (target < root.val)  { //a. 左子树中没有更小的话那就是root啦!
            TreeNode temp = successor(root.left, target);
            return temp == null ? root : temp;
            }
        else if (target > root.val) { //b. 显然要在右子树中找。
            return successor(root.right, target);
        } else { //c.  target == root.val 走到这一步说明上面(a步暂时)还没找到
            // 右子树中有的话就是结果,没有的话对上层(a)判断也有用。
            return successor(root.right, target);
        } 

    }
    
    public TreeNode min(TreeNode root) {
        if (root.left == null) return root;
        else return min(root.left);
    }
    
    public TreeNode max(TreeNode root) {
        if (root.right == null) return root;
        else return max(root.right);
    }
}

注: 这里包含了 searchrangesuccessorminmax
注意min/max 方法中: 左/右子树不存在再化为子问题。

109 Convert Sorted List to Binary Search Tree (medium)

108 Convert Sorted Array to Binary Search Tree (easy)

两题很相似。array好做因为access array element简单。只用mid = lo + (hi-lo)/2 就可以得到中间数
LinkList可以用双指针* 来求区间中点!

image.png

public class ConvertSortedListToBST {
     public TreeNode sortedListToBST(ListNode head) {
         if (head == null) return null;
         
         return toBST(head, null); 
     }
     
     private TreeNode toBST(ListNode head, ListNode tail) {
         if (head == tail) return null;
         
         ListNode slow = head;
         ListNode fast = head;
         
         // 这个双指针(在list中)找中点 太经典了!!!
         //注意是 fast != tail 不是 != null.
         while (fast != tail && fast.next != tail) {
             slow = slow.next;
             fast = fast.next.next;
         }
         TreeNode root = new TreeNode(slow.val);
         root.left = toBST(head, slow);
         root.right = toBST(slow.next, tail);
         return root;
     }
}

解释: complexity O(nlogn) -- n 层迭代,每层需要指针遍历O(n)

    public TreeNode sortedArrayToBST(int[] A) {
            if (A == null) return null;
            
            return buildBST(A, 0, A.length-1);
    }
    
    private TreeNode buildBST(int[] A, int start, int end) {
            if (start > end) return null;
            
            int mid = start + (end - start) / 2;
            TreeNode root = new TreeNode(A[mid]);
            root.left = buildBST(A, start, mid-1);
            root.right = buildBST(A, mid+1, end);
            
            return root;
    }

note:上面是108的解答。

LeetCode 106. Construct Binary Tree from Inorder and Postorder Traversal

这道题有道原型体:Construct Tree from given Inorder and Preorder traversals https://www.geeksforgeeks.org/construct-tree-from-given-inorder-and-preorder-traversal/

image.png

这题真是recursion的经典题。
要点:需要意识到preorder怎么使用。
inorder的构造是left -> current -> right
buildTree时必然要先找到current val然后构造current(所有node全部都是这么构造的)。之后再递归构造left 和 right
=====> 这样的构造node的顺序是 current->left->right正好是preorder traversal.所以preorder array是完全按照顺利来遍历的。只需要维护一个field,让递归时也能access到正确的node就可以了。

public class BinaryTreeConstructionInorderPreorderTraversal {
    
    private int preIndex;
    public TreeNode buildTree(int[] inorder, int[] preorder) {
        preIndex = 0;
        return buildTree(inorder, preorder, 0, inorder.length-1);
    }
    
    
    private TreeNode buildTree(int[] inorder, int[] preorder, int inStart, int inEnd) {
        if (inStart > inEnd) return null;
        
        TreeNode root = new TreeNode(preorder[preIndex++]);
        int curInIndex = inIndex(inorder, preorder[preIndex-1], inStart, inEnd);
        // 一定要先递归构造左子树, 才能让preIndex正常++
        root.left = buildTree(inorder, preorder, inStart, curInIndex-1);
        root.right = buildTree(inorder, preorder, curInIndex+1, inEnd);
        return root;
    }
    
    
    // 假设target是存在的
    public int inIndex(int[] inorder, int target, int inStart, int inEnd) {
        int i = 0;
        for (i = inStart; i <= inEnd; i++) {
            if (inorder[i] == target) 
                break;
        }
        return i; //不考虑越界问题。
    }
}

解析,考虑树: root = node(1); root.left = node(2); root.left.right = node(3); root.right = node(4);
inorder traversal: 2 3 左 | 1 |4 右
preorder traversal: 1 | 2 3 左 | 4右
postorder traversal 3 2 左 | 4 右|1

  1. 无论pre还是post都是必须的,因为可以直接通过首/尾元素构造root节点(此处为1)。
  2. 再inorder 找到首/尾元素就可以把inorder分成两个子序列[inStart, inIndex-1] [inIndex+1, inEnd] 分别对应root的左右子树。
  3. 仔细想,这题跟其他重建BT的题(i.e. leetcode 297) 也有点像,每次只重建#一个#node!然后递归重建该节点的左右children。正因为如此我们可以意识到对preorder/postorder元素的遍历是一个一个地进行的。
    • preorder的遍历是root->左子树->右子树, 所以递归时先buildTree左子树,再右子树。
    • postorder的遍历(array倒叙~)是root->右子树->左子树,所以递归时先buildTree右子树,再左子树
image.png
public class BinaryTreeConstructionInorderPostorderTraversal {
    private int curPostIndex;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        curPostIndex = postorder.length-1;
        return buildTree(inorder, postorder, 0, inorder.length-1);
    }
    
    /*
     * 这题跟其他重建BT的题也有点像,每次只重建一个node!然后递归重建该节点的左右children
     */
    private TreeNode buildTree(int[] in, int[] post, int inStart, int inEnd) {
        if (inStart > inEnd) return null;
        
        TreeNode root = new TreeNode(post[curPostIndex--]); 
        int curInIndex = inIndex(in, post[curPostIndex+1], inStart, inEnd);
        //一定要先right tree!!!!
        root.right = buildTree(in, post, curInIndex+1, inEnd);
        root.left = buildTree(in, post, inStart, curInIndex-1);
        return root;
    }
        
    // assume target always present in array
    private int inIndex(int[] in, int target, int inStart, int inEnd) {
        int i = 0;
        for (i = inStart; i <= inEnd; i++) {
            if (in[i] == target)
                break;
        }
        return I;
    }
}

解析上面已经分析过了,昨晚pre+inorder, post+inorder应该就迎刃而解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容