web-dev-qa-db-fra.com

Impression de BFS (arbre binaire) dans l'ordre des niveaux avec une mise en forme spécifique

Pour commencer, cette question n'est pas un dup de celui-ci , mais se base sur elle.

Prenant l'arbre dans cette question à titre d'exemple,

    1 
   / \
  2   3
 /   / \
4   5   6

Comment modifieriez-vous votre programme pour l'imprimer ainsi,

1
2 3
4 5 6

plutôt que le général

1 
2 
3 
4 
5 
6

Je cherche fondamentalement des intuitions sur le moyen le plus efficace de le faire. J'ai une méthode qui consiste à ajouter le résultat à une liste, puis à le parcourir en boucle. Un moyen plus efficace pourrait consister à stocker le dernier élément de chaque niveau au fur et à mesure de son apparition, puis à imprimer une nouvelle ligne.

Des idées?

29
viksit

Il suffit de construire un niveau à la fois, par exemple:

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)

Edit : si vous souhaitez obtenir une petite économie de mémoire "auxiliaire" maximale consommée (ne jamais avoir simultanément tout ce niveau et le niveau suivant dans une telle mémoire "auxiliaire"), vous pouvez bien sûr utiliser collection.deque au lieu de list et consommez le niveau actuel au fur et à mesure (via popleft) au lieu de simplement boucler. L’idée de créer un niveau à la fois (à mesure que vous consommez - ou répétez-le - le précédent) reste intacte - lorsque vous devez distinguer les niveaux, c’est plus direct que d’utiliser un seul grand deque plus des informations auxiliaires ( tels que la profondeur ou le nombre de nœuds restant dans un niveau donné).

Cependant, une liste qui est seulement ajoutée à (et bouclée, plutôt que "consommée") est un peu plus efficace qu'un deque (et si vous recherchez des solutions C++, de manière similaire, un std :: vector utilisant seulement Push_back pour le construire, et une boucle pour ensuite l'utiliser, est plus efficace qu'un std :: deque). Puisque toute la production a lieu en premier, puis toute l'itération (ou la consommation), une alternative intéressante si memory est étroitement contraint peut être d'utiliser une liste pour représenter chaque niveau, puis .reverse le avant de commencer à le consommer (avec .pop appelle) - Je n'ai pas de grands arbres à vérifier par mesure, mais je soupçonne que cette approche serait toujours plus rapide (et consommerait en réalité moins de mémoire) que deque (en supposant que l'implémentation sous-jacente de list [[ou std :: vector]] recycle effectivement la mémoire après quelques appels à pop [[ou pop_back]] - et avec la même hypothèse pour deque, bien sûr ;-).

59
Alex Martelli

Cela ressemble à width-first traversal pour moi.

La traversée en largeur d'abord est implémentée avec un queue . Ici, insérez simplement dans la file d'attente un jeton spécial indiquant qu'une nouvelle ligne doit être imprimée. Chaque fois que le jeton est trouvé, imprimez une nouvelle ligne et réinsérez-le dans la file d'attente (à la fin, il s'agit de la définition d'une file d'attente).

Démarrez l'algorithme avec une file d'attente contenant la racine, suivie du jeton de nouvelle ligne spécial.

9
Pascal Cuoq

Il s'agit d'une première recherche étendue, vous pouvez donc utiliser une file d'attente et le faire de manière récursive de manière simple et compacte ...

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

Ma solution est similaire à celle d'Alex Martelli, mais je sépare la traversée de la structure de données du traitement de la structure de données. Je mets le contenu du code dans iterLayers pour que printByLayer soit court et agréable.

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

qui affiche les éléments suivants lors de l'exécution:

1
2 3
4 5 6
7
3
Ben Haynor

pourquoi ne pas garder sentinel en file d'attente et vérifier quand tous les nœuds du niveau actuel sont traités.

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

Il s’agit essentiellement du même code que celui fourni par Alex Martelli, sauf que celui-ci est modifié pour Python 3.

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)
1
grepit
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()
1
Swapneel Patil

Une version simple basée sur Bread First Search. Ce code est applicable aux graphiques en général et peut également être utilisé pour les arbres binaires.

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

Une autre version basée sur la récursivité, qui est spécifique à l’arbre binaire

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

Ici, mon code imprime l'arbre niveau par niveau, ainsi que la tête en bas.

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

Ici, dans mon code, la fonction BFS imprime l’arborescence niveau par niveau, qui remplit également les données dans un tableau int pour imprimer l’arbre à l'envers. (notez qu'il y a un peu d'échange utilisé lors de l'impression de l'arbre à l'envers, ce qui aide à atteindre notre objectif). Si la permutation n’est pas effectuée, alors pour un arbre comme

                    8
                   /  \
                  1    12
                  \     /
                   5   9
                 /   \
                4     7
                     /
                    6

o/p sera

  6
  7 4
  9 5
  12 1
  8

mais le o/p doit être

  6
  4 7
  5 9
  1 12
  8

c’est la raison pour laquelle il fallait échanger une partie dans ce tableau.

1
Sumit Kumar Saha

Le code suivant imprimera chaque niveau d'arborescence binaire dans une nouvelle ligne:

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++;
        }
     }
}
0
Ramy

Je pense que ce que vous attendez est d’imprimer les nœuds à chaque niveau, séparés par un espace ou une virgule et les niveaux séparés par une nouvelle ligne. Voici comment je coderais l'algorithme. Nous savons que lorsque nous effectuons une recherche approfondie sur un graphique ou une arborescence et que nous insérons les noeuds dans une file d'attente, tous les noeuds de la file d'attente sortant seront au même niveau que le précédent ou un nouveau niveau qui est le niveau parent. +1 et rien d'autre.

Ainsi, lorsque vous êtes à un niveau, continuez d’imprimer les valeurs des nœuds et dès que vous constatez que le niveau du nœud augmente de 1, vous insérez une nouvelle ligne avant de commencer à imprimer tous les nœuds de ce niveau.

C'est mon code qui n'utilise pas beaucoup de mémoire et seule la file d'attente est nécessaire pour tout.

En supposant que l'arbre commence à la racine.

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

A la fin, tout ce dont vous avez besoin est la file d'attente pour tous les traitements.

0
Ankur Kothari

Pour ceux qui sont simplement intéressés par la visualisation des arbres binaires (et pas tellement dans la théorie derrière la recherche en largeur d'abord), il existe une fonction show dans le paquet binarytree . Appliqué à l'exemple donné dans la question,

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)

qui imprime

    1    
   / \   
  2   3  
 /   / \ 
4   5   6
0
Kurt Peek