Showing posts with label Tree - Post-Order. Show all posts
Showing posts with label Tree - Post-Order. Show all posts

LeetCode 427 - Construct Quad Tree


https://leetcode.com/problems/construct-quad-tree/
We want to use quad trees to store an N x N boolean grid. Each cell in the grid can only be true or false. The root node represents the whole grid. For each node, it will be subdivided into four children nodes until the values in the region it represents are all the same.
Each node has another two boolean attributes : isLeaf and valisLeaf is true if and only if the node is a leaf node. The val attribute for a leaf node contains the value of the region it represents.
Your task is to use a quad tree to represent a given grid. The following example may help you understand the problem better:
Given the 8 x 8 grid below, we want to construct the corresponding quad tree:
It can be divided according to the definition above:

The corresponding quad tree should be as following, where each node is represented as a (isLeaf, val) pair.
For the non-leaf nodes, val can be arbitrary, so it is represented as *.
Note:
  1. N is less than 1000 and guaranteened to be a power of 2.
  2. If you want to know more about the quad tree, you can refer to its wiki.
X. Post order: O(N)
https://leetcode.com/problems/construct-quad-tree/discuss/154565/Java-recursive-solution
    public Node construct(int[][] grid) {
        return helper(grid, 0, 0, grid.length);
    }
    private Node helper(int[][] grid, int x, int y, int len) {
        if (len == 1) {
            return new Node(grid[x][y] != 0, true, null, null, null, null);
        }
        Node result = new Node();
        Node topLeft = helper(grid, x, y, len / 2);
        Node topRight = helper(grid, x, y + len / 2, len / 2);
        Node bottomLeft = helper(grid, x + len / 2, y, len / 2);
        Node bottomRight = helper(grid, x + len / 2, y + len / 2, len / 2);
        if (topLeft.isLeaf && topRight.isLeaf && bottomLeft.isLeaf && bottomRight.isLeaf
           && topLeft.val == topRight.val && topRight.val == bottomLeft.val && bottomLeft.val == bottomRight.val) {
            result.isLeaf = true;
            result.val = topLeft.val;
        } else {
            result.topLeft = topLeft;
            result.topRight = topRight;
            result.bottomLeft = bottomLeft;
            result.bottomRight = bottomRight;
        }
        return result;
    }
https://leetcode.com/problems/construct-quad-tree/discuss/170983/Easy-to-understand-Java-Recursive-Solution
  1. To do this recusively, we have to split the grid into 4 smaller sub-grids until the sub-grid's length is 1. The sub-grid whose length is 1 is the leaf node.
  2. We merge the sub-grids if all four sub-grids are leaf nodes and have same value.
class Solution {
    public Node construct(int[][] grid) {
        return helper(grid, 0, 0, grid.length);
    }
    private Node helper(int[][] grid, int x, int y, int len) {
        if(len == 1) return new Node(grid[x][y] == 1, true, null, null, null, null);
        
        Node nodeTL = helper(grid, x, y, len/2);
        Node nodeTR = helper(grid, x, y+len/2, len/2);
        Node nodeBL = helper(grid, x+len/2, y, len/2);
        Node nodeBR = helper(grid, x+len/2, y+len/2, len/2);
        // Merge all child nodes
        if(nodeTL.isLeaf && nodeTR.isLeaf && nodeBL.isLeaf && nodeBR.isLeaf) {
            if(nodeTL.val && nodeTR.val && nodeBL.val && nodeBR.val) return new Node(true, true, null, null, null, null);
            if(!nodeTL.val && !nodeTR.val && !nodeBL.val && !nodeBR.val)  return new Node(false, true, null, null, null, null);
        }
        return new Node(true, false, nodeTL, nodeTR, nodeBL, nodeBR);
    }
}
Time Complexity: O(N^2 logN)N is the length of the grid.
Space Complexity: O(N^2)

Why time complexity is O(N^2 logN). Shouldn't it be O(n) , since recursion relation is T(n) = 4*T(n/2) . n is length of the grid.

