そもそも、この質問は this one の重複ではなく、それに基づいています。
その質問のツリーを例にとると、
1
/ \
2 3
/ / \
4 5 6
それを印刷するためにプログラムをどのように修正しますか?
1
2 3
4 5 6
一般的ではなく
1
2
3
4
5
6
私は基本的にそれを行う最も効率的な方法に関する直観を探しています-リストに結果を追加し、それをループする方法があります。より効率的な方法は、ポップされるたびに最後の要素を各レベルに保存し、後で新しい行を印刷することです。
アイデア?
一度に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)
編集:最大消費「補助」メモリを少し節約したい場合(この「補助」メモリにこのレベルと次のレベルを同時に持たないでください)、もちろん使用できますlist
の代わりにcollection.deque
を使用し、単にループするのではなく、(popleft
経由で)現在のレベルを使用します。レベルを区別する必要がある場合、一度に1つのレベルを作成するというアイデア(以前のレベルを消費する(または繰り返す)として)はそのままです。単一の大きな両端キューと補助情報を使用するよりも直接的です(深さ、特定のレベルに残っているノードの数など)。
ただし、追加される(および「消費される」のではなくループされる)リストは、両端キューよりもかなり効率的です(C++ソリューションを使用している場合は、同様に、Push_back
のみを使用したstd :: vectorそれを構築し、それを使用するためのループは、std :: dequeよりも効率的です)。すべての生成が最初に行われ、次にすべての反復(または消費)が行われるため、興味深い代替案ifメモリが厳しく制限されているため、リストを使用して各レベルを表現し、消費を開始する前に.reverse
it(.pop
呼び出しを使用)-測定によって確認する大きなツリーはありませんが、このアプローチはdeque
(リストの基礎となる実装を前提とする場合)よりも高速で(実際にはメモリ消費が少ないと思われます) [またはstd :: vector]]は、実際にpop
[[またはpop_back
]]を数回呼び出した後、メモリをリサイクルします。もちろん、dequeと同じ仮定で、-)。
breadth-first traversal のように聞こえます。
幅優先走査は、 queue で実装されます。ここでは、改行を印刷する必要があることを示す特別なトークンをキューに挿入するだけです。トークンが見つかるたびに、改行を出力し、キューにトークンを再挿入します(最後に、それがキューの定義です)。
ルートとそれに続く特別な改行トークンを含むキューでアルゴリズムを開始します。
これは幅優先の検索であるため、キューを使用し、シンプルかつコンパクトな方法で再帰的にこれを行うことができます...
# 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)
私のソリューションはAlex Martelliのソリューションに似ていますが、データ構造の処理とデータ構造のトラバースを分離しています。 printByLayerを短く簡潔にするために、コードの内容をiterLayersに入れます。
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
センチネルをキューに入れたままにして、現在のレベルのすべてのノードが処理されるタイミングをチェックしてください。
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);
}
}
これは、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()
Bread First Searchに基づく単純なバージョン。このコードは一般的なグラフに適用でき、バイナリツリーにも使用できます。
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
これが、そのアレイで部品を交換する必要があった理由です。
次のコードは、バイナリツリーの各レベルを新しい行に出力します。
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
最後に必要なのは、すべての処理のためのキューです。
Python Gistはレベルごとにツリーを印刷します。その背後にある考え方は、BFSを使用し、レベルの最後のノードの終わりをマークするレベルマーカー整数を保持することです。 Nareshのセンチネルアプローチ。ただし、キュー内にセンチネルを挿入する必要はありません。これはレベルマーカーによって実現されるためです。
このアルゴリズムは、O(2tree_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
このGistは https://Gist.github.com/lopespm/993f0af88cf30b7f8c9e17982518b71b で入手できます。
(幅優先探索の背後にある理論ではなく)バイナリツリーの視覚化に単に興味がある人のために、 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