Печать BFS (двоичное дерево) в порядке уровней с определенным форматированием
для начала, этот вопрос не является dup этот, но и опирается на него.
взяв дерево в этом вопросе, например,
1
/
2 3
/ /
4 5 6
как бы вы изменили свою программу, чтобы напечатать ее так,
1
2 3
4 5 6
, а не общие
1
2
3
4
5
6
Я в основном ищу интуиции на наиболее эффективный способ сделать это - у меня есть метод, включающий добавление результата в список, а затем цикл через него. Более эффективным способом может быть сохранение последнего элемента на каждом уровне по мере его вывода и последующая печать новой строки.
идеи?
12 ответов
просто построить один уровень за раз, например:
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 плюс вспомогательная информация (например, глубина или количество узлов, оставшихся на данном уровне).
однако список, который только добавляется (и зацикливается, а не" потребляется"), довольно эффективен, чем deque (и если вы после решений на C++, аналогично, std::vector, используя только push_back
для построения его, и цикл для После этого используя его, более эффективен чем std:: deque). Поскольку все производство происходит сначала, то все итерации (или потребление), интересная альтернатива если память жестко ограничена может быть использовать список в любом случае для представления каждого уровня, а затем .reverse
это прежде чем вы начнете потреблять ее (с .pop
звонки) -- у меня нет больших деревьев вокруг, чтобы проверить по измерению, но я подозреваю, что этот подход все равно будет быстрее (и на самом деле меньше памяти), чем deque
(предполагая, что базовый реализация list [[или std:: vector]] фактически перерабатывает память после нескольких вызовов pop
[[или pop_back
]] -- и с тем же предположением для deque, конечно;-).
звучит как в ширину обход для меня.
ширина-первый обход реализуется с помощью очереди. Здесь просто вставьте в очередь специальный маркер, который указывает, что новая строка должна быть напечатана. Каждый раз, когда маркер найден, распечатайте новую строку и повторно вставьте маркер в очередь (в конце-это определение очереди).
запустите алгоритм с очередью, содержащей корень, за которым следует специальная новая строка знак.
это первый поиск ширины, поэтому вы можете использовать очередь и рекурсивно делать это простым и компактным способом ...
# 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)
почему бы не сохранить sentinal в очереди и не проверить, когда обрабатываются все узлы текущего уровня.
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);
}
}
мое решение похоже на решение Алекса Мартелли, но я отделяю обход структуры данных от обработки структуры данных. Я поместил мясо кода в 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
простая версия, основанная на первом поиске хлеба, этот код применим для графиков в целом и может использоваться для двоичных деревьев.
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
здесь мой код печатает уровень дерева по уровню, а также вверх ногами
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
это причина, по которой замена части была необходима в этом массиве.
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()
следующий код выведет каждый уровень двоичного дерева в новую строку:
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++;
}
}
}
для тех, кто просто заинтересован в визуализации бинарных деревьев (и не так много в теории широты первого поиска), есть
Я думаю, что вы ожидаете распечатать узлы на каждом уровне, разделенные пробелом или запятой, а уровни будут разделены новой строкой. Вот как я бы закодировал алгоритм. Мы знаем, что когда мы делаем широтно-первый поиск на графике или дереве и вставляем узлы в очередь, все узлы в очереди будут либо на том же уровне, что и предыдущий, либо на новом уровне, который является родительским уровнем + 1, и ничего больше.
поэтому, когда вы находитесь на уровне держать распечатайте значения узлов, и как только вы обнаружите, что уровень узла увеличивается на 1, вы вставляете новую строку перед началом печати всех узлов на этом уровне.
Это мой код, который не использует много памяти и на все нужны только очереди.
предполагая, что дерево начинается с корня.
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
В конце все, что вам нужно, это очереди на обработку.
версию, которая не требует дополнительного хранения:
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 еще.