X.
https://zxi.mytechroad.com/blog/geometry/leetcode-427-construct-quad-tree/
Time complexity: O(n^2*logn)

Space complexity: O(n^2)

http://www.cnblogs.com/grandyang/p/9649348.html
这道题让我们根据一个二维数组来建立一棵四叉树,关于四叉树的介绍题目中也了给了wiki百科的链接。但是博主开始看到题目中给的那个例子,没怎么看懂。后来分析大神们的代码,才略微弄明白了一些。原来叶结点表示的是值相同的一片区域,比如我们看二维数组图示那行的第三个图,首先整个数组被分成了四等份,左上,左下,和右下部分内的值均相同,那么他们都是一个叶结点,而右上只有再四等分一下,才能使各自部分内的值相同,所以其就不是叶结点,而四等分后的每个区间才是叶结点。题目中限定了N的值一定是2的指数,就是说其如果可分的话,一定可以四等分,而之前说了,只有区间内的值不同时,才需要四等分,否则整体就当作一个叶结点。所以我们需要check四等分区间内的值是否相同,当然,我们可以将二维数组拆分为四个二维数组,但是那样可能不太高效,而且还占用额外空间,一个比较好的选择是用坐标变量来控制等分数组的范围,我们只需要一个起始点坐标,和区间的长度,就可以精确定位一个区间了。比如说对于例子中的整个二维数组数组来说,知道起始点坐标 (0, 0),还有长度8,就知道表示的是哪个区间。我们可以遍历这个区间上的其他所有的点,跟起点对比,只要有任何点跟起点不相同,则说明该区间是可分的,因为我们前面说了,只有一个区间上所有的值均相同,才能当作一个叶结点。只要有不同,就表示可以四分,那么我们就新建一个结点,这里的左上,左下,右上,和右下四个子结点就需要用过调用递归函数来实现了,实现原理都一样,重要的地方就是确定每个四分区间的起点和长度,长度好确定,都是当前长度的一半,然后就是把各个区间的起点确定好,这个也不难,就是细心一点,不要写错了就可以了,另外,对于非叶结点,结点值可以是true或者false都没问题。如果某个区间上所有值均相同,那么就生成一个叶结点,结点值就跟区间值相同,isLeaf是true,四个子结点均为NULL即可

    Node* construct(vector<vector<int>>& grid) {
        return build(grid, 0, 0, grid.size());
    }
    Node* build(vector<vector<int>>& grid, int x, int y, int len) {
        if (len <= 0) return NULL;
        for (int i = x; i < x + len; ++i) {
            for (int j = y; j < y + len; ++j) {
                if (grid[i][j] != grid[x][y]) {
                    return new Node(true, false,
                           build(grid, x, y, len / 2),
                           build(grid, x, y + len / 2, len / 2),
                           build(grid, x + len/ 2, y, len / 2),
                           build(grid, x + len / 2, y + len / 2, len / 2));
                }
            }
        }
        return new Node(grid[x][y] == 1, true, NULL, NULL, NULL, NULL);
    }


