Подтвердить что ты не робот

Печать BFS (двоичное дерево) в порядке уровня с _специфическим форматированием_

Начнем с того, что этот вопрос не является дубликом этого, но основывается на нем.

Взяв дерево в этом вопросе в качестве примера,

    1 
   / \
  2   3
 /   / \
4   5   6

Как бы вы изменили свою программу для ее печати,

1
2 3
4 5 6

а не общий

1 
2 
3 
4 
5 
6

В основном я ищу интуицию на самом эффективном способе сделать это - у меня есть метод, включающий добавление результата в список, а затем его прокрутку. Более эффективным способом может быть сохранение последнего элемента на каждом уровне по мере его всплывания и последующая распечатка новой строки.

Идеи?

4b9b3361

Ответ 1

Просто создайте один уровень за раз, например:

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 (и если вы после С++-решений, совершенно аналогично, std::vector, используя просто push_back для его создания, а цикл для его использования более эффективен, чем std:: deque). Поскольку все производственные операции происходят сначала, то вся итерация (или потребление), интересная альтернативная , если память жестко ограничена, может заключаться в том, чтобы использовать список в любом случае для представления каждого уровня, затем .reverse он перед вами начать с его потребления (с вызовами .pop). У меня нет больших деревьев для проверки по измерениям, но я подозреваю, что этот подход все равно будет быстрее (и на самом деле менее трудоемким), чем deque (если предположить, что базовая реализация списка [[или std::vector]] фактически перерабатывает память после нескольких вызовов на pop [[или pop_back]] - и с тем же предположением для deque, конечно; -).

Ответ 2

Похоже на обход в ширину для меня.

Обход первого пути реализуется с помощью queue. Здесь просто добавьте в очередь специальный токен, указывающий, что должна быть напечатана новая строка. Каждый раз, когда маркер найден, печатайте новую строку и повторно вставляйте токен в очередь (в конце - это определение очереди).

Запустите алгоритм с очередью, содержащей корень, за которой следует специальный токен новой строки.

Ответ 3

Это первый поиск по ширине, поэтому вы можете использовать очередь и рекурсивно сделать это простым и компактным способом...

# 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)

Ответ 4

почему бы не оставить посланника в очереди и проверить, когда обрабатываются все узлы текущего уровня.

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);
    }
}

Ответ 5

Мое решение похоже на Alex Martelli's, но я отделяю обход структуры данных от обработки структуры данных. Я поместил мясо кода в 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

Ответ 6

Простая версия, основанная на первом поиске хлеба. Этот код применим для графиков в целом и может также использоваться для двоичных деревьев.

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
>>> 

Другая версия, основанная на рекурсии, которая специфична для двоичного дерева

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 

Ответ 7

Здесь мой код печатает уровень дерева по уровню, а также вверх дном.

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

это причина, почему в этом массиве была нужна своп-часть.

Ответ 8

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()

Ответ 9

Следующий код будет печатать каждый уровень двоичного дерева в новой строке:

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

Для тех, кто просто интересуется визуализацией бинарных деревьев (и не столько в теории, лежащей в основе поиска по ширине), в binarytree. Применительно к примеру, указанному в вопросе,

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

Версия, которая не требует дополнительного хранения:

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 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. извините за код С++, я пока еще не очень уверен в Python.