二叉树级别顺序遍历

时间:2011-09-05 08:06:17

标签: algorithm

  

三种类型的树遍历是有序,预订和后置顺序。

     

第四种不经常使用的遍历是水平顺序遍历。在一个   水平顺序traveresal,深度“d”的所有节点之前被处理   深度为d + 1的任何节点。级别顺序遍历与另一个不同   遍历,因为它不是递归的;使用队列,   而不是隐含的递归堆栈。

我对上述文字摘要的问题是

  1. 为什么级别订单遍历不是递归完成的?
  2. 如何在级别顺序遍历中使用队列?使用伪代码澄清请求会有所帮助。
  3. 谢谢!

8 个答案:

答案 0 :(得分:14)

Level order traversal实际上是BFS,本质上不是递归的。它使用Queue代替Stack来保存应打开的下一个顶点。其原因在于此遍历,您希望以FIFO顺序打开节点,而不是通过递归获得的LIFO顺序

正如我所提到的,级别顺序实际上是BFS,其[BFS]伪代码[取自维基百科]是:

1  procedure BFS(Graph,source):
2      create a queue Q
3      enqueue source onto Q
4      mark source
5      while Q is not empty:
6          dequeue an item from Q into v
7          for each edge e incident on v in Graph:
8              let w be the other end of e
9              if w is not marked:
10                 mark w
11                 enqueue w onto Q

(*)在树中,不需要标记顶点,因为您无法到达2个不同路径中的同一节点。

答案 1 :(得分:4)

void levelorder(Node *n)
{    queue < Node * >q;

     q.push(n);


     while(!q.empty())
     {
             Node *node = q.front();
             cout<<node->value;
             q.pop();
             if(node->left != NULL)
             q.push(node->left);
             if (node->right != NULL)
             q.push(node->right);

     }

}

答案 2 :(得分:0)

即使使用代码段,您也会在Wikipedia中找到一个很好的概述。

答案 3 :(得分:0)

我使用地图来解决这个问题,而不是队列。如果你有兴趣,看看吧。当我进行后序遍历时,我会保持每个节点的定位深度,并使用此深度作为地图中的关键字来收集同一级别的值

class Solution { public: map<int, vector<int> > levelValues; void recursivePrint(TreeNode *root, int depth){ if(root == NULL) return; if(levelValues.count(root->val) == 0) levelValues.insert(make_pair(depth, vector<int>())); levelValues[depth].push_back(root->val); recursivePrint(root->left, depth+1); recursivePrint(root->right, depth+1); } vector<vector<int> > levelOrder(TreeNode *root) { recursivePrint(root, 1); vector<vector<int> > result; for(map<int,vector<int> >::iterator it = levelValues.begin(); it!= levelValues.end(); ++it){ result.push_back(it->second); } return result; } };

可以在此处找到整个解决方案 - http://ideone.com/zFMGKU 解决方案返回向量向量,每个内向量以正确的顺序包含树中的元素。

您可以尝试在此处解决问题 - https://oj.leetcode.com/problems/binary-tree-level-order-traversal/

而且,正如您所看到的,我们也可以在与队列解决方案相同的时间和空间复杂度下递归执行此操作!

答案 4 :(得分:0)

https://github.com/arun2pratap/data-structure/blob/master/src/main/java/com/ds/tree/binarytree/BinaryTree.java

for complete可以留意以上链接。

 public void levelOrderTreeTraversal(List<Node<T>> nodes){
    if(nodes == null || nodes.isEmpty()){
        return;
    }
    List<Node<T>> levelNodes = new ArrayList<>();
    nodes.stream().forEach(node -> {
        if(node != null) {
            System.out.print(" " + node.value);
            levelNodes.add(node.left);
            levelNodes.add(node.right);
        }
    });
    System.out.println("");
    levelOrderTreeTraversal(levelNodes);
}

也可以退房 http://www.geeksforgeeks.org/

在这里,您将找到几乎所有与数据结构相关的答案。

答案 5 :(得分:0)

queue实现的层序遍历

# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

def levelOrder(root: TreeNode) -> List[List[int]]:
    res = []    # store the node value

    queue = [root]
    while queue:
        node = queue.pop()
        
        # visit the node
        res.append(node.val)
        
        if node.left:
            queue.insert(0, node.left)
        if node.right:
            queue.insert(0, node.right)
            
    return res

递归实现也是可能的。但是,它需要提前知道根的最大深度。

def levelOrder(root: TreeNode) -> List[int]:
    res = []
    max_depth = maxDepth(root)
    for i in range(max_depth):
        # level start from 0 to max_depth-1
        visitLevel(root, i, action)
    return res

def visitLevel(root:TreeNode, level:int, res: List):
    if not root:
        return 
    if level==0:
        res.append(node.val)
    else:
        self.visitLevel(root.left, level-1, res)
        self.visitLevel(root.right, level-1, res)
            

def maxDepth(root: TreeNode) -> int:
    if not root:
        return 0
    if not root.left and not root.right:
        return 1
    return max([ maxDepth(root.left), maxDepth(root.right)]) + 1

答案 6 :(得分:0)

<块引用>

我对上述文本片段的问题是

  1. 为什么层序遍历不是递归完成的?
  2. 如何在层序遍历中使用队列?使用伪代码请求澄清会有所帮助。

我认为从第二个问题开始实际上会更容易。一旦你理解了第二个问题的答案,你就会更好地准备理解第一个问题的答案。

层序遍历的工作原理

