【问题标题】:Given a BST and its root, print all sequences of nodes which give rise to the same bst给定一个 BST 及其根,打印所有产生相同 bst 的节点序列
【发布时间】:2014-01-19 00:29:53
【问题描述】:

给定一个 BST,找到从根开始的所有节点序列,它们基本上会给出相同的二叉搜索树。

给一个bst,说

  3
 /  \
1    5

答案应该是 3,1,5 和 3,5,1。

另一个例子

       5
     /   \
    4     7
   /     / \
  1     6   10

输出将是

5,4,1,7,6,10

5,4,7,6,10,1

5,7,6,10,4,1

然而,这里的不变量是父索引必须始终小于其子索引。我很难实现它。

【问题讨论】:

标签: permutation binary-search-tree sequences


【解决方案1】:

我假设您想要生成相同 BST 的所有序列的列表。
在这个答案中,我们将使用分而治之
我们将创建一个函数findAllSequences(Node *ptr),它将节点指针作为输入并返回所有不同的序列,这些序列将生成悬挂在ptr 的子树。此函数将返回一个整数向量的向量,即包含所有序列的vector<vector<int>>

生成序列的主要思想根必须在其所有子级之前

算法

基本情况 1:
如果ptrNULL,则返回一个空序列的向量。

if (ptr == NULL) {
    vector<int> seq;
    vector<vector<int> > v;
    v.push_back(seq);
    return v;
}

基本情况 2:
如果ptrleaf node,则返回具有单个序列的向量。这个序列将只包含一个元素,即该节点的值,这很简单。

if (ptr -> left == NULL && ptr -> right == NULL) {
    vector<int> seq;
    seq.push_back(ptr -> val);
    vector<vector<int> > v;
    v.push_back(seq);
    return v;
}

划分部分这部分很简单。
我们假设我们有一个函数可以解决这个问题,因此我们为左子树和右子树解决它。

vector<vector<int> > leftSeq  = findAllSeq(ptr -> left);
vector<vector<int> > rightSeq = findAllSeq(ptr -> right);

