これはインタビューの質問です
解決策を考えます。キューを使用します。
public Void BFS()
{
Queue q = new Queue();
q.Enqueue(root);
Console.WriteLine(root.Value);
while (q.count > 0)
{
Node n = q.DeQueue();
if (n.left !=null)
{
Console.Writeln(n.left);
q.EnQueue(n.left);
}
if (n.right !=null)
{
Console.Writeln(n.right);
q.EnQueue(n.right);
}
}
}
Queueを使用しない、これよりも優れたソリューションは何か考えられますか?
レベルごとのトラバーサルは、幅優先トラバーサルと呼ばれます。これを行うには、キューを使用するのが適切です。深さ優先トラバーサルを行う場合は、スタックを使用します。
あなたがそれを持っている方法はかなり標準的ではありませんがこれがどうあるべきかです。
public Void BFS()
{
Queue q = new Queue();
q.Enqueue(root);//You don't need to write the root here, it will be written in the loop
while (q.count > 0)
{
Node n = q.DeQueue();
Console.Writeln(n.Value); //Only write the value when you dequeue it
if (n.left !=null)
{
q.EnQueue(n.left);//enqueue the left child
}
if (n.right !=null)
{
q.EnQueue(n.right);//enque the right child
}
}
}
編集
これが実際のアルゴリズムです。次のような木があるとします。
1
/ \
2 3
/ / \
4 5 6
まず、ルート(1)がエンキューされます。その後、ループに入ります。キュー(1)の最初の項目がデキューされて印刷されます。 1の子は左から右にエンキューされ、キューには{2、3}が含まれ、ループの最初に戻りますキュー(2)の最初の項目がデキューされ、印刷された2の子は左から右にエンキューされ、キューには{3、 4}ループの開始に戻る...
キューには、各ループでこれらの値が含まれます
1:{1}
2:{2、3}
3:{3、4}
4:{4、5、6}
5:{5、6}
6:{6}
7:{} //空、ループ終了
出力:
1
2
3
4
5
6
質問ではツリーをレベルごとに印刷する必要があるため、改行文字をコンソールにいつ印刷するかを決定する方法が必要です。これは、キューにNewLineノードを追加することで同じことを試みるコードです。
void PrintByLevel(Node *root)
{
Queue q;
Node *newline = new Node("\n");
Node *v;
q->enque(root);
q->enque(newline);
while(!q->empty()) {
v = q->deque();
if(v == newline) {
printf("\n");
if(!q->empty())
q->enque(newline);
}
else {
printf("%s", v->val);
if(v->Left)
q-enque(v->left);
if(v->right)
q->enque(v->right);
}
}
delete newline;
}
いくつかのScalaソリューションを見てみましょう。最初に、非常に基本的なバイナリツリーを定義します。
case class Tree[+T](value: T, left: Option[Tree[T]], right: Option[Tree[T]])
次のツリーを使用します。
1
/ \
2 3
/ / \
4 5 6
次のようにツリーを定義します。
val myTree = Tree(1,
Some(Tree(2,
Some(Tree(4, None, None)),
None
)
),
Some(Tree(3,
Some(Tree(5, None, None)),
Some(Tree(6, None, None))
)
)
)
各要素に必要な関数を適用してツリーをトラバースするbreadthFirst関数を定義します。これで、print関数を定義し、次のように使用します。
def printTree(tree: Tree[Any]) =
breadthFirst(tree, (t: Tree[Any]) => println(t.value))
printTree(myTree)
ここで、Scalaソリューション、再帰的、リストですがキューはありません:
def breadthFirst[T](t: Tree[T], f: Tree[T] => Unit): Unit = {
def traverse(trees: List[Tree[T]]): Unit = trees match {
case Nil => // do nothing
case _ =>
val children = for{tree <- trees
Some(child) <- List(tree.left, tree.right)}
yield child
trees map f
traverse(children)
}
traverse(List(t))
}
次に、Scalaソリューション、キュー、再帰なし:
def breadthFirst[T](t: Tree[T], f: Tree[T] => Unit): Unit = {
import scala.collection.mutable.Queue
val queue = new Queue[Option[Tree[T]]]
import queue._
enqueue(Some(t))
while(!isEmpty)
dequeue match {
case Some(tree) =>
f(tree)
enqueue(tree.left)
enqueue(tree.right)
case None =>
}
}
その再帰的な解決策は完全に機能しますが、さらに単純化できるという不安があります。
キューバージョンは機能しませんが、非常に効果的です。オブジェクトのインポートについてのちょっとしたことはScalaでは珍しいことですが、ここでは十分に活用されています。
C++:
struct node{
string key;
struct node *left, *right;
};
void printBFS(struct node *root){
std::queue<struct node *> q;
q.Push(root);
while(q.size() > 0){
int levelNodes = q.size();
while(levelNodes > 0){
struct node *p = q.front();
q.pop();
cout << " " << p->key ;
if(p->left != NULL) q.Push(p->left);
if(p->right != NULL) q.Push(p->right);
levelNodes--;
}
cout << endl;
}
}
入力:
以下から作成されたバランスツリー:
string a[] = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n"};
出力:
g
c k
a e i m
b d f h j l n
PS:私はOPが言った、キューがないことを知っています。私の答えは、誰かがキューを使用してC++ソリューションを探しているかどうかを示すことです。
public class LevelOrderTraversalQueue {
Queue<Nodes> qe = new LinkedList<Nodes>();
public void printLevelOrder(Nodes root)
{
if(root == null) return;
qe.add(root);
int count = qe.size();
while(count!=0)
{
System.out.print(qe.peek().getValue());
System.out.print(" ");
if(qe.peek().getLeft()!=null) qe.add(qe.peek().getLeft());
if(qe.peek().getRight()!=null) qe.add(qe.peek().getRight());
qe.remove(); count = count -1;
if(count == 0 )
{
System.out.println(" ");
count = qe.size();
}
}
}
}
私はあなたが期待していることは、スペースまたはコンマで区切られた各レベルのノードを印刷し、レベルを改行で区切ることだと思います。これは私がアルゴリズムをコード化する方法です。グラフまたはツリーで幅優先検索を実行してノードをキューに挿入すると、出てくるキューのすべてのノードは、1つ前のレベルと同じレベルか、親レベルである新しいレベルになります。 + 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そうするためのコードがあります。
from collections import deque
class BTreeNode:
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def printLevel(self):
""" Breadth-first traversal, print out the data by level """
level = 0
lastPrintedLevel = 0
visit = deque([])
visit.append((self, level))
while len(visit) != 0:
item = visit.popleft()
if item[1] != lastPrintedLevel: #New line for a new level
lastPrintedLevel +=1
print
print item[0].data,
if item[0].left != None:
visit.append((item[0].left, item[1] + 1))
if item[0].right != None:
visit.append((item[0].right, item[1] + 1))
これを試してください(完全なコード):
class HisTree
{
public static class HisNode
{
private int data;
private HisNode left;
private HisNode right;
public HisNode() {}
public HisNode(int _data , HisNode _left , HisNode _right)
{
data = _data;
right = _right;
left = _left;
}
public HisNode(int _data)
{
data = _data;
}
}
public static int height(HisNode root)
{
if (root == null)
{
return 0;
}
else
{
return 1 + Math.max(height(root.left), height(root.right));
}
}
public static void main(String[] args)
{
// 1
// / \
// / \
// 2 3
// / \ / \
// 4 5 6 7
// /
// 21
HisNode root1 = new HisNode(3 , new HisNode(6) , new HisNode(7));
HisNode root3 = new HisNode(4 , new HisNode(21) , null);
HisNode root2 = new HisNode(2 , root3 , new HisNode(5));
HisNode root = new HisNode(1 , root2 , root1);
printByLevels(root);
}
private static void printByLevels(HisNode root) {
List<HisNode> nodes = Arrays.asList(root);
printByLevels(nodes);
}
private static void printByLevels(List<HisNode> nodes)
{
if (nodes == null || (nodes != null && nodes.size() <= 0))
{
return;
}
List <HisNode> nodeList = new LinkedList<HisNode>();
for (HisNode node : nodes)
{
if (node != null)
{
System.out.print(node.data);
System.out.print(" , ");
nodeList.add(node.left);
nodeList.add(node.right);
}
}
System.out.println();
if (nodeList != null && !CheckIfNull(nodeList))
{
printByLevels(nodeList);
}
else
{
return;
}
}
private static boolean CheckIfNull(List<HisNode> list)
{
for(HisNode elem : list)
{
if (elem != null)
{
return false;
}
}
return true;
}
}
もちろん、キューを使用する必要はありません。これはpythonです。
# Function to print level order traversal of tree
def printLevelOrder(root):
h = height(root)
for i in range(1, h+1):
printGivenLevel(root, i)
# Print nodes at a given level
def printGivenLevel(root , level):
if root is None:
return
if level == 1:
print "%d" %(root.data),
Elif level > 1 :
printGivenLevel(root.left , level-1)
printGivenLevel(root.right , level-1)
""" Compute the height of a tree--the number of nodes
along the longest path from the root node down to
the farthest leaf node
"""
def height(node):
if node is None:
return 0
else :
# Compute the height of each subtree
lheight = height(node.left)
rheight = height(node.right)
return max(lheight, reight)
以下のコードで試してください。
public void printLevelOrder(TreeNode root) {
if (root == null) {
return;
}
Queue<TreeNode> nodesToVisit = new LinkedList<>();
nodesToVisit.add(root);
int count = nodesToVisit.size();
while (count != 0) {
TreeNode node = nodesToVisit.remove();
System.out.print(" " + node.data);
if (node.left != null) {
nodesToVisit.add(node.left);
}
if (node.right != null) {
nodesToVisit.add(node.right);
}
count--;
if (count == 0) {
System.out.println("");
count = nodesToVisit.size();
}
}
}
答えを微調整して、nullノードを表示し、高さで出力しました。赤黒木のバランスをテストするために実際にはかなりまともだった。できる
また、印刷ラインに色を追加して、黒の高さを確認します。
Queue<node> q = new Queue<node>();
int[] arr = new int[]{1,2,4,8,16,32,64,128,256};
int i =0;
int b = 0;
int keeper = 0;
public void BFS()
{
q.Enqueue(root);
while (q.Count > 0)
{
node n = q.Dequeue();
if (i == arr[b])
{
System.Diagnostics.Debug.Write("\r\n"+"("+n.id+")");
b++;
i =0 ;
}
else {
System.Diagnostics.Debug.Write("(" + n.id + ")");
}
i++;
if (n.id != -1)
{
if (n.left != null)
{
q.Enqueue(n.left);
}
else
{
node c = new node();
c.id = -1;
c.color = 'b';
q.Enqueue(c);
}
if (n.right != null)
{
q.Enqueue(n.right);
}
else
{
node c = new node();
c.id = -1;
c.color = 'b';
q.Enqueue(c);
}
}
}
i = 0;
b = 0;
System.Diagnostics.Debug.Write("\r\n");
}
これが私の答えです。
//for level order traversal
func forEachLevelOrder(_ visit : (TreeNode) -> Void) {
visit(self)
var queue = Queue<TreeNode>()
children.forEach {
queue.Enqueue($0)
}
while let node = queue.Dequeue() {
visit(node)
node.children.forEach { queue.Enqueue($0)}
}
}
childrenは、ノードの子を格納する配列です。