使用_specific formatting_以级别顺序打印BFS(二叉树)

时间:2009-12-12 22:04:00

标签: python algorithm binary-tree breadth-first-search

首先,这个问题不是this one的重复,而是建立在它上面。

以该问题中的树为例,

    1 
   / \
  2   3
 /   / \
4   5   6

如何修改程序以进行打印,

1
2 3
4 5 6

而不是一般的

1 
2 
3 
4 
5 
6

我基本上是以最有效的方式寻找直觉 - 我有一个方法,包括将结果附加到列表,然后循环遍历它。一种更有效的方法可能是在弹出每个级别时存储最后一个元素,然后打印出一个新行。

想法?

16 个答案:

答案 0 :(得分:61)

一次只建立一个级别,例如:

class Node(object):
  def __init__(self, value, left=None, right=None):
    self.value = value
    self.left = left
    self.right = right

def traverse(rootnode):
  thislevel = [rootnode]
  while thislevel:
    nextlevel = list()
    for n in thislevel:
      print n.value,
      if n.left: nextlevel.append(n.left)
      if n.right: nextlevel.append(n.right)
    print
    thislevel = nextlevel

t = Node(1, Node(2, Node(4, Node(7))), Node(3, Node(5), Node(6)))

traverse(t)

编辑:如果你渴望在最大消耗的“辅助”内存中获得一点点保存(永远不会同时拥有所有这个级别以及这种“辅助”内存中的下一级别),你可以当然使用collection.deque而不是list,并且随时使用当前级别(通过popleft),而不是简单地循环。一次创建一个级别(当你消耗 - 或迭代 - 前一个级别)的想法保持不变 - 当你需要区分级别时,它比使用单个大型双端加上辅助信息更直接(例如深度或给定级别中剩余的节点数量。)