合并两个解决方案。(关键在这一步。
到目前为止,我们有两个包含不同序列的集合:

i. leftSeq  - all sequences in this set will generate left subtree.
ii. rightSeq - all sequences in this set will generate right subtree.

现在左子树中的每个序列都可以与右子树中的每个序列合并。在合并时,我们应该注意保留元素的相对顺序。同样在每个合并序列中,我们将在开头添加当前节点的值,因为 root 必须位于所有子节点之前。

用于合并的伪代码

vector<vector<int> > results
for all sequences L in leftSeq
    for all sequences R in rightSeq
        create a vector flags with l.size() 0's and R.size() 1's
        for all permutations of flag
            generate the corresponding merged sequence.
            append the current node's value in beginning
            add this sequence to the results.

return results. 

解释:让我们从集合leftSeq 中取一个序列,比如L(of size n),从集合rightSeq 中取一个序列,比如R(of size m)
现在这两个序列可以以 m+nCn 方式合并!
证明 : 合并后,新序列将有m + n 元素。由于我们必须保持元素的相对顺序,所以首先我们将L 中的所有n 元素填充到所有n 位置中的任何(m+n) 位置中。之后剩余的m 位置可以由R 的元素填充。因此我们必须choose n places from (m+n) places
为此,让我们创建一个布尔向量,例如 flags 并用 n 0'sm 1's 填充它。0 的值代表来自 left 序列的成员和一个值1 的成员代表来自 right 序列的成员。剩下的就是生成这个标志向量的所有permutations,这可以用next_permutation完成。现在,对于标志的每个排列,我们将有一个不同的 LR 合并序列。
例如:L={1, 2, 3} R= {4, 5}
所以,n=3m=2
因此,我们可以有 3+2C3 个合并序列,即 10.
1.现在,最初flags = {0 0 0 1 1},填充有3 0's2 1's
这将导致这个合并序列:1 2 3 4 5
2.调用 nextPermutation 后,我们将有
flags = {0 0 1 0 1}
这将生成序列:1 2 4 3 5
3.再次调用 nextPermutation 后,我们将有
flags = {0 0 1 1 0}
这将生成序列:1 2 4 5 3
等等...

C++ 代码

vector<vector<int> > findAllSeq(TreeNode *ptr)
{
    if (ptr == NULL) {
        vector<int> seq;
        vector<vector<int> > v;
        v.push_back(seq);
        return v;
    }


    if (ptr -> left == NULL && ptr -> right == NULL) {
        vector<int> seq;
        seq.push_back(ptr -> val);
        vector<vector<int> > v;
        v.push_back(seq);
        return v;
    }

    vector<vector<int> > results, left, right;
    left  = findAllSeq(ptr -> left);
    right = findAllSeq(ptr -> right);
    int size = left[0].size() + right[0].size() + 1;

    vector<bool> flags(left[0].size(), 0);
    for (int k = 0; k < right[0].size(); k++)
        flags.push_back(1);

    for (int i = 0; i < left.size(); i++) {
        for (int j = 0; j < right.size(); j++) {
            do {
                vector<int> tmp(size);
                tmp[0] = ptr -> val;
                int l = 0, r = 0;
                for (int k = 0; k < flags.size(); k++) {
                    tmp[k+1] = (flags[k]) ? right[j][r++] : left[i][l++];
                }
                results.push_back(tmp);
            } while (next_permutation(flags.begin(), flags.end()));
        }
    }

    return results;
}

2017 年 3 月 3 日更新:如果原始树包含重复项,此解决方案将无法正常工作。

【讨论】:

  • 如果我只想计算这些序列的数量,即。 results.size(),而不是枚举它们?它是否能够扩展到N = 50,即。 50 个整数?
  • @Atul 你认为vector&lt;vector&lt;int&gt;&gt; 在每次调用时都会返回子树的所有序列。
  • @Rahul 是的。这是可行的,因为它是一个递归解决方案
【解决方案2】:

这是我在 Python 3 中为您编写的一个清晰、简洁且有据可查的解决方案。希望对您有所帮助!

代码:bst_sequences.py

from binarytree import bst, Node


def weave_lists(first: list, second: list, results: list, prefix: list) -> None:
    """Recursively Weave the first list into the second list and append 
    it to the results list.  The prefix list grows by an element with the 
    depth of the call stack.  Ultimately, either the first or second list will 
    be exhausted and the base case will append a result."""
    # base case
    if not first or not second:
        results.append(prefix + first + second)
        return

    # recursive case
    first_head, first_tail = first[0], first[1:]
    weave_lists(first_tail, second, results, prefix + [first_head])

    second_head, second_tail = second[0], second[1:]
    weave_lists(first, second_tail, results, prefix + [second_head])


def all_sequences(root: Node) -> list:
    """Splits the tree into three lists: prefix, left, and right."""
    if root is None:
        return []

    answer = []
    prefix = [root.value]
    left = all_sequences(root.left) or [[]]
    right = all_sequences(root.right) or [[]]

    # At a minimum, left and right must be a list containing an empty list
    # for the following nested loop
    for i in range(len(left)):
        for j in range(len(right)):
            weaved = []
            weave_lists(left[i], right[j], weaved, prefix)
        answer.extend(weaved)

    return answer


if __name__ == "__main__":
    t = bst(2)
    print(t)
    solution = all_sequences(t)
    for e, item in enumerate(solution):
        print(f"{e:03}: {item}")

样本输出

    __4
   /   \
  1     5
 / \     \
0   2     6

000: [4, 1, 0, 2, 5, 6]
001: [4, 1, 0, 5, 2, 6]
002: [4, 1, 0, 5, 6, 2]
003: [4, 1, 5, 0, 2, 6]
004: [4, 1, 5, 0, 6, 2]
005: [4, 1, 5, 6, 0, 2]
006: [4, 5, 1, 0, 2, 6]
007: [4, 5, 1, 0, 6, 2]
008: [4, 5, 1, 6, 0, 2]
009: [4, 5, 6, 1, 0, 2]
010: [4, 1, 2, 0, 5, 6]
011: [4, 1, 2, 5, 0, 6]
012: [4, 1, 2, 5, 6, 0]
013: [4, 1, 5, 2, 0, 6]
014: [4, 1, 5, 2, 6, 0]
015: [4, 1, 5, 6, 2, 0]
016: [4, 5, 1, 2, 0, 6]
017: [4, 5, 1, 2, 6, 0]
018: [4, 5, 1, 6, 2, 0]
019: [4, 5, 6, 1, 2, 0]

Process finished with exit code 0

【讨论】:

  • 这看起来像是 CTCI 解决方案的一个非常精确的复制品,如果是这样的话,我建议您引用您的资料 :)
  • 您应该提到您从破解编码面试第 4 章中提取了代码,而不是声称它是您编写的。