我认为理解层序遍历如何工作的最好方法是一步一步地执行,所以让我们这样做。

我们有一棵树。

enter image description here

我们想一层一层地遍历它。

enter image description here

因此,我们访问节点的顺序是 A B C D E F G

为此,我们使用队列。请记住,队列是先进先出 (FIFO)。我喜欢想象节点正在排队等待服务员处理。

enter image description here

让我们首先将第一个节点 A 放入队列。

enter image description here

好的。系好安全带。设置结束。我们即将开始潜水。

第一步是将 A 从队列中取出,以便对其进行处理。可是等等!在此之前,让我们将 A孩子BC 也放入队列中。

enter image description here

注意:此时 A 实际上不再在队列中。我把它变灰以试图传达这一点。如果我将它从图表中完全删除,就会很难想象故事后面发生的事情。

注意:A 正在由图表中服务台的服务员处理。在现实生活中,处理一个节点可能意味着很多事情。使用它来计算总和、发送短信、登录控制台等。脱离我图中的比喻,您可以告诉服务员您希望他们如何处理节点。

现在我们转到下一个节点。在这种情况下,B

我们对 A 执行相同的操作:1) 将子节点添加到行中,以及 2) 处理节点。

enter image description here

嘿,看看吧!看起来我们在这里所做的将让我们获得我们正在寻找的级别顺序遍历!让我们通过继续执行此步骤来向自己证明这一点。

一旦我们完成了 B,接下来就是 C。我们将 C 的孩子放在行的后面,然后处理 C

enter image description here

现在让我们看看接下来会发生什么。 D 是下一个。 D 没有任何孩子,所以我们不会在行的后面放置任何东西。我们只处理 D

enter image description here

然后对于 EFG 也是一样的。

为什么不递归完成

想象一下,如果我们使用 stack 而不是 queue 会发生什么。让我们回到刚刚访问 A 的地方。

enter image description here

这是我们使用堆栈时的样子。

enter image description here

现在,这位新服务员喜欢先服务最近的客户,而不是等待时间最长的客户,而不是“按顺序”。所以C是下一个,而不是B

这里是关键点。堆栈开始导致与队列不同的处理顺序。

像以前一样,我们添加 C 的孩子,然后处理 C。这次我们只是将它们添加到堆栈而不是队列中。

enter image description here

现在,下一步是什么?这位新服务员喜欢先为最近的客户提供服务(即我们使用的是堆栈),因此接下来是 G

我会在这里停止执行。关键是像用堆栈替换队列这样简单的事情实际上给了我们一个完全不同的执行顺序。不过,我鼓励您完成这一步。

您可能会想:“好吧……但问题是关于递归的。这与递归有什么关系?”好吧,当您使用递归时,会发生一些偷偷摸摸的事情。 从未对 s = new Stack() 之类的堆栈数据结构做过任何事情。但是,运行时使用调用堆栈。这最终在概念上与我上面所做的相似,因此不会给我们从级别顺序遍历中寻找的 A B C D E F G 排序。

答案 7 :(得分:0)

对于您的观点 1) 我们可以使用 Java 下面的代码进行递归顺序的层次遍历,我们没有使用任何树的库函数,都是用户定义的树和特定于树的函数 -

class Node
{
    int data;
    Node left, right;
    public Node(int item)
    {
        data = item;
        left = right = null;
    }

    boolean isLeaf() { return left == null ? right == null : false; }
}

public class BinaryTree {
    Node root;

    Queue<Node> nodeQueue = new ConcurrentLinkedDeque<>();

    public BinaryTree() {
        root = null;
    }

    public static void main(String args[]) {
        BinaryTree tree = new BinaryTree();
        tree.root = new Node(1);
        tree.root.left = new Node(2);
        tree.root.right = new Node(3);
        tree.root.left.left = new Node(4);
        tree.root.left.right = new Node(5);
        tree.root.right.left = new Node(6);
        tree.root.right.right = new Node(7);
        tree.root.right.left.left = new Node(8);
        tree.root.right.left.right = new Node(9);
        tree.printLevelOrder();
    }

    /*Level order traversal*/
    void printLevelOrder() {
        int h = height(root);
        int i;
        for (i = 1; i <= h; i++)
            printGivenLevel(root, i);
        System.out.println("\n");
    }

    void printGivenLevel(Node root, int level) {
        if (root == null)
            return;
        if (level == 1)
            System.out.print(root.data + " ");

        else if (level > 1) {
            printGivenLevel(root.left, level - 1);
            printGivenLevel(root.right, level - 1);
        }
    }

    /*Height of Binary tree*/
    int height(Node root) {
        if (root == null)
            return 0;
        else {
            int lHeight = height(root.left);
            int rHeight = height(root.right);

            if (lHeight > rHeight)
                return (lHeight + 1);
            else return (rHeight + 1);
        }
    }
} 

对于你的观点 2) 如果你想使用非递归函数,那么你可以使用队列如下函数-

public void levelOrder_traversal_nrec(Node node){
        System.out.println("Level order traversal !!! ");
        if(node == null){
            System.out.println("Tree is empty");
            return;
        }
         nodeQueue.add(node);
        while (!nodeQueue.isEmpty()){
            node = nodeQueue.remove();
            System.out.printf("%s ",node.data);
            if(node.left !=null)
                nodeQueue.add(node.left);
            if (node.right !=null)
                nodeQueue.add(node.right);
        }
        System.out.println("\n");
    }