web-dev-qa-db-ja.com

最上部から始めて、レベルごとにバイナリツリーのデータをどのように出力しますか?

これはインタビューの質問です

解決策を考えます。キューを使用します。

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を使用しない、これよりも優れたソリューションは何か考えられますか?

17
Learner

レベルごとのトラバーサルは、幅優先トラバーサルと呼ばれます。これを行うには、キューを使用するのが適切です。深さ優先トラバーサルを行う場合は、スタックを使用します。

あなたがそれを持っている方法はかなり標準的ではありませんがこれがどうあるべきかです。

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

4

5

6

39

質問ではツリーをレベルごとに印刷する必要があるため、改行文字をコンソールにいつ印刷するかを決定する方法が必要です。これは、キューに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;
}
17
Mru

いくつかの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では珍しいことですが、ここでは十分に活用されています。

5

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 

アルゴリズム:

  1. リンクリストノードのArrayListを作成します。
  2. キュー(幅優先検索)を使用してレベル順トラバーサルを実行します。
  3. 各レベルのすべてのノードを取得するには、キューからノードを取り出す前に、キューのサイズを変数に格納します(たとえば、levelNodesと呼びます)。
  4. LevelNodes> 0の間に、ノードを取り出して印刷し、その子をキューに追加します。
  5. その後、whileループで改行します。

PS:私はOPが言った、キューがないことを知っています。私の答えは、誰かがキューを使用してC++ソリューションを探しているかどうかを示すことです。

4
kaushal
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();
            }
        }           
    }

}
3
anshu

私はあなたが期待していることは、スペースまたはコンマで区切られた各レベルのノードを印刷し、レベルを改行で区切ることだと思います。これは私がアルゴリズムをコード化する方法です。グラフまたはツリーで幅優先検索を実行してノードをキューに挿入すると、出てくるキューのすべてのノードは、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

最後に必要なのは、すべての処理のキューです。

1
Ankur Kothari

レベルごとに出力するために、キューに追加するタプルとしてノードと共にレベル情報を保存できます。その後、レベルが変更されるたびに新しい行を印刷できます。ここに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))
1
Faruk Sahin

これを試してください(完全なコード):

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;
    }
}
1
JAN

もちろん、キューを使用する必要はありません。これは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)
0
Aerin

以下のコードで試してください。

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();
        }
    }
}
0
Naren

答えを微調整して、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");
    }
0

これが私の答えです。

//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は、ノードの子を格納する配列です。

0
Arsh Bhullar