还有一种写法,记录了区间的左上点和右下点,知道这两个点也可以确定一个区间的位置,整体思路和上面的方法并没有什么太大的区别
    Node* construct(vector<vector<int>>& grid) {
        return build(grid, 0, 0, grid.size() - 1, grid.size() - 1);
    }
    Node* build(vector<vector<int>>& grid, int r1, int c1, int r2, int c2) {
        if (r1 > r2 || c1 > c2) return NULL;
        bool isLeaf = true;
        int val = grid[r1][c1], rowMid = (r1 + r2) / 2, colMid = (c1 + c2) / 2;
        for (int i = r1; i <= r2; ++i) {
            for (int j = c1; j <= c2; ++j) {
                if (grid[i][j] != val) {
                    isLeaf = false;
                    break;
                }
            }
        }
        if (isLeaf) return new Node(val == 1, true, NULL, NULL, NULL, NULL);
        return new Node(false, false, 
               build(grid, r1, c1, rowMid, colMid),
               build(grid, r1, colMid + 1, rowMid, c2),
               build(grid, rowMid + 1, c1, r2, colMid),
               build(grid, rowMid + 1, colMid + 1, r2, c2));
    }
    public Node construct(int[][] g) { return build(0, 0, g.length - 1, g.length - 1, g);}

    Node build(int r1, int c1, int r2, int c2, int[][] g) {
        if (r1 > r2 || c1 > c2) return null;
        boolean isLeaf = true;
        int val = g[r1][c1];
        for (int i = r1; i <= r2; i++)
            for (int j = c1; j <= c2; j++)
                if (g[i][j] != val) {
                    isLeaf = false;
                    break;
                }
        if (isLeaf)
            return new Node(val == 1, true, null, null, null, null);
        int rowMid = (r1 + r2) / 2, colMid = (c1 + c2) / 2;
        return new Node(false, false,
            build(r1, c1, rowMid, colMid, g),//top left 
            build(r1, colMid + 1, rowMid, c2, g),//top right
            build(rowMid + 1, c1, r2, colMid, g),//bottom left 
            build(rowMid + 1, colMid + 1, r2, c2, g));//bottom right
    }

还原黑白图片

给一个只有黑白pixel的图片,调用给定的两个method,把图片重新画出来。两个method分别是:int readSource(int x1, int y1, int x2, int y2)和void writeTarget(int x1, int y1, int x2, int y2);
补充
题目意思大概是要利用第一个method来判断坐标区域内是否为全黑全白或者都有,三种情况会分别返回1,-1,0。第二个method是在坐标区域内画图,但是画出来的图只有黑色。
·LC类似题目:427  construct quad tree

思路:
  1. dfs解法,先检查当前square是否全黑全白,如果全黑,涂黑当前区域,返回,如果全白,返回,如果有黑有白,按照construct quad tree的的思路分成4块递归


https://blog.csdn.net/dpengwang/article/details/84785232
另一道与四分树有关的题
题目 558. Quad Tree Intersection
求两个四分树的OR操作,这道题的解决思想也是递归,不过是两棵树同时递归,每次递归,传入的父节点的位置得是对应的,即他们所表示的区域是相同的。这道题还有要注意的一点是,如果合并以后父节点的子节点的值都是相同的,得将这个父节点转为叶子节点(即删除所有子节点,并把isleaf 设置为True)


Verify Post-order Sequence Of BST


http://www.zrzahid.com/check-postordered-array-forms-a-bst/
Given an array that contains a Post order traversal of a Binary Tree. Check if a possible Binary Tree formed from the postorder traversal is a Binary Search Tree.
For example, a1=[1, 3, 4, 2, 7, 6, 5] is a BST but a2=[1, 3, 4, 6, 7, 2, 5] is not a BST.
           5                              5 
          / \                           /   \
        2     6                       4       2
      /   \    \                     /         \ 
    1      4    7                   3           7
          /                        /           /
        3                         1           6

        (a1)                            (a2)
In order to be a BST, a binary tree should satisfy that any subtree rooted at node r is greater than all nodes in its left subtree and smaller than nodes in its right subtree. Now, in post order traversal we traverse left subtree first then the right subtree and at last the root. Thus, if the array is a post traversal of a BST, then a[0..n-1] can be divided into two parts a[0..i-1] and a[i, n-2], where
  • 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
                         ^                                 /     \
                     /       \                            2       6
           {1, 3, 4, 2}      {7,6}                      /  \       \
                     ^          ^                      1    4       7
                  /     \        \                         /
               {1}    {3,4}      {7}                      3
                         ^
                        /
                      {3}
Below is the implementation of this idea which 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]
        5
       /  
      4
     /
    3
   /
  2
 /