【解决方案3】:

请注意,问题实际上是关于树的topological sorting:找出所有可能的方式来执行拓扑排序。也就是说,我们不关心树的具体构建方式,重要的是元素总是作为叶子添加,从不改变现有节点的结构。输出的约束是节点永远不会先于它们的祖先 - 将树视为经典 dependency graph

但与一般DAG 的拓扑排序不同,这里不需要引用计数,因为这是一棵树 - 引用的数量总是 1 或 0。

这是一个简单的 Python 实现:

    def all_toposorts_tree(sources, history):
        if not sources:
            print(history)
            return
        for t in sources:
            all_toposorts((sources - {t}) | {t.left, t.right} - {None}, history + [t.v])
    
    all_toposorts_tree({root}, [])

这是Cracking the Coding Interview, 6th Edition中的问题4.9。

【讨论】:

    【解决方案4】:

    我有一个更短的解决方案。你怎么看?

    function printSequences(root){
        let combinations = [];
    
        function helper(node, comb, others){
            comb.push(node.values);
    
            if(node.left) others.push(node.left);
            if(node.right) others.push(node.right);
    
            if(others.length === 0){
                combinations.push(comb);
                return;
            }else{
                for(let i = 0; i<others.length; i++){
                    helper(others[i], comb.slice(0), others.slice(0, i).concat(others.slice(i+1, others.length)));
                }
            }
        }
    
        helper(root, [], []);
        return combinations;
    }
    

    【讨论】:

      【解决方案5】:

      这是我的 python 代码,它为相同的 BST 生成所有元素/数字序列。 对于我提到的逻辑,我参考了 Gayle Laakmann Mcdowell 的《破解编码采访》一书

      from binarytree import  Node, bst, pprint
      
      def wavelist_list(first, second, wave, prefix):
          if first:
             fl = len(first)
          else:
             fl = 0
      
          if second:       
              sl = len(second)
          else:
             sl = 0   
          if fl == 0 or sl == 0:
             tmp = list()
             tmp.extend(prefix)
             if first:
                tmp.extend(first)
             if second:   
                tmp.extend(second)
             wave.append(tmp)
             return
      
          if fl:
              fitem = first.pop(0)
              prefix.append(fitem)
              wavelist_list(first, second, wave, prefix)
              prefix.pop()
              first.insert(0, fitem)
      
          if sl:
              fitem = second.pop(0)
              prefix.append(fitem)
              wavelist_list(first, second, wave, prefix)
              prefix.pop()
              second.insert(0, fitem)        
      
      
      def allsequences(root):
          result = list()
          if root == None:
             return result
      
          prefix = list()
          prefix.append(root.value)
      
          leftseq = allsequences(root.left)
          rightseq = allsequences(root.right)
          lseq = len(leftseq)
          rseq = len(rightseq)
      
          if lseq and rseq:
             for i in range(lseq):
                for j in range(rseq):
                  wave = list()
                  wavelist_list(leftseq[i], rightseq[j], wave, prefix)
                  for k in range(len(wave)):
                      result.append(wave[k])
      
          elif lseq:
            for i in range(lseq):
              wave = list()
              wavelist_list(leftseq[i], None, wave, prefix)
              for k in range(len(wave)):
                  result.append(wave[k])
      
          elif rseq:
            for j in range(rseq):
              wave = list()
              wavelist_list(None, rightseq[j], wave, prefix)
              for k in range(len(wave)):
                  result.append(wave[k])
         else:
             result.append(prefix) 
      
         return result
      
      
      
      if __name__=="__main__":
          n = int(input("what is height of tree?"))
          my_bst = bst(n)
          pprint(my_bst)
      
          seq = allsequences(my_bst)
          print("All sequences")
          for i in range(len(seq)):
              print("set %d = " %(i+1), end="")
              print(seq[i])
      
       example output:
       what is height of tree?3
      
             ___12      
            /     \     
        __ 6       13   
       /   \        \  
       0     11       14
        \               
         2              
      
      
        All sequences
        set 1 = [12, 6, 0, 2, 11, 13, 14]
        set 2 = [12, 6, 0, 2, 13, 11, 14]
        set 3 = [12, 6, 0, 2, 13, 14, 11]
        set 4 = [12, 6, 0, 13, 2, 11, 14]
        set 5 = [12, 6, 0, 13, 2, 14, 11]
        set 6 = [12, 6, 0, 13, 14, 2, 11]
        set 7 = [12, 6, 13, 0, 2, 11, 14]
        set 8 = [12, 6, 13, 0, 2, 14, 11]
        set 9 = [12, 6, 13, 0, 14, 2, 11]
        set 10 = [12, 6, 13, 14, 0, 2, 11]
        set 11 = [12, 13, 6, 0, 2, 11, 14]
        set 12 = [12, 13, 6, 0, 2, 14, 11]
        set 13 = [12, 13, 6, 0, 14, 2, 11]
        set 14 = [12, 13, 6, 14, 0, 2, 11]
        set 15 = [12, 13, 14, 6, 0, 2, 11]
        set 16 = [12, 6, 0, 11, 2, 13, 14]
        set 17 = [12, 6, 0, 11, 13, 2, 14]
        set 18 = [12, 6, 0, 11, 13, 14, 2]
        set 19 = [12, 6, 0, 13, 11, 2, 14]
        set 20 = [12, 6, 0, 13, 11, 14, 2]
        set 21 = [12, 6, 0, 13, 14, 11, 2]
        set 22 = [12, 6, 13, 0, 11, 2, 14]
        set 23 = [12, 6, 13, 0, 11, 14, 2]
        set 24 = [12, 6, 13, 0, 14, 11, 2]
        set 25 = [12, 6, 13, 14, 0, 11, 2]
        set 26 = [12, 13, 6, 0, 11, 2, 14]
        set 27 = [12, 13, 6, 0, 11, 14, 2]
        set 28 = [12, 13, 6, 0, 14, 11, 2]
        set 29 = [12, 13, 6, 14, 0, 11, 2]
        set 30 = [12, 13, 14, 6, 0, 11, 2]
        set 31 = [12, 6, 11, 0, 2, 13, 14]
        set 32 = [12, 6, 11, 0, 13, 2, 14]
        set 33 = [12, 6, 11, 0, 13, 14, 2]
        set 34 = [12, 6, 11, 13, 0, 2, 14]
        set 35 = [12, 6, 11, 13, 0, 14, 2]
        set 36 = [12, 6, 11, 13, 14, 0, 2]
        set 37 = [12, 6, 13, 11, 0, 2, 14]
        set 38 = [12, 6, 13, 11, 0, 14, 2]
        set 39 = [12, 6, 13, 11, 14, 0, 2]
        set 40 = [12, 6, 13, 14, 11, 0, 2]
        set 41 = [12, 13, 6, 11, 0, 2, 14]
        set 42 = [12, 13, 6, 11, 0, 14, 2]
        set 43 = [12, 13, 6, 11, 14, 0, 2]
        set 44 = [12, 13, 6, 14, 11, 0, 2]
        set 45 = [12, 13, 14, 6, 11, 0, 2]
      

      【讨论】:

      • 对于上面的代码,我使用二叉树包来创建给定长度的 BST。
      【解决方案6】:

      这是另一个基于递归的简洁易懂的解决方案:

      from binarytree import  Node, bst, pprint
      
      def allsequences1(root):
          if not root:
              return None
          lt = allsequences1(root.left)
          rt = allsequences1(root.right)
          ret = []
          if not lt and not rt:
              ret.append([root])
          elif not rt:
              for one in lt:
                  ret.append([root]+one)
          elif not lt:
              for two in rt:
                  ret.append([root]+two)
          else:
              for one in lt:
                  for two in rt:
                      ret.append([root]+one+two)
                      ret.append([root]+two+one)
          return ret
      
      
      
      if __name__=="__main__":
          n = int(input("what is height of tree?"))
          my_bst = bst(n)
          pprint(my_bst)
          seg = allsequences1(my_bst)
          print("All sequences ..1")
          for i in range(len(seq)):
              print("set %d = " %(i+1), end="")
              print(seq[i])
      

      【讨论】:

      • 能否请您添加一些 cmets 或分享一些关于您正在尝试做的事情?谢谢:)
      【解决方案7】:

      让我们首先观察创建相同的 BST 必须遵循什么。这里唯一足够的规则是在他们的左右孩子之前插入父母。因为,如果我们可以保证对于某个节点(我们有兴趣插入),所有父节点(包括祖父节点)都被插入,但没有一个子节点被插入,那么该节点将找到合适的位置插入。

      根据这一观察,我们可以编写回溯以生成将产生相同 BST 的所有序列。

      active_list = {root}
      current_order = {}
      result ={{}}
      backtrack():
           if(len(current_order) == total_node):
               result.push(current_order)
               return;
           for(node in active_list):
                current_order.push(node.value)
      
                if node.left : 
                     active_list.push(node.left)
                if node.right: 
                     active_list.push(node.right)
      
                active_list.remove(node)
                backtrack()
                active_list.push(node)
      
                if node.left : 
                     active_list.remove(node.left)
                if node.right: 
                     active_list.remove(node.right)
                current_order.remove(node.val)
      

      这不是有效的实现。仅用于说明目的。

      【讨论】:

        【解决方案8】:
        
        public class Solution {
            ArrayList<LinkedList<Long>> result;
            /*Return the children of a node */
            ArrayList<TreeNode> getChilden(TreeNode parent) {
                ArrayList<TreeNode> child = new ArrayList<TreeNode>();
                if(parent.left != null) child.add(parent.left);
                if(parent.right != null) child.add(parent.right);
                return child;
            }
            /*Gets all the possible Compinations*/
            void getPermutations(ArrayList<TreeNode> permutations, LinkedList<Long> current) {
                if(permutations.size() == 0) {
                    result.add(current);
                    return;
                }
                int length = permutations.size();
                for(int i = 0; i < length; i++) {
                    TreeNode node = permutations.get(i);
                    permutations.remove(i);
                    ArrayList<TreeNode> newPossibilities = new ArrayList<TreeNode>();
                    newPossibilities.addAll(permutations);
                    newPossibilities.addAll(getChilden(node));
                    LinkedList<Long> newCur = new LinkedList<Long>();
                    newCur.addAll(current);
                    newCur.add(node.val);
                    getPermutations(newPossibilities, newCur);
                    permutations.add(i,node);
                }
            }
        
            /*This method returns a array of arrays which will lead to a given BST*/
            ArrayList<LinkedList<Long>> inputSequencesForBst(TreeNode node) { 
                result = new ArrayList<LinkedList<Long>>();
                if(node == null)
                    return result;
                ArrayList<TreeNode> permutations = getChilden(node);
                LinkedList<Long> current = new LinkedList<Long>();
                current.add(node.val);
                getPermutations(permutations, current);
                return result;
            }
        }
        

        我的解决方案。完美运行。

        【讨论】:

        • 除了代码之外,您可能还需要添加一些说明。
        【解决方案9】:

        这是我的 Python 解决方案,其中包含大量解释。

        我们通过从该位置的一组可能选择中为每个位置选择一个节点来从左到右构建每个数组。我们将节点值添加到路径中,并将节点的子节点(如果有)添加到可能性列表中,然后进一步递归。当没有其他选择时,我们有一个候选数组。为了生成其余的数组,我们回溯,直到我们可以做出不同的选择并再次递归。

        关键是使用合适的数据结构来保存可能性。列表有效,但在回溯时必须将节点放回先前的位置(顺序很重要,因为我们添加了必须在节点之后访问的节点的子节点)。从列表中插入和删除需要线性时间。一套不起作用,因为它不维持秩序。 dict 效果最好,因为 Python 字典会记住插入顺序,并且所有操作都在恒定时间内运行。

        def bst_seq(root: TreeNode) -> list[list[int]]:
            def _loop(choices: MutableMapping[TreeNode, bool], path: list[int], result: list[list[int]]) -> None:
                if not choices:
                    result.append([*path])
                else:
                    # Take a snapshot of the keys to avoid concurrent modification exception
                    for choice in list(choices.keys()):
                        del choices[choice]
                        children = list(filter(None, [choice.left, choice.right]))
                        for child in children:
                            choices[child] = False
                        path.append(choice.val)
                        _loop(choices, path, result)
                        path.pop()
                        choices[choice] = False
                        for child in children:
                            del choices[child]
        
            result = []
            _loop({root: False}, [], result)
            return result
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-10-07
          • 1970-01-01
          • 2021-12-28
          • 2022-01-08
          • 2021-01-29
          • 1970-01-01
          • 2021-10-23
          相关资源
          最近更新 更多