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?
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 ;-).
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.
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)
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
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);
}
}
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)
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()
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
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.
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++;
}
}
}
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.
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