1   
http://louie.link/Algorithm-Practice/Algorithm%20Practice/24_VerifyPostOrderSquenceOfBST/

public static boolean isBSTPostOrder(int[] a, int p, int q){
 int n = q-p+1;;
 //base case always true for 1 element
 if(n < 2){
  return true;
 }
 
 //partition into left subtree a[p..right-1] and right subtree a[right..q-1]
 int right = p;
 while(a[right] < a[q]) right++;
 
 //check validity of right subtree
 int i = right;
 while(i < q && a[i] > a[q]) i++;
 if(i < q){
  return false;
 }
 
 return isBSTPostOrder(a, p, right-1) && isBSTPostOrder(a, right, q-1);
}
http://louie.link/Algorithm-Practice/Algorithm%20Practice/24_VerifyPostOrderSquenceOfBST/
public static boolean VerifySquenceOfBST(int[] sequence) {
    if (sequence == null || sequence.length == 0)
        return false;
    return helper(sequence, 0, sequence.length - 1); // [lo, hi]
}

private static boolean helper(int[] sequence, int lo, int hi) {
    if (lo >= hi) //since we used 'leftRoot + 1, hi - 1', lo could > hi
        return true;
    int root = sequence[hi];
    int leftRoot = hi - 1; //left to root

    // first, find left-subtree's root, traverse backwards
    while (leftRoot >= lo) {
        if (sequence[leftRoot] < root)
            break;
        leftRoot--;
    }

    // we found the left-subtree's root, all the elements in left-subtree should be smaller than root value
    // keep traversing backward, if there exists an element > root, then invalid left subtree
    for (int i = leftRoot; i >= lo; i--) {
        if (sequence[i] > root)
            return false;
    }

    //hi - 1 is rightRoot
    return helper(sequence, lo, leftRoot) && helper(sequence, leftRoot + 1, hi - 1); 
}

https://github.com/xieqilu/Qilu-leetcode/blob/master/B206.ValidatePostorderTraversalBST.cs


Q:如何验证后序序列?
A:后序序列的顺序是left - right - root,而先序的顺序是root - left - right。我们同样可以用本题的方法解,不过是从数组的后面向前面遍历,因为root在后面了。而且因为从后往前看是先遇到right再遇到left,所以我们要记录的是限定的最大值,而不再是最小值,栈pop的条件也变成pop所有比当前数大得数。栈的增长方向也是从高向低了。
    public boolean IsValidPostOrderBst(int[] nums)
    {
        int i = nums.length;
        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;
    }
验证二叉树后序遍历序列

LeetCode 968 - Binary Tree Cameras


https://leetcode.com/problems/binary-tree-cameras/
Given a binary tree, we install cameras on the nodes of the tree. 
Each camera at a node can monitor its parent, itself, and its immediate children.
Calculate the minimum number of cameras needed to monitor all nodes of the tree.

Example 1:

Input: [0,0,null,0,0]
Output: 1
Explanation: One camera is enough to monitor all nodes if placed as shown.
Example 2:

Input: [0,0,null,0,null,0,null,null,0]
Output: 2
Explanation: At least two cameras are needed to monitor all nodes of the tree. The above image shows one of the valid configurations of camera placement.

Note:
  1. The number of nodes in the given tree will be in the range [1, 1000].
  2. Every node has value 0
X. DP
http://reeestart.me/2019/01/02/LeetCode-968-Binary-Tree-Camera/

