如何使用级别顺序遍历序列构造二叉树

时间:2014-05-20 07:57:05

标签: algorithm binary-tree

如何使用级别顺序遍历序列构造二叉树,例如从序列{1,2,3,#,#,#,4,#,#,5}构造二叉树,我们可以构造这样的二叉树: / p>

     1
    / \
   2   3
      /
     4
      \
       5

其中'#'表示下面没有节点的路径终结符。

最后,我用c ++

实现了Pham Trung的算法
struct TreeNode
{
    TreeNode *left;
    TreeNode *right;
    int val;

    TreeNode(int x): left(NULL), right(NULL), val(x) {}
};
TreeNode *build_tree(char nodes[], int n)
{
    TreeNode *root = new TreeNode(nodes[0] - '0'); 
    queue<TreeNode*> q;
    bool is_left = true;
    TreeNode *cur = NULL;
    q.push(root);

    for (int i = 1; i < n; i++) {
        TreeNode *node = NULL;
        if (nodes[i] != '#') {
            node = new TreeNode(nodes[i] - '0');
            q.push(node);
        }

        if (is_left) {
            cur = q.front();
            q.pop();
            cur->left = node;
            is_left = false;
        } else {
            cur->right = node;
            is_left = true;
        }
    }

    return root;
}

4 个答案:

答案 0 :(得分:7)

假设使用基于0的索引的数组int[]data,我们有一个简单的函数来获取子项:

  • 左子女

    int getLeftChild(int index){
       if(index*2 + 1 >= data.length)
          return -1;// -1 Means out of bound
       return data[(index*2) + 1];
    }
    
  • 正确的孩子

    int getRightChild(int index){
       if(index*2 + 2 >= data.length)
          return -1;// -1 Means out of bound           
       return data[(index*2) + 2];
    }
    

编辑:  好的,所以通过维护队列,我们​​可以构建这个二叉树。

我们使用队列来维护那些尚未处理的节点。

使用变量计数来跟踪为当前节点添加的子项数。

首先,创建一个根节点,将其指定为当前节点。 因此,从索引1开始(索引0是根),当计数为0时,我们将此节点添加为当前节点的左子节点。 增加数量。如果此节点不是&#39;#&#39;,请将其添加到队列中。

移动到下一个索引,计数为1,因此我们将其添加为当前节点的右子节点,将count重置为0并更新当前节点(通过将当前节点指定为队列中的第一个元素)。如果此节点不是&#39;#&#39;,请将其添加到队列中。

     int count = 0;
     Queue q = new Queue();
     q.add(new Node(data[0]);
     Node cur = null;
     for(int i = 1; i < data.length; i++){
        Node node = new Node(data[i]);
        if(count == 0){
           cur = q.dequeue();           
        }
        if(count==0){
          count++;
          cur.leftChild = node;
        }else {
          count = 0;
          cur.rightChild = node;
        }
        if(data[i] != '#'){
          q.enqueue(node);
        }
     }    



    class Node{
       int data;
       Node leftChild, rightChild;
    } 

答案 1 :(得分:0)

我们可以通过维护队列从级别顺序遍历构建此二叉树。队列用于维护那些尚未处理的节点。

  1. 使用变量count(索引变量)来跟踪为当前节点添加的子代数。

  2. 首先,创建一个根节点,将其分配为当前节点。所以从索引1开始 索引值为1表示将下一个值添加为左节点。 索引值2表示我们将下一个值添加为右节点,索引值2表示我们已添加左节点和右节点,然后对其余节点执行相同操作。

  3. 如果arr值为-1

3.a。如果索引值为1,即没有左节点,则更改索引​​变量以添加右节点。
3.b.如果索引值为2,即没有右节点,则对其余的节点重复此步骤。

static class Node{
    int data;
    Node left;
    Node right;
    Node(int d){
        data=d;
        left=null;
        right=null;
    }
}

公共静态节点constBT(int arr [],int n){

    Node root=null;
    Node curr=null;
    int index=0;
    Queue<Node> q=new LinkedList<>();
    for(int i=0;i<n;i++){

        if(root==null){
            root=new Node(arr[i]);
            q.add(root);
            curr=q.peek();
            index=1;
        }else{
            if(arr[i]==-1){

                 if(index==1)
                    index=2;
                else{
                    q.remove();
                    curr=q.peek();
                    index=1;

                }
            }

            else if(index==1){
                curr.left=new Node(arr[i]);
                q.add(curr.left);
                index=2;

            }else if(index==2){
                curr.right=new Node(arr[i]);
                q.add(curr.right);
                q.remove();
                curr=q.peek();
                index=1;
            }

        }
    }
    return root;
}

答案 2 :(得分:0)

我的方法类似于Pham Trung,但很直观。我们将维护给定数据的节点数组,而不使用队列。我们将使用队列对BFS进行反向工程。因为树的BFS基本上是其级别顺序遍历(LOT)。

重要的是要注意,对于LOT,我们应该具有节点的NULL子代,而从LOT重构Tree才是可能的。

在这种情况下,LOT:1,2,3,-1,-1,4,-1,-1,5
我用-1而不是'#'来表示NULL
树是

           1
        /    \
       2      3
      / \    /
    -1  -1  4
           / \
         -1   5

在这里,我们可以很容易地看到,当从BFS队列中弹出1时,它推了它的左孩子 (2)和右孩子(3)在队列中。类似地,对于2,它为两个孩子都推了-1(NULL)。并且该过程继续进行。
因此,我们可以按照以下伪代码生成以LOT [0]

为根的树
j = 1
For every node in LOT:
  if n<=j: break
  if node  != NULL:
    make LOT[j] left child of node 
    if n<=j+1: break
    make LOT[j+1]  right child of node
  j <- j+2

最后,相同的C ++代码
类声明和预订遍历

class Node{
public:
    int val;
    Node* lft, *rgt;
    Node(int x ):val(x) {lft=rgt=nullptr;}
};


void preorder(Node* root) {
    if(!root)   return;
    cout<<root->val<<" ";
    preorder(root->lft);
    preorder(root->rgt);
}

从LOT Logic恢复树


int main(){
    int arr[] = {1,2,3,-1,-1,4,-1,-1,5};
    int n = sizeof(arr)/sizeof(int);
    Node* brr[n];
    for(int i=0;i<n;i++)  {
        if(arr[i]==-1)  brr[i] = nullptr;
        else brr[i] = new Node(arr[i]);
    }
    for(int i=0,j=1;j<n;i++) {
        if(!brr[i]) continue;
        brr[i]->lft = brr[j++];
        if(j<n) brr[i]->rgt = brr[j++];
    }
    preorder(brr[0]);
}
    

输出1 2 3 4 5

答案 3 :(得分:0)

以上所有方法假设逆向工程Level order traversal可以根据JUST level order input创建BST

但是这是错误的,除非从中获取输入级别顺序的树完全填充了所有级别,否则您无法创建仅具有级别顺序输入的 BST。即 2^0, 2^1, 2^2, 2^3....=> 1, 2, 4, 8, 16...没有节点

尝试对于{1,2,3,2,#,4,#,#,5},上述所有方法都失败因为他们假设下一级的第一个元素必须是左/右上一级最左边父级的子级。

由于在 OP 的情况下4 是第 3 级的唯一节点下一级元素 5 被分配了 strong> 到 4 作为右孩子

但是如果在第 3 级有 {2, #, 4, #} 那么 5 将被分配为 2 的右孩子。这是不正确的。 因此,我们不能仅从级别顺序输入重建 BST。除非树是完整的/完美的。

下面的代码使用范围来确定子级实际上属于当前父级还是父级兄弟姐妹。
这种方法完全使用了 BFS 代码,但是除了队列中的节点信息之外,还保留了子范围作为元组/列表/字典

import sys
import queue

def levelToLevelBST(level):
    if len(level) == 0:
        return
    root = Node(level[0])
    q = Queue()
    
    # Preserve current node's child-bounds (lower and higher) 
    # (root can accommodate any two child ie bounds = -INF to +INF)
    q.put([root, -sys.maxsize, sys.maxsize])
    
    i = 1
    while (q.qsize() and i < len(level)):
        curr, lo, hi = q.get()
        if (inRange(level[i], lo, curr.val)): 
            curr.left = Node(level[i])
            q.push([curr.left, lo, curr.val])
            i+=1
        elif (inRange(level[i], curr.val, hi)): 
            curr.right = Node(level[i])
            q.push([curr.right, curr.val, hi]) 
            i+=1
        else:
            pass # not within bounds so, leave it for next
    return root

def inRange(val, lo, hi):
    return val >= lo and val <= hi

其他方法将是使用 DFS 的普通插入来查找插入点:

为什么插入可以用于级别顺序输入以重新创建从中获取级别顺序的精确 BST?
因为,由于输入本身是级别顺序(逐级)的属性,级别顺序输入导致每次插入仅在当前级别完全完成时在新级别创建新节点。
因此,除非当前层被填满并且不会在该层添加更多节点,否则我们不会超调到更深的深度。

递归:

def insert(root , data):
    if root == None:
        return Node(data)
    if(data <= root.val):
        root.left = insert(root.left, data)
    else:
        root.right = insert(root.right, data)
    return root   

迭代:

    def insert(self, val):
        if self.root == None:
            self.root = Node(val)
            return
        node = self.root
        while True:
            if val <= node.val:
                if node.left == None:
                    node.left = Node(val)
                    return
                node = node.left
            else:
                if node.right == None:
                    node.right = Node(val)
                    return
                node = node.right