LeetCode 971 - Flip Binary Tree To Match Preorder Traversal


https://leetcode.com/problems/flip-binary-tree-to-match-preorder-traversal/
Given a binary tree with N nodes, each node has a different value from {1, ..., N}.
A node in this binary tree can be flipped by swapping the left child and the right child of that node.
Consider the sequence of N values reported by a preorder traversal starting from the root.  Call such a sequence of N values the voyage of the tree.
(Recall that a preorder traversal of a node means we report the current node's value, then preorder-traverse the left child, then preorder-traverse the right child.)
Our goal is to flip the least number of nodes in the tree so that the voyage of the tree matches the voyage we are given.
If we can do so, then return a list of the values of all nodes flipped.  You may return the answer in any order.
If we cannot do so, then return the list [-1].

Example 1:
Input: root = [1,2], voyage = [2,1]
Output: [-1]
Example 2:
Input: root = [1,2,3], voyage = [1,3,2]
Output: [1]
Example 3:
Input: root = [1,2,3], voyage = [1,2,3]
Output: []

Note:
  1. 1 <= N <= 100
https://blog.csdn.net/fuxuemingzhu/article/details/85939252
其实这个题不难,因为题目就说了是前序遍历,所以做法肯定还是前序遍历。我刚开始一直想不通的地方在于,题目又是返回[-1],又是正常返回,没想好怎么做区分。其实做法就是递归函数不仅要修改res数组,还要返回表示能不能构成题目条件的bool变量。

按照lee215的说法,看到二叉树的题,很大可能就需要递归,所以直接先写出dfs函数,然后再慢慢向里面填东西。

我们定义的dfs函数意义是,我们能不能通过翻转(或者不翻转)该root节点的左右子树,得到对应v。如果能,返回true,否则返回false。

首先在递归函数中,我们对root节点进行判断,如果root不存在,这种情况不应该认为是题目输入错误,而是应该认为已经遍历到最底部了,这个时候相当于root = [], voyage = [],所以返回true;在先序遍历的时候,root节点是第一个要被遍历到的节点,如果不和voyage[0]相等,直接返回false;

这个题目的难点在于是否需要翻转一个节点的左右孩子。判断的方法其实是简单的:如果voyage第二个元素等于root的左孩子,那么说明不用翻转,直接递归调用左右孩子;否则如果voyage的第二个元素等于root的右孩子,那么还要注意一下,在左孩子存在的情况下,我们需要翻转当前的节点左右孩子。

翻转是什么概念呢?这里并没有直接交换,而是把当前遍历到的位置使用遍历i保存起来,这样voyage[i]就表示当前遍历到哪个位置了。所以dfs调用两个孩子的顺序很讲究,它体现了先序遍历先解决哪个树的问题,也就是完成了逻辑上的交换左右孩子。
https://zhanghuimeng.github.io/post/leetcode-971-flip-binary-tree-to-match-preorder-traversal/
接下来的问题就是要不要翻转。如果有左子结点,那么下一个值必然是左子结点的值;否则,如果有右子结点,下一个值必然是右子结点的值;否则(也就是说没有子结点),下一个值和之前的遍历相关,可能是父节点的右子结点之类的。
所以做出翻转的决策就很简单:如果左子结点有值,且这个值和遍历序列中预期的下一个值不等,那么就交换左右子树(显然不交换肯定不行了)。判断是否可行的方法也很简单,在上述翻转的基础上,每次都判断当前根结点的值和遍历序列中预期的值是否相等。

事实上,这个判断左子结点的方法是题解[1]里给出的。我比赛的时候用的是,判断是否有右子结点,如果有的话,它的值如果和预期的下一个值相等,才有可能有必要进行交换(当然也有可能根本实现不了)。这个判断本质上和上述方法是一样的。
X.
https://leetcode.com/problems/flip-binary-tree-to-match-preorder-traversal/discuss/214216/JavaC%2B%2BPython-DFS-Solution
Global integer i indicates next index in voyage v.
If current node == null, it's fine, we return true
If current node.val != v[i], doesn't match wanted value, return false
If left child exist but don't have wanted value, flip it with right child.
    List<Integer> res = new ArrayList<>();
    int i = 0;
    public List<Integer> flipMatchVoyage(TreeNode root, int[] v) {
        return dfs(root, v) ? res : Arrays.asList(-1);
    }

    public Boolean dfs(TreeNode node, int[] v) {
        if (node == null) return true;
        if (node.val != v[i++]) return false;
        if (node.left != null && node.left.val != v[i]) {
            res.add(node.val);
            return dfs(node.right, v) && dfs(node.left, v);
        }
        return dfs(node.left, v) && dfs(node.right, v);
    }
https://blog.csdn.net/fly_yr/article/details/86154745
    public List<Integer> flipMatchVoyage(TreeNode root, int[] voyage) {
        List<Integer> flipPath = new ArrayList<>();
        int resultIdx = dfs(root, 0, flipPath, voyage);
     
        if (resultIdx == -1 || resultIdx < voyage.length-1) {
            return Arrays.asList(-1);
        }
        return flipPath;
    }

    public int dfs(TreeNode root, int idx, List<Integer> flipPath, int[] voyage) {
        if (root == null) {
            return idx;
        }
     
        if (idx >= voyage.length) {
            return -1;
        }

        if(root.val != voyage[idx]) {
            return -1;
        }

        int left = dfs(root.left, idx + 1, flipPath, voyage);
        if (left != -1) {
            //Completed the leaf node in left child, then right child
            return dfs(root.right, left, flipPath, voyage);
        }

        //Need to flip
        flipPath.add(root.val);

        int right = dfs(root.right, idx + 1, flipPath, voyage);
        if (right != -1) {
            return dfs(root.left, right, flipPath, voyage);
        }

        return -1;
    }
https://www.acwing.com/solution/LeetCode/content/755/
    bool dfs(TreeNode *rt, const vector<int>& voyage, int& cur, vector<int>& ans) {
        if (rt == nullptr)
            return true;

        if (rt -> val != voyage[cur])
            return false;

        cur++;

        if (dfs(rt -> left, voyage, cur, ans) && dfs(rt -> right, voyage, cur, ans))
            return true;

        if (dfs(rt -> right, voyage, cur, ans) && dfs(rt -> left, voyage, cur, ans)) {
            ans.push_back(rt -> val);
            return true;
        }

        return false;
    }

    vector<int> flipMatchVoyage(TreeNode* root, vector<int>& voyage) {
        int cur = 0;
        vector<int> ans;

        if (!dfs(root, voyage, cur, ans))
            return {-1};

        return ans;
    }
https://leetcode.com/articles/flip-binary-tree-to-match-preorder-traversal/
Approach 1: Depth-First Search
As we do a pre-order traversal, we will flip nodes on the fly to try to match our voyage with the given one.
If we are expecting the next integer in our voyage to be voyage[i], then there is only at most one choice for path to take, as all nodes have different values.

Do a depth first search. If at any node, the node's value doesn't match the voyage, the answer is [-1].
Otherwise, we know when to flip: the next number we are expecting in the voyage voyage[i] is different from the next child.

  List<Integer> flipped;
  int index;
  int[] voyage;

  public List<Integer> flipMatchVoyage(TreeNode root, int[] voyage) {
    flipped = new ArrayList();
    index = 0;
    this.voyage = voyage;

    dfs(root);
    if (!flipped.isEmpty() && flipped.get(0) == -1) {
      flipped.clear();
      flipped.add(-1);
    }

    return flipped;
  }

  public void dfs(TreeNode node) {
    if (node != null) {
      if (node.val != voyage[index++]) {
        flipped.clear();
        flipped.add(-1);
        return;
      }

      if (index < voyage.length && node.left != null && node.left.val != voyage[index]) {
        flipped.add(node.val);
        dfs(node.right);
        dfs(node.left);
      } else {
        dfs(node.left);
        dfs(node.right);
      }
    }
  }
X.
  1. Input i of dfs() is the current index in voyage, then root.val == voyage[i], otherwise return -1.
  2. The return of dfs() is the index in voyage for the successor of preorder traversal.
    public List<Integer> flipMatchVoyage(TreeNode root, int[] voyage) {
        List<Integer> path = new ArrayList<>();
        if (dfs(root, voyage, 0, path) == -1) {
            List<Integer> result = new ArrayList<>();
            result.add(-1);
            return result;
        }
        return path;
    }
    private int dfs(TreeNode root, int[] voyage, int i, List<Integer> path) {
        if (root == null) {
            return i;
        }
        if (root.val != voyage[i]) {
            return -1;
        }
        int left = dfs(root.left, voyage, i + 1, path);
        if (left != -1) {
            return dfs(root.right, voyage, left, path);
        }
  // need a flip
        path.add(root.val);
        int right = dfs(root.right, voyage, i + 1, path);
        if (right != -1) {
            return dfs(root.left, voyage, right, path);
        }
        return -1;
    }
https://blog.csdn.net/fuxuemingzhu/article/details/85939252
其实这个题不难,因为题目就说了是前序遍历,所以做法肯定还是前序遍历。我刚开始一直想不通的地方在于,题目又是返回[-1],又是正常返回,没想好怎么做区分。其实做法就是递归函数不仅要修改res数组,还要返回表示能不能构成题目条件的bool变量。

按照lee215的说法,看到二叉树的题,很大可能就需要递归,所以直接先写出dfs函数,然后再慢慢向里面填东西。

我们定义的dfs函数意义是,我们能不能通过翻转(或者不翻转)该root节点的左右子树,得到对应v。如果能,返回true,否则返回false。

首先在递归函数中,我们对root节点进行判断,如果root不存在,这种情况不应该认为是题目输入错误,而是应该认为已经遍历到最底部了,这个时候相当于root = [], voyage = [],所以返回true;在先序遍历的时候,root节点是第一个要被遍历到的节点,如果不和voyage[0]相等,直接返回false;

这个题目的难点在于是否需要翻转一个节点的左右孩子。判断的方法其实是简单的:如果voyage第二个元素等于root的左孩子,那么说明不用翻转,直接递归调用左右孩子;否则如果voyage的第二个元素等于root的右孩子,那么还要注意一下,在左孩子存在的情况下,我们需要翻转当前的节点左右孩子。

翻转是什么概念呢?这里并没有直接交换,而是把当前遍历到的位置使用遍历i保存起来,这样voyage[i]就表示当前遍历到哪个位置了。所以dfs调用两个孩子的顺序很讲究,它体现了先序遍历先解决哪个树的问题,也就是完成了逻辑上的交换左右孩子。

https://medium.com/@poitevinpm/solution-to-leetcode-problem-971-flip-binary-tree-to-match-preorder-traversal-bea0124e7f8c
- No need to change the tree
  public List<Integer> flipMatchVoyage(TreeNode root, int[] voyage) {
    List<Integer> result = new ArrayList<>();
    if (helper(root, voyage, result, new int[] { -1 }, null))
      return result;
    result.clear();
    result.add(-1);
    return result;
  }

  public boolean helper(TreeNode root, int[] voyage, List<Integer> result, int[] index, TreeNode prev) {

    if (root == null)
      return true;
    int k = ++index[0];
    if (root.val != voyage[k])
      return false;

    if (root.left != null) {
      if (k + 1 >= voyage.length) //
        return false;
      if (root.left.val != voyage[k + 1]) {
        result.add(k + 1);
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
      }

      if (!helper(root.left, voyage, result, index, root))
        return false;
    }

    return helper(root.right, voyage, result, index, root);

  }

X. Stack
https://leetcode.com/problems/flip-binary-tree-to-match-preorder-traversal/discuss/214384/JavaC%2B%2BPython-Iterative-Solution-Using-Stack

    public List<Integer> flipMatchVoyage(TreeNode root, int[] voyage) {
        List<Integer> res = new ArrayList<>();
        int i = 0;
        Stack<TreeNode> s = new Stack<>();
        s.push(root);
        while (s.size() > 0) {
            TreeNode node = s.pop();
            if (node == null) continue;
            if (node.val != voyage[i++]) return Arrays.asList(-1);
            if (node.right != null && node.right.val == voyage[i]) {
                if (node.left != null) res.add(node.val);
                s.push(node.left);
                s.push(node.right);
            } else {
                s.push(node.right);
                s.push(node.left);
            }
        }
        return res;
    }

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