但是,仅附加到(并循环而不是“消耗”)的列表比deque更有效(如果您使用的是C ++解决方案,那么类似于std :: vector仅使用push_back构建它,然后使用它循环,比std :: deque更有效。由于所有生成都是先发生,然后所有迭代(或消耗),一个有趣的替代如果内存受到严格限制,可能是使用列表来表示每个级别,然后.reverse它在开始使用它之前(使用.pop调用) - 我没有大型树木来测量,但我怀疑这种方法仍然比{{更快(实际上更少耗费内存) 1}}(假设list [[或std :: vector]]的底层实现在对deque [[或pop]]进行几次调用之后实际上会回收内存 - 并且使用相同的当然,对于双端空格的假设; - )。

答案 1 :(得分:9)

对我来说听起来像breadth-first traversal

使用queue实现广度优先遍历。在这里,只需在队列中插入一个特殊标记,表示必须打印换行符。每次找到令牌时,打印换行符并在队列中重新插入令牌(最后 - 这是队列的定义)。

使用包含root后跟特殊换行符号的队列启动算法。

答案 2 :(得分:4)

这是广泛的第一次搜索,因此您可以使用队列并以简单紧凑的方式递归地执行此操作...

# built-in data structure we can use as a queue
from collections import deque

def print_level_order(head, queue = deque()):
    if head is None:
        return
    print head.data
    [queue.append(node) for node in [head.left, head.right] if node]
    if queue:
        print_level_order(queue.popleft(), queue)

答案 3 :(得分:3)

为什么不将Sentinel保留在队列中并检查当前级别中的所有节点何时被处理。

public void printLevel(Node n) {
    Queue<Integer> q = new ArrayBlockingQueue<Integer>();
    Node sentinal = new Node(-1);
    q.put(n);
    q.put(sentinal);
    while(q.size() > 0) {
        n = q.poll();
        System.out.println(n.value + " "); 
        if (n == sentinal && q.size() > 0) {
           q.put(sentinal); //push at the end again for next level
           System.out.println();
        }
        if (q.left != null) q.put(n.left);
        if (q.right != null) q.put(n.right);
    }
}

答案 4 :(得分:3)

我的解决方案类似于Alex Martelli,但我将数据结构的遍历与处理数据结构分开。我将代码的内容放入iterLayers中,以保持printByLayer的简短和甜蜜。

from collections import deque

class Node:
    def __init__(self, val, lc=None, rc=None):
        self.val = val
        self.lc = lc
        self.rc = rc

    def iterLayers(self):
        q = deque()
        q.append(self)
        def layerIterator(layerSize):
            for i in xrange(layerSize):
                n = q.popleft()
                if n.lc: q.append(n.lc)
                if n.rc: q.append(n.rc)
                yield n.val
        while (q):
            yield layerIterator(len(q))

    def printByLayer(self):
        for layer in self.iterLayers():
            print ' '.join([str(v) for v in layer])

root = Node(1, Node(2, Node(4, Node(7))), Node(3, Node(5), Node(6)))
root.printByLayer()

在运行时打印以下内容:

1
2 3
4 5 6
7

答案 5 :(得分:1)

基于面包优先搜索的简单版本,此代码一般适用于图形,也可用于二叉树。

def printBfsLevels(graph,start):
  queue=[start]
  path=[]
  currLevel=1
  levelMembers=1
  height=[(0,start)]
  childCount=0
  print queue
  while queue:
    visNode=queue.pop(0)
    if visNode not in path:
      if  levelMembers==0:
        levelMembers=childCount
        childCount=0
        currLevel=currLevel+1
      queue=queue+graph.get(visNode,[])
      if levelMembers > 0:
        levelMembers=levelMembers-1
        for node in graph.get(visNode,[]):
          childCount=childCount+1
          height.append((currLevel,node))
      path=path+[visNode]

  prevLevel=None

  for v,k in sorted(height):
        if prevLevel!=v:
          if prevLevel!=None:
            print "\n"
        prevLevel=v
        print k,
  return height

g={1: [2, 3,6], 2: [4, 5], 3: [6, 7],4:[8,9,13]}
printBfsLevels(g,1)


>>> 
[1]
1 

2 3 6 

4 5 6 7 

8 9 13
>>> 

另一个基于Recursion的版本,它特定于二叉树

class BinTree:
  "Node in a binary tree"
  def __init__(self,val,leftChild=None,rightChild=None,root=None):
    self.val=val
    self.leftChild=leftChild
    self.rightChild=rightChild
    self.root=root
    if not leftChild and not rightChild:
      self.isExternal=True

  def getChildren(self,node):
    children=[]
    if node.isExternal:
      return []
    if node.leftChild:
      children.append(node.leftChild)
    if node.rightChild:
      children.append(node.rightChild)
    return children

  @staticmethod
  def createTree(tupleList):
    "Creates a Binary tree Object from a given Tuple List"
    Nodes={}
    root=None
    for item in treeRep:
      if not root:
        root=BinTree(item[0])
        root.isExternal=False
        Nodes[item[0]]=root
        root.root=root
        root.leftChild=BinTree(item[1],root=root)
        Nodes[item[1]]=root.leftChild
        root.rightChild=BinTree(item[2],root=root)
        Nodes[item[2]]=root.rightChild
      else:
        CurrentParent=Nodes[item[0]]
        CurrentParent.isExternal=False
        CurrentParent.leftChild=BinTree(item[1],root=root)
        Nodes[item[1]]=CurrentParent.leftChild
        CurrentParent.rightChild=BinTree(item[2],root=root)
        Nodes[item[2]]=CurrentParent.rightChild
    root.nodeDict=Nodes
    return root

  def printBfsLevels(self,levels=None):
    if levels==None:
      levels=[self]
    nextLevel=[]
    for node in levels:
      print node.val,
    for node in levels:
      nextLevel.extend(node.getChildren(node))
    print '\n'
    if nextLevel:
      node.printBfsLevels(nextLevel)  


##       1
##     2     3
##   4   5  6  7
##  8

treeRep = [(1,2,3),(2,4,5),(3,6,7),(4,8,None)]
tree= BinTree.createTree(treeRep)
tree.printBfsLevels()

>>> 
1 

2 3 

4 5 6 7 

8 None 

答案 6 :(得分:1)

这里我的代码按级别和颠倒打印树

int counter=0;// to count the toatl no. of elments in the tree

void tree::print_treeupsidedown_levelbylevel(int *array)
{
    int j=2;  
    int next=j;
    int temp=0;
    while(j<2*counter)
    {
        if(array[j]==0)
        break;

        while(array[j]!=-1)
        {
            j++;
        }

        for(int i=next,k=j-1 ;i<k; i++,k--)
        {
            temp=array[i];
            array[i]=array[k];
            array[k]=temp;
        }

        next=j+1;
        j++;
    }

    for(int i=2*counter-1;i>=0;i--)
    {
        if(array[i]>0)
        printf("%d ",array[i]);

        if(array[i]==-1)
        printf("\n");
    }
}

void tree::BFS()
{
    queue<node *>p;

    node *leaf=root;

    int array[2*counter];
    for(int i=0;i<2*counter;i++)
    array[i]=0;

    int count=0;

    node *newline=new node; //this node helps to print a tree level by level
    newline->val=0;
    newline->left=NULL;
    newline->right=NULL;
    newline->parent=NULL;

    p.push(leaf);
    p.push(newline);

    while(!p.empty())
    {
        leaf=p.front();
        if(leaf==newline)
        {
            printf("\n");
            p.pop();
            if(!p.empty())
            p.push(newline);
            array[count++]=-1;
        }
        else
        {
            cout<<leaf->val<<" ";
            array[count++]=leaf->val;

            if(leaf->left!=NULL)
            {
                p.push(leaf->left);
            }
            if(leaf->right!=NULL)
            {
                p.push(leaf->right);
            }
            p.pop();
        }
    }
    delete newline;

    print_treeupsidedown_levelbylevel(array);
}

在我的代码中,函数BFS按级别打印树级,它还填充int数组中的数据以便将树颠倒打印。 (注意在颠倒打印树时会使用一些交换,这有助于实现我们的目标)。如果没有执行交换,那么对于像

这样的树
                    8
                   /  \
                  1    12
                  \     /
                   5   9
                 /   \
                4     7
                     /
                    6

o / p将是

  6
  7 4
  9 5
  12 1
  8

但是o / p必须是

  6
  4 7
  5 9
  1 12
  8

这就是在该阵列中需要交换部分的原因。

答案 7 :(得分:1)

class TNode:
  def __init__(self, data, left=None, right=None):
    self.data = data
    self.left = left
    self.right = right

class BST:
  def __init__(self, root):
    self._root = root

  def bfs(self):
    list = [self._root]
    while len(list) > 0:
        print [e.data for e in list]
        list = [e.left for e in list if e.left] + \
               [e.right for e in list if e.right]
bst = BST(TNode(1, TNode(2, TNode(4), TNode(5)), TNode(3, TNode(6), TNode(7))))
bst.bfs()

答案 8 :(得分:1)

除了为python 3进行了修改外,这与Alex Martelli提供的代码基本相同。

class Node(object):
  def __init__(self, value, left=None, right=None):
    self.value = value
    self.left = left
    self.right = right

def traverse(rootnode):
  thislevel = [rootnode]
  while thislevel:
    nextlevel = list()
    for n in thislevel:
      print (n.value,' ', end=''),
      if n.left: nextlevel.append(n.left)
      if n.right: nextlevel.append(n.right)
    print(" ")
    thislevel = nextlevel

t = Node(1, Node(2, Node(4, Node(7))), Node(3, Node(5), Node(6)))

traverse(t)

答案 9 :(得分:0)

以下代码将每个级别的二叉树打印到新行:

public void printbylevel(node root){
    int counter = 0, level = 0;
    Queue<node> qu = new LinkedList<node>();

    qu.add(root);
    level = 1;
    if(root.child1 != null)counter++;
    if(root.child2 != null)counter++;

     while(!qu.isEmpty()){
         node temp = qu.remove();
         level--;
         System.out.print(temp.val);
         if(level == 0 ){
             System.out.println();

             level = counter;
             counter = 0;
         }
        if(temp.child1 != null){
            qu.add(temp.child1);
            counter++;
        }
        if(temp.child2 != null){
            qu.add(temp.child2);
            counter++;
        }
     }
}

答案 10 :(得分:0)

对于那些只对二叉树可视化感兴趣的人(而不是广度优先搜索背后的理论),binarytree包中有一个show函数。适用于问题中给出的例子,

from binarytree import Node, show

root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.right.left = Node(5)
root.right.right = Node(6)

show(root)

打印

    1    
   / \   
  2   3  
 /   / \ 
4   5   6

答案 11 :(得分:0)

我认为您期望的是在每个级别上打印节点,这些节点用空格或逗号分隔,而级别则用换行分隔。这就是我将算法编码的方式。我们知道,当我们在图或树上进行广度优先搜索并将节点插入队列时,队列中出现的所有节点将与上一个级别处于同一级别,或者处于父级别的新级别+ 1,没别的。

因此,当您处于某个级别时,请继续打印出节点值,并且一旦发现节点级别增加1,则在开始打印该级别的所有节点之前,请插入新行。 / p>

这是我的代码,它不占用太多内存,并且只需要队列就可以了。

假设树从根开始。

queue = [(root, 0)]  # Store the node along with its level. 
prev = 0
while queue:
  node, level = queue.pop(0)
  if level == prev:
    print(node.val, end = "")
  else:
    print()
    print(node.val, end = "")
  if node.left:
    queue.append((node.left, level + 1))
  if node.right:
    queue.append((node.right, level + 1))
  prev = level

最后,您需要的是所有处理的队列。

答案 12 :(得分:0)

这是一个Python要点,它按级别打印树。其背后的思想是使用BFS并保留一个级别标记整数,该整数标记级别的最后节点。这类似于Naresh的哨兵方法,但是不需要在队列中插入哨兵,因为这是通过级别标记完成的。

此算法的空间复杂度为O(2 tree_height

# Print tree by levels - using BFS
# Time complexity of O(n)
# Space complexity: O(2^tree_height)

from collections import deque

class Node:
    def __init__(self, data, left=None, right=None):
        self.data = data
        self.left = left
        self.right = right

def print_levels_tree(root: Node):
    q = deque()
    q.append(root)
    level, level_marker = 0, 1
    while q:
        if (level_marker == 0):
            level, level_marker = level + 1, len(q)
            print("", end = '\n')
        level_marker -= 1

        node = q.popleft()

        if (node is None):
            continue

        print(node.data, " ", end = '')

        q.append(node.left)
        q.append(node.right)


# Some examples
tree = Node(19, Node(7, Node(3), Node(11)), Node(19)) 
print_levels_tree(tree)

left = Node(7, Node(3, Node(2), Node(5)), Node(11, None, Node(17, Node(13))))
tree = Node(19, left, Node(43))
print_levels_tree(tree)

left = Node(7, Node(3, Node(2), Node(5)), Node(11, None, Node(17, Node(13))))
right = Node(43, Node(23, None, Node(37, Node(29, None, Node(31)), Node(41))), Node(47, None, Node(53)) )
tree = Node(19, left, right)
print_levels_tree(tree)

哪个打印如下:

19  
7  43  
3  11  23  47  
2  5  17  37  53  

如果您想使用\t分隔符,它将类似于:

19  
7   43  
3   11  23  47  
2   5   17  37  53  

此要旨可从https://gist.github.com/lopespm/993f0af88cf30b7f8c9e17982518b71b

获得。

答案 13 :(得分:0)

使用队列(恒定的空间复杂度)

def bfs(self, queue=None):
    
    if(queue is None):
        queue = deque([self.root])
        
    elif(not queue):
        return

    currNode  = queue.pop()
    
    print(currNode.data)
    
    if currNode.left:
        queue.appendleft(currNode.left)
        
    if currNode.right:
        queue.appendleft(currNode.right)
    
    self.bfs(queue)

答案 14 :(得分:0)

BT / BST 的层序遍历

在 BASH 中,您只需在 .txt 文件中创建/维护节点,即可轻松实现 BT/BST,而无需使用任何哈希数组/映射等。

这是我的小窍门。 2 个小函数 print_level()print_root() 就可以了。脚本可以从任何给定用户指定的根节点打印任何级别的节点信息。

#!/bin/bash

print_level(){
 echo $cs

 call_flag=0
 for row in $cs; do if [[ `grep "^${row}:" ~/btree-data.txt` ]]; then call_flag=1; fi; done

 if [[ $call_flag == 1 ]]; then rs="$cs"; print_root; fi
}

print_root(){
  if [[ ${flag} == 0 ]]; then echo "${rs}"; flag=1; fi

  cs=""
  for r in $rs; do cs+="$(grep "^${r}:" ~/btree-data.txt | cut -d':' -f2 | sed "s/,/ /") "; done
  print_level
  echo
}

flag=0
echo -e "\n\n
# It reads a file ~/btree-data.txt (as shown below). 
# -- One can easily implement CRUD operation to maintain BT/BST using a .txt file. 
#
# -- Here .txt file ROW's Format is: NODE:left_leaf_node,right_leaf_node
#
# 100:50,200
# 50:20,60
# 200:300
# 300:350
# 60:55,75
# 20:10,25
# 10:5
# 350:320
# 25:27
# 5:2
# 320:310,325
# 2:3

## ----------------- BTree Diagram -------------------------------------------------------------------------
#                                                             100
#                                             50                               200
#                              20                       60                             300
#                        10          25             55       75                                      350
#                    5                     27                                                  320
#              2                                                                           310     325
#                 3
## ----------------- BTree Diagram -------------------------------------------------------------------------
\n\n"

echo -ne "\n-- Enter which root: "; read rs

print_root
echo -e "\n\n"

跑步时吐口水

$ ./btree-bash-level-print.sh

# It reads a file ~/btree-data.txt (as shown below). 
# -- One can easily implement CRUD operation to maintain BT/BST using a .txt file. 
#
# -- Here .txt file ROW's Format is: NODE:left_leaf_node,right_leaf_node
#
# 100:50,200
# 50:20,60
# 200:300
# 300:350
# 60:55,75
# 20:10,25
# 10:5
# 350:320
# 25:27
# 5:2
# 320:310,325
# 2:3

## ----------------- BTree Diagram -------------------------------------------------------------------------
#                                                             100
#                                             50                               200
#                              20                       60                             300
#                        10          25             55       75                                      350
#                    5                     27                                                  320
#              2                                                                           310     325
#                 3
## ----------------- BTree Diagram -------------------------------------------------------------------------




-- Enter which root: 100
100
50 200
20 60 300
10 25 55 75 350
5 27 320
2 310 325
3

对于root (300)

-- Enter which root: 300
300
350
320
310 325

对于root (50)

-- Enter which root: 50
50
20 60
10 25 55 75
5 27
2
3

答案 15 :(得分:-1)

不需要额外存储空间的版本:

std::deque<Node> bfs;
bfs.push_back(start);
int nodesInThisLayer = 1;
int nodesInNextLayer = 0;
while (!bfs.empty()) {
    Node front = bfs.front();
    bfs.pop_front();
    for (/*iterate over front's children*/) {
        ++nodesInNextLayer;
        nodes.push_back(child);
    }
    std::cout << node.value;
    if (0 == --nodesInThisLayer) {
        std::cout << std::endl;
        nodesInThisLayer = nodesInNextLayer; 
        nodesInNextLayer = 0;
    } else {
        std::cout << " ";
    }
}

P.S。对不起C ++代码,我还不是很熟悉Python。