https://www.acwing.com/solution/LeetCode/content/698/
f(i,0)f(i,0) 表示 ii 结点被父结点监控的所需要的最少摄像头数量;同理,f(i,1)f(i,1) 和 f(i,2)f(i,2) 分别表示被自己监控和被某个儿子结点监控所需要的最少摄像头数量。
我们通过递归的方式来更新 ff 数组;对于叶结点,f(i,0)=0f(i,0)=0,f(i,1)=1f(i,1)=1,f(i,2)=+∞f(i,2)=+∞。
对非叶结点转移时,f(i,0)f(i,0) 不能取儿子结点中被父结点监控,所以很容易写出 f(i,0)=min(f(j1,1)+f(j2,2))+min(f(j2,1)+f(j2,2))f(i,0)=min(f(j1,1)+f(j2,2))+min(f(j2,1)+f(j2,2));f(i,1)f(i,1) 可以取儿子结点中三种状态的最小值之和;f(i,2)f(i,2) 则需要某个儿子必须取“被自己监控”,其余取“被自己监控”和“被某个儿子监控”中的最小值。转移方程都可以很容易的写出。
最终答案为,min(f(root,1),f(root,2))min(f(root,1),f(root,2))。
我们需要用一个哈希表,将指针映射到数字编号。

每个结点仅遍历一次,遍历某个结点仅需要常数的时间更新,故总时间复杂度为  O(n)

https://blog.csdn.net/zjucor/article/details/85380470
考虑为了计算root节点的值,需要每个子节点收集什么信息,满足什么条件,可以有哪些状态。

首先,子节点的子节点必须是合理,而子节点可以是被cover到的,也可以没有cover到(当前root节点可以cover子节点)。所以子节点有2个因素会影响父节点的计算:

1. 当前子节点有没有被cover(没有被cover的话,父节点就一定要cover当前子节点)

2. 当前子节点有没有放camera(放了的话父节点就被cover了)

所以,为了计算父节点的值,需要收集子节点3(有1种状态不可能出现)种状态的值:

1. 子节点没被cover(子节点一定没有camera)

2. 子节点被cover,而且子节点处没放camera

3. 子节点被cover,而且子节点处放了camera


Let's try to cover every node, starting from the top of the tree and working down. Every node considered must be covered by a camera at that node or some neighbor.
Because cameras only care about local state, we can hope to leverage this fact for an efficient solution. Specifically, when deciding to place a camera at a node, we might have placed cameras to cover some subset of this node, its left child, and its right child already.
Algorithm
Let solve(node) be some information about how many cameras it takes to cover the subtree at this node in various states. There are essentially 3 states:
  • [State 0] Strict subtree: All the nodes below this node are covered, but not this node.
  • [State 1] Normal subtree: All the nodes below and including this node are covered, but there is no camera here.
  • [State 2] Placed camera: All the nodes below and including this node are covered, and there is a camera here (which may cover nodes above this node).
Once we frame the problem in this way, the answer falls out:
  • To cover a strict subtree, the children of this node must be in state 1.
  • To cover a normal subtree without placing a camera here, the children of this node must be in states 1 or 2, and at least one of those children must be in state 2.
  • To cover the subtree when placing a camera here, the children can be in any state.
  public int minCameraCover(TreeNode root) {
    int[] ans = solve(root);
    return Math.min(ans[1], ans[2]);
  }

  // 0: Strict ST; All nodes below this are covered, but not this one
  // 1: Normal ST; All nodes below and incl this are covered - no camera
  // 2: Placed camera; All nodes below this are covered, plus camera here
  public int[] solve(TreeNode node) {
    if (node == null)
      return new int[] { 0, 0, 99999 };

    int[] L = solve(node.left);
    int[] R = solve(node.right);
    int mL12 = Math.min(L[1], L[2]);
    int mR12 = Math.min(R[1], R[2]);

    int d0 = L[1] + R[1];
    int d1 = Math.min(L[2] + mR12, R[2] + mL12);
    int d2 = 1 + Math.min(L[0], mL12) + Math.min(R[0], mR12);
    return new int[] { d0, d1, d2 };

  }
https://blog.csdn.net/likewind1993/article/details/85537408
    int minCameraCover(TreeNode* root)
    {
        vector<int> ans = solve(root);
//由于状态0不满足覆盖条件,因此当当前节点为root时, 选择满足状态1和状态2中的最小值
        return min(ans[1], ans[2]);

    }
    vector<int> solve(TreeNode* node) {
//按照之前递归出口的分析,这里将MAX_VALUE设为了99999
        if(node == NULL)
            return vector<int>{0, 0, 99999};

        //对当前节点的左子节点以及右子节点计算最优解值
        vector<int> L = solve(node->left);
        vector<int> R = solve(node->right);
//求解左子节点以及右子节点中满足的状态1、状态2的最小值
//也就是全部覆盖左子树、右子树所需相机数的最优解
        int mL12 = min(L[1], L[2]);
        int mR12 = min(R[1], R[2]);
        //当当前节点为状态0时,按照【总结1】,两个子节点必须处于状态1.
        //因此得到的最优解为L[1]+R[1]
        int d0 = L[1] + R[1];
        //当当前节点为状态1时,按照【总结2】,两个子节点中至少有一个节点处于状态2,另一个子节点处于状态1或2
        //因此得到的最优解为这两种情况的最小值
        int d1 = min(L[2] + mR12, R[2] + mL12);
        //当当前节点为状态2时,按照【总结3】, 只要求得两个子节点的最优解在加上当前节点放置的相机数1即可
        int d2 = 1 + min(L[0], mL12) + min(R[0], mR12);

        return vector<int>{d0, d1, d2};
    }

X. Greedy
https://leetcode.com/problems/binary-tree-cameras/discuss/211180/JavaC%2B%2BPython-Greedy-DFS
Consider a node in the tree.
It can be covered by its parent, itself, its two children.
Four options.
Consider the root of the tree.
It can be covered by left child, or right child, or itself.
Three options.
Consider one leaf of the tree.
It can be covered by its parent or by itself.
Two options.
If we set a camera at the leaf, the camera can cover the leaf and its parent.
If we set a camera at its parent, the camera can cover the leaf, its parent and its sibling.
We can see that the second plan is always better than the first.
Now we have only one option, set up camera to all leaves' parent.
Here is our greedy solution:
  1. Set cameras on all leaves' parents, thenremove all covered nodes.
  2. Repeat step 1 until all nodes are covered.

Explanation:

Apply a recusion function dfs.
Return 0 if it's a leaf.
Return 1 if it's a parent of a leaf, with a camera on this node.
Return 2 if it's coverd, without a camera on this node.
For each node,
if it has a child, which is leaf (node 0), then it needs camera.
if it has a child, which is the parent of a leaf (node 1), then it's covered.
If it needs camera, then res++ and we return 1.
If it's covered, we return 2.
Otherwise, we return 0.
    int res = 0;
    public int minCameraCover(TreeNode root) {
        return (dfs(root) < 1 ? 1 : 0) + res;
    }

    public int dfs(TreeNode root) {
        if (root == null) return 2;
        int left = dfs(root.left), right = dfs(root.right);
        if (left == 0 || right == 0) {
            res++;
            return 1;
        }
        return left == 1 || right == 1 ? 2 : 0;
    }
因为仔细想一下, 对于叶子结点Leaf来说, 是根本不可能放camera的. 肯定是放在parent of leaf划算.
因此最直接的方法就是把tree转换成一个graph, 然后循环一下步骤直到没有结点位置:
  1. remove all leaf nodes.
  2. res += (number of current leaf nodes)
  3. remove all leaf nodes. (parent of actual leaf)
  4. remove all leaf nodes. (parent of the node with camera)
  5. Go back to step 1.
这个方法代码写起来估计有点烦, 大神们连graph都不用建, 直接用遍历一遍tree就搞定. 自己欣赏吧.
private static final int LEAF = 0;
private static final int HAS_CAMERA = 1;
private static final int NO_NEED_CAMERA = 2;
int res = 0;
public int minCameraCover(TreeNode root) {
if (root == null) return 0;
if (dfs(root) == LEAF) res++;
return res;
}
private int dfs(TreeNode root) {
if (root == null) return NO_NEED_CAMERA;
if (root.left == null && root.right == null) return LEAF;
int l = dfs(root.left);
int r = dfs(root.right);
if (l == LEAF || r == LEAF) {
res++;
return HAS_CAMERA;
} else if (l == HAS_CAMERA || r == HAS_CAMERA) {
return NO_NEED_CAMERA;
} else {
return LEAF;
}
}
https://raw.githubusercontent.com/cherryljr/LeetCode/master/Binary%20Tree%20Cameras.java
 * Approach: Divide and Conquer with Greedy
 * 基本上所有 树 类型的题目,都是差不多这个套路。
 * 不过本题难点在于能不能想出这个贪心的处理。
 * (其实也不难,因为在推理状态的时候,其实挺自然地就能想出来了)
 * 本道题目其实算是 Distribute Coins in Binary Tree 的一个升级变形题。
 *
 * 首先我们定义一个 Func 作用为:在当前node的子树中放置照相机,并保证每个节点都能被监控到。
 * 同时,定义一个全局变量 res,每次放置相机的时候,加上需要的个数即可。
 * 然后,我们就会遇到本题的难点:
 *  放置相机之后,其能够监听 当前节点 + 左右孩子 + 节点的父亲。
 * 因此我们需要知道在 Func 处理之后,每个节点的状态是什么。(照相机到底放在哪个点上)
 * 而放置方法就用到了贪心的策略:
 *  因为放在 叶子节点的父亲节点 上能监控到的点比放在 叶子节点 上要多,
 *  故我们在放置相机的时候,会选择放在 left.parent 上。
 *  然后将已经被监控的节点去除掉,再考虑未被监控的点。
 *  这其实也就是 递归调用 的一个过程。
 * 因此这里定义:
 *  return 0:代表当前节点是 叶子 节点
 *  return 1:代表当前节点是 放置相机 的节点(即叶子节点的父亲)
 *  return 2:代表当前节点是 已经被监控 的节点(可以理解为放置了相机的节点的父亲)
 * 然后,只有当 left == 0 || right == 0.即当前节点为 leaf.parent 时,我们需要放置相机,此时 res++
 * 最后,值得注意的一点是:
 *  当我们对 root 处理之后,我们需要查询 root 是否已经被监控到。
 *  如果没有的话,我们需要多放置一台相机来监控 root.
 *  eg. 整颗树只有一个节点,此时我们对 root 处理完后发现,dfs(root) 返回值为 0
 *  代表去除被监控的点后,root作为leaf还未被监控,此时我们需要对结果加一。
 *

    public int minCameraCover(TreeNode root) {
        int state = dfs(root);
        return res + (state == 0 ? 1 : 0);
    }

    // Return 0 if it's a leaf.
    // Return 1 if it's a parent of a leaf, with a camera on this node
    // Return 2 if it's coverd, without a camera on this node
    // (like the parent node of a node with camera)
    private int dfs(TreeNode root) {
        if (root == null) {
            return 2;
        }
        int left = dfs(root.left);
        int right = dfs(root.right);
        if (left == 0 || right == 0) {
            res++;
            return 1;
        }
        return left == 1 || right == 1 ? 2 : 0;
    }

https://leetcode.com/articles/binary-tree-cameras/
Instead of trying to cover every node from the top down, let's try to cover it from the bottom up - considering placing a camera with the deepest nodes first, and working our way up the tree.
If a node has its children covered and has a parent, then it is strictly better to place the camera at this node's parent.
If a node has children that are not covered by a camera, then we must place a camera here. Additionally, if a node has no parent and it is not covered, we must place a camera here.

  int ans;
  Set<TreeNode> covered;

  public int minCameraCover(TreeNode root) {
    ans = 0;
    covered = new HashSet();
    covered.add(null);

    dfs(root, null);
    return ans;
  }

  public void dfs(TreeNode node, TreeNode par) {
    if (node != null) {
      dfs(node.left, node);
      dfs(node.right, node);

      if (par == null && !covered.contains(node) || !covered.contains(node.left) || !covered.contains(node.right)) {
        ans++;
        covered.add(node);
        covered.add(par);
        covered.add(node.left);
        covered.add(node.right);
      }
    }
  }

http://www.noteanddata.com/leetcode-968-Binary-Tree-Cameras-java-solution-note.html
  1. 首先对于任意一个节点,只有四种可能是会让这个节点被cover的, 一种是父节点有camera, 一种是自己有camera,一种是左节点有camera,一种是右节点有camera
  2. 那么如果我们用dfs写, 首先需要传入的状态有父节点是否有camera, 因为这个会影响到当前节点是否一定要放置camera的问题
  3. 然后, 对于每个节点,我们可以选择当前是否放置camera, 这里遇到一个比较tricky的问题是, 如果父节点没有camera, 当前节点也选择不放置camera的话,
    那么,在左节点或者右节点必须要放置一个camera, 才可以保证当前节点被cover, 所以, 这里会需要传入另外一个状态force, 用来表示某个节点是否一定要放置camera(就是上一次访问的节点的子节点),
  4. 到这里,我们可以定义递归函数如下
    int dfsmin(TreeNode node, boolean parentVisited, boolean force)
  5. 函数的实现,就是对几种情况分别做处理, 然后取合法的最小值就好
  6. 当然, 这样做会超时, 原因是递归的过程中, 对很多节点做了大量的重复计算, 所以我们可能会考虑用一个Map<TreeNode, Integer>来做记忆化搜索, 可是实际上这样提交以后会几乎全错, 原因是这里会对每个node做多次计算, 但是每次状态都是不一样的, 比如parentvisited的值会不一样, 所以这里需要定义一个state, 来对这个递归过程做记忆
public int minCameraCover(TreeNode root) {
    return dfsmin(root, false, false, new HashMap<>());
}

// each dfsmin will care about how to cover the current node
int dfsmin(TreeNode node, boolean parentVisited, boolean force, Map<State, Integer> map) {
    if(null == node) return 0;
    
    State state = new State(node, parentVisited, force);
    // note we have to use the state as the key, use the node as the key will give wa
    // because each node is called multiple times with different state
    if(map.containsKey(state)) {  
        return map.get(state);
    }
    
    if(parentVisited) {
        // case 1: visit current node
        int min1 = 1 + dfsmin(node.left, true, false, map) + dfsmin(node.right, true, false, map);
        // case 2 : not visit current node (because current node is covered by parent)
        int min2 = dfsmin(node.left, false, false, map) + dfsmin(node.right, false, false, map);
        int min = Math.min(min1, min2);
        map.put(state, min);

        return min;
    }
    else {
        // case 1: visit current node
        int min = 1 + dfsmin(node.left, true, false, map) + dfsmin(node.right, true, false, map);
        
        if(!force) {
            // case 2: not visit cur, visit left (if left exist) so that left can cover cur
            if(node.left != null) {
                int min2 = dfsmin(node.left, false, true, map) + dfsmin(node.right, false, false, map);    
                min = Math.min(min, min2);
            }
            if(node.right != null) {
                int min3 = dfsmin(node.left, false, false, map) + dfsmin(node.right, false, true, map);
                min = Math.min(min, min3);
            }
        }
        
        map.put(state, min);
        
        return min;
    }
}

static class State {
    private TreeNode node;
    private boolean parentVisited;
    private boolean force;
    public State(TreeNode node, boolean parentVisited, boolean force) {
        this.node = node;
        this.parentVisited = parentVisited;
        this.force = force;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        State state = (State) o;
        return parentVisited == state.parentVisited &&
                force == state.force &&
                Objects.equals(node, state.node);
    }

    @Override
    public int hashCode() {
        return Objects.hash(node, parentVisited, force);
    }
}    

时间复杂度

O(N*4), 对于每个node,有4种不同的状态,分别做了搜索

空间复杂度

O(N*4), 对于每个状态都做了搜索




Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts