【问题标题】:Performing Breadth First Search recursively递归执行广度优先搜索
【发布时间】:2010-03-31 00:05:37
【问题描述】:

假设您想要实现二叉树的广度优先搜索递归。你会怎么做呢?

是否可以只使用调用堆栈作为辅助存储?

【问题讨论】:

  • 非常好的问题。这一点都不简单。基本上,您要求仅使用堆栈来实现 BFS。
  • 递归只有一个堆栈?这让我头疼。
  • 我通常使用堆栈来移除递归行为
  • 如果我在最大堆上使用 BFS,我想知道下面给出的解决方案是否正常工作?有什么想法吗?

标签: algorithm breadth-first-search


【解决方案1】:

(我假设这只是某种思维练习,甚至是一个技巧性的家庭作业/面试问题,但我想我可以想象一些奇怪的场景,由于某种原因你不允许任何堆空间 [一些自定义内存管理器真的很糟糕?一些奇怪的运行时/操作系统问题?] 而您仍然可以访问堆栈...)

广度优先遍历传统上使用队列,而不是堆栈。队列和堆栈的本质是完全相反的,所以试图使用调用堆栈(这是一个堆栈,因此得名)作为辅助存储(队列)几乎注定要失败,除非你正在做调用堆栈有些愚蠢的荒谬,你不应该这样做。

同样,您尝试实现的任何非尾递归本质上都是在算法中添加一个堆栈。这使得它不再在二叉树上进行广度优先搜索,因此传统 BFS 的运行时和诸如此类的东西不再完全适用。当然,您总是可以轻松地将任何循环转换为递归调用,但这不是任何有意义的递归。

但是,正如其他人所展示的那样,有一些方法可以以一定的成本实现遵循 BFS 语义的东西。如果比较的成本很昂贵但节点遍历很便宜,那么就像@Simon Buchan 所做的那样,您可以简单地运行迭代深度优先搜索,只处理叶子。这意味着堆中没有存储不断增长的队列,只有一个局部深度变量,并且随着树被一遍又一遍地遍历,堆栈在调用堆栈上一遍又一遍地构建。正如@Patrick 所指出的,由数组支持的二叉树通常以广度优先遍历顺序存储,因此广度优先搜索将是微不足道的,也不需要辅助队列。

【讨论】:

  • 这确实只是一个思考练习。我真的无法想象你真正想要这样做的情况。感谢您深思熟虑的答案!
  • "但我想我可以想象一些奇怪的场景,由于某种原因不允许任何堆空间":不知道,我可以想象一个嵌入式环境,其中只有堆栈(以及任何只读内存空间)可用(如果您确切知道程序将要做什么,那么在不使用堆的情况下编写软件实际上非常容易和高效,嵌入式软件通常就是这种情况) .所以这对我来说并不是那么“奇怪”。不寻常,也许,但并不奇怪。
  • 我认为您的回答可能包含对本文的引用(ibm.com/developerworks/aix/library/au-aix-stack-tree-traversal)。它显示了您在答案的第二部分中所写内容的实现
  • 如果唯一的限制是使用“堆栈”和“无队列” - 使用两个堆栈来模拟队列
【解决方案2】:

如果使用数组来支持二叉树,则可以通过代数方式确定下一个节点。如果i 是一个节点,那么可以在2i + 1(对于左节点)和2i + 2(对于右节点)找到它的子节点。节点的下一个邻居由i + 1 给出,除非i2 的幂

这是一个非常简单的在数组支持的二叉搜索树上实现广度优先搜索的伪代码。这假定一个固定大小的数组,因此是一个固定深度的树。它将查看无父节点,并可能创建一个难以管理的大堆栈。

bintree-bfs(bintree, elt, i)
    if (i == LENGTH)
        return false

    else if (bintree[i] == elt)
        return true

    else 
        return bintree-bfs(bintree, elt, i+1)        

【讨论】:

  • 不错。我忽略了我们正在处理二叉树的事实。可以使用 DFS 分配索引。顺便说一句,您在第一种情况下忘记了 return false。
  • 我想我更喜欢排队的方法;P。添加了返回 false。
  • 聪明。我没有想到将节点存储在数组中并以代数方式引用它们。
【解决方案3】:

我找不到完全递归的方法(没有任何辅助数据结构)。但是如果队列 Q 是通过引用传递的,那么你可以有下面这个傻尾递归函数:

BFS(Q)
{
  if (|Q| > 0)
     v <- Dequeue(Q)
     Traverse(v)
     foreach w in children(v)
        Enqueue(Q, w)    

     BFS(Q)
}

【讨论】:

  • 这是不自然的方式,将递归添加到干净和正确的函数中。
  • 完全不同意——我觉得它更自然——也更有用;您可以扩展此方法以在通过层时传递工作状态
【解决方案4】:

以下方法使用 DFS 算法来获取特定深度的所有节点 - 这与为该级别执行 BFS 相同。如果您找出树的深度并对所有级别执行此操作,结果将与 BFS 相同。

public void PrintLevelNodes(Tree root, int level) {
    if (root != null) {
        if (level == 0) {
            Console.Write(root.Data);
            return;
        }
        PrintLevelNodes(root.Left, level - 1);
        PrintLevelNodes(root.Right, level - 1);
    }
}

for (int i = 0; i < depth; i++) {
    PrintLevelNodes(root, i);
}

找到树的深度是小菜一碟:

public int MaxDepth(Tree root) {
    if (root == null) {
        return 0;
    } else {
        return Math.Max(MaxDepth(root.Left), MaxDepth(root.Right)) + 1;
    }
}

【讨论】:

  • 请多注意您的代码格式。我做了一些改变。
  • 但是,等等……这是 DFS 而不是 BFS?因为 PrintLevelNodes 直到 level 为零才返回。
  • @HerringtonDarkholme,正确。它会进行 DFS 搜索,但输出值就像它对某个级别进行 BFS 一样。感谢您指出这一点。
  • @Sanjay ,这确实很好地展示了如何在 DFS 顺序的节点上执行某些操作。这不一定是实际“接触” DFS 顺序中的节点的方式,但肯定会允许以 DFS 顺序递归“操作”节点,在这种情况下打印它们的值。
【解决方案5】:

Java 中一个简单的 BFS 和 DFS 递归:
只需在堆栈/队列中推送/提供树的根节点并调用这些函数。

public static void breadthFirstSearch(Queue queue) {

    if (queue.isEmpty())
        return;

    Node node = (Node) queue.poll();

    System.out.println(node + " ");

    if (node.right != null)
        queue.offer(node.right);

    if (node.left != null)
        queue.offer(node.left);

    breadthFirstSearch(queue);
}

public static void depthFirstSearch(Stack stack) {

    if (stack.isEmpty())
        return;

    Node node = (Node) stack.pop();

    System.out.println(node + " ");

    if (node.right != null)
        stack.push(node.right);

    if (node.left != null)
        stack.push(node.left);

    depthFirstSearch(stack);
}

【讨论】:

  • 将堆栈作为参数传递给 DFS 有点奇怪,因为那里已经有了隐式堆栈。另外问题是仅使用调用堆栈作为数据结构。
【解决方案6】:

我发现了一个非常漂亮的递归(甚至是函数式)广度优先遍历相关算法。不是我的想法,但我认为应该在本主题中提及。

Chris Okasaki 仅用 3 张图片非常清楚地解释了他在 ICFP 2000 上的广度优先编号算法http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html

我在http://debasishg.blogspot.de/2008/09/breadth-first-numbering-okasakis.html 找到的 Debasish Ghosh 的 Scala 实现是:

trait Tree[+T]
case class Node[+T](data: T, left: Tree[T], right: Tree[T]) extends Tree[T]
case object E extends Tree[Nothing]

def bfsNumForest[T](i: Int, trees: Queue[Tree[T]]): Queue[Tree[Int]] = {
  if (trees.isEmpty) Queue.Empty
  else {
    trees.dequeue match {
      case (E, ts) =>
        bfsNumForest(i, ts).enqueue[Tree[Int]](E)
      case (Node(d, l, r), ts) =>
        val q = ts.enqueue(l, r)
        val qq = bfsNumForest(i+1, q)
        val (bb, qqq) = qq.dequeue
        val (aa, tss) = qqq.dequeue
        tss.enqueue[org.dg.collection.BFSNumber.Tree[Int]](Node(i, aa, bb))
    }
  }
}

def bfsNumTree[T](t: Tree[T]): Tree[Int] = {
  val q = Queue.Empty.enqueue[Tree[T]](t)
  val qq = bfsNumForest(1, q)
  qq.dequeue._1
}

【讨论】:

  • +1 用于漂亮的算法。但是,我发现它仍在使用队列。 “规则3”的左边其实就是dequeue和enqueue操作。
【解决方案7】:

笨方法:

template<typename T>
struct Node { Node* left; Node* right; T value; };

template<typename T, typename P>
bool searchNodeDepth(Node<T>* node, Node<T>** result, int depth, P pred) {
    if (!node) return false;
    if (!depth) {
        if (pred(node->value)) {
            *result = node;
        }
        return true;
    }
    --depth;
    searchNodeDepth(node->left, result, depth, pred);
    if (!*result)
        searchNodeDepth(node->right, result, depth, pred);
    return true;
}

template<typename T, typename P>
Node<T>* searchNode(Node<T>* node, P pred) {
    Node<T>* result = NULL;
    int depth = 0;
    while (searchNodeDepth(node, &result, depth, pred) && !result)
        ++depth;
    return result;
}

int main()
{
    // a c   f
    //  b   e
    //    d
    Node<char*>
        a = { NULL, NULL, "A" },
        c = { NULL, NULL, "C" },
        b = { &a, &c, "B" },
        f = { NULL, NULL, "F" },
        e = { NULL, &f, "E" },
        d = { &b, &e, "D" };

    Node<char*>* found = searchNode(&d, [](char* value) -> bool {
        printf("%s\n", value);
        return !strcmp((char*)value, "F");
    });

    printf("found: %s\n", found->value);

    return 0;
}

【讨论】:

    【解决方案8】:

    这里是简短的 Scala 解决方案:

      def bfs(nodes: List[Node]): List[Node] = {
        if (nodes.nonEmpty) {
          nodes ++ bfs(nodes.flatMap(_.children))
        } else {
          List.empty
        }
      }
    

    使用返回值作为累加器的想法非常适合。 可以用其他语言以类似的方式实现,只需确保您的递归函数处理节点列表

    测试代码清单(使用@marco 测试树):

    import org.scalatest.FlatSpec
    
    import scala.collection.mutable
    
    class Node(val value: Int) {
    
      private val _children: mutable.ArrayBuffer[Node] = mutable.ArrayBuffer.empty
    
      def add(child: Node): Unit = _children += child
    
      def children = _children.toList
    
      override def toString: String = s"$value"
    }
    
    class BfsTestScala extends FlatSpec {
    
      //            1
      //          / | \
      //        2   3   4
      //      / |       | \
      //    5   6       7  8
      //  / |           | \
      // 9  10         11  12
      def tree(): Node = {
        val root = new Node(1)
        root.add(new Node(2))
        root.add(new Node(3))
        root.add(new Node(4))
        root.children(0).add(new Node(5))
        root.children(0).add(new Node(6))
        root.children(2).add(new Node(7))
        root.children(2).add(new Node(8))
        root.children(0).children(0).add(new Node(9))
        root.children(0).children(0).add(new Node(10))
        root.children(2).children(0).add(new Node(11))
        root.children(2).children(0).add(new Node(12))
        root
      }
    
      def bfs(nodes: List[Node]): List[Node] = {
        if (nodes.nonEmpty) {
          nodes ++ bfs(nodes.flatMap(_.children))
        } else {
          List.empty
        }
      }
    
      "BFS" should "work" in {
        println(bfs(List(tree())))
      }
    }
    

    输出:

    List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
    

    【讨论】:

      【解决方案9】:

      这是一个 BFS 递归遍历 Python 实现,适用于没有循环的图。

      def bfs_recursive(level):
          '''
           @params level: List<Node> containing the node for a specific level.
          '''
          next_level = []
          for node in level:
              print(node.value)
              for child_node in node.adjency_list:
                  next_level.append(child_node)
          if len(next_level) != 0:
              bfs_recursive(next_level)
      
      
      class Node:
          def __init__(self, value):
              self.value = value
              self.adjency_list = []
      

      【讨论】:

        【解决方案10】:

        我想将我的美分添加到top answer,因为如果语言支持生成器之类的东西,bfs 可以共同递归地完成。

        首先,@Tanzelax 的回答是:

        广度优先遍历传统上使用队列,而不是堆栈。队列和栈的本质是完全相反的,所以试图使用调用栈(这是一个栈,因此得名)作为辅助存储(队列)几乎注定要失败

        确实,普通函数调用的堆栈不会像普通堆栈那样表现。但是生成器函数会暂停函数的执行,这样我们就有机会产生下一级节点的子节点,而无需深入研究节点的更深层次的后代。

        以下代码是Python中的递归 bfs。

        def bfs(root):
          yield root
          for n in bfs(root):
            for c in n.children:
              yield c
        

        这里的直觉是:

        1. bfs first 将返回根作为第一个结果
        2. 假设我们已经有了 bfs 序列,bfs 中的下一级元素是序列中前一个节点的直接子节点
        3. 重复以上两个步骤

        【讨论】:

        • 我不懂 Python,但我认为您的代码转换为 this C# code。它执行 BFS 遍历,但由于 stackoverflow 异常而崩溃。到目前为止我还没有弄清楚原因。但是,我修改了算法,使其停止(并且可能表现更好)。你可以找到我的工作样本here
        【解决方案11】:

        这是一个 python 实现:

        graph = {'A': ['B', 'C'],
                 'B': ['C', 'D'],
                 'C': ['D'],
                 'D': ['C'],
                 'E': ['F'],
                 'F': ['C']}
        
        def bfs(paths, goal):
            if not paths:
                raise StopIteration
        
            new_paths = []
            for path in paths:
                if path[-1] == goal:
                    yield path
        
                last = path[-1]
                for neighbor in graph[last]:
                    if neighbor not in path:
                        new_paths.append(path + [neighbor])
            yield from bfs(new_paths, goal)
        
        
        for path in bfs([['A']], 'D'):
            print(path)
        

        【讨论】:

          【解决方案12】:

          这是递归 BFS 的 Scala 2.11.4 实现。为了简洁起见,我牺牲了尾调用优化,但 TCOd 版本非常相似。另见@snv 的帖子。

          import scala.collection.immutable.Queue
          
          object RecursiveBfs {
            def bfs[A](tree: Tree[A], target: A): Boolean = {
              bfs(Queue(tree), target)
            }
          
            private def bfs[A](forest: Queue[Tree[A]], target: A): Boolean = {
              forest.dequeueOption exists {
                case (E, tail) => bfs(tail, target)
                case (Node(value, _, _), _) if value == target => true
                case (Node(_, l, r), tail) => bfs(tail.enqueue(List(l, r)), target)
              }
            }
          
            sealed trait Tree[+A]
            case class Node[+A](data: A, left: Tree[A], right: Tree[A]) extends Tree[A]
            case object E extends Tree[Nothing]
          }
          

          【讨论】:

            【解决方案13】:

            以下对我来说似乎很自然,使用 Haskell。在树的各个层级上递归迭代(这里我将名称收集到一个大的有序字符串中以显示通过树的路径):

            data Node = Node {name :: String, children :: [Node]}
            aTree = Node "r" [Node "c1" [Node "gc1" [Node "ggc1" []], Node "gc2" []] , Node "c2" [Node "gc3" []], Node "c3" [] ]
            breadthFirstOrder x = levelRecurser [x]
                where levelRecurser level = if length level == 0
                                            then ""
                                            else concat [name node ++ " " | node <- level] ++ levelRecurser (concat [children node | node <- level])
            

            【讨论】:

              【解决方案14】:

              我必须实现一个以 BFS 顺序输出的堆遍历。它实际上不是 BFS,但完成了相同的任务。

              private void getNodeValue(Node node, int index, int[] array) {
                  array[index] = node.value;
                  index = (index*2)+1;
              
                  Node left = node.leftNode;
                  if (left!=null) getNodeValue(left,index,array);
                  Node right = node.rightNode;
                  if (right!=null) getNodeValue(right,index+1,array);
              }
              
              public int[] getHeap() {
                  int[] nodes = new int[size];
                  getNodeValue(root,0,nodes);
                  return nodes;
              }
              

              【讨论】:

              • 对于其他查看者:这是在数组中实现 complete 树的示例;具体来说,@Justin 正在执行前序遍历,在此期间,他将节点值(按 BFS 顺序)保存在适当 BFS 索引处的数组中。这允许调用函数对数组进行线性迭代,以 BFS 顺序打印值。看到这个general description 注意:调用函数必须处理不完整树的情况。
              【解决方案15】:

              设 v 为起始顶点

              设 G 为有问题的图

              以下是不使用队列的伪代码

              Initially label v as visited as you start from v
              BFS(G,v)
                  for all adjacent vertices w of v in G:
                      if vertex w is not visited:
                          label w as visited
                  for all adjacent vertices w of v in G:
                      recursively call BFS(G,w)
              

              【讨论】:

              • 我认为这可能会陷入无限循环——顶点被标记为已访问,但在再次递归之前从未测试它们的已访问性。
              • 这个sn-p类似于DFS而不是BFS
              【解决方案16】:

              二叉树(或 n-ary)树的 BFS 可以在没有队列的情况下递归完成,如下所示(此处为 Java):

              public class BreathFirst {
              
                  static class Node {
                      Node(int value) {
                          this(value, 0);
                      }
                      Node(int value, int nChildren) {
                          this.value = value;
                          this.children = new Node[nChildren];
                      }
                      int value;
                      Node[] children;
                  }
              
                  static void breathFirst(Node root, Consumer<? super Node> printer) {
                      boolean keepGoing = true;
                      for (int level = 0; keepGoing; level++) {
                          keepGoing = breathFirst(root, printer, level);
                      }
                  }
              
                  static boolean breathFirst(Node node, Consumer<? super Node> printer, int depth) {
                      if (depth < 0 || node == null) return false;
                      if (depth == 0) {
                          printer.accept(node);
                          return true;
                      }
                      boolean any = false;
                      for (final Node child : node.children) {
                          any |= breathFirst(child, printer, depth - 1);
                      }
                      return any;
                  }
              }
              

              以升序遍历打印数字 1-12 的示例:

              public static void main(String... args) {
                  //            1
                  //          / | \
                  //        2   3   4
                  //      / |       | \
                  //    5   6       7  8
                  //  / |           | \
                  // 9  10         11  12
              
                  Node root = new Node(1, 3);
                  root.children[0] = new Node(2, 2);
                  root.children[1] = new Node(3);
                  root.children[2] = new Node(4, 2);
                  root.children[0].children[0] = new Node(5, 2);
                  root.children[0].children[1] = new Node(6);
                  root.children[2].children[0] = new Node(7, 2);
                  root.children[2].children[1] = new Node(8);
                  root.children[0].children[0].children[0] = new Node(9);
                  root.children[0].children[0].children[1] = new Node(10);
                  root.children[2].children[0].children[0] = new Node(11);
                  root.children[2].children[0].children[1] = new Node(12);
              
                  breathFirst(root, n -> System.out.println(n.value));
              }
              

              【讨论】:

                【解决方案17】:
                #include <bits/stdc++.h>
                using namespace std;
                #define Max 1000
                
                vector <int> adj[Max];
                bool visited[Max];
                
                void bfs_recursion_utils(queue<int>& Q) {
                    while(!Q.empty()) {
                        int u = Q.front();
                        visited[u] = true;
                        cout << u << endl;
                        Q.pop();
                        for(int i = 0; i < (int)adj[u].size(); ++i) {
                            int v = adj[u][i];
                            if(!visited[v])
                                Q.push(v), visited[v] = true;
                        }
                        bfs_recursion_utils(Q);
                    }
                }
                
                void bfs_recursion(int source, queue <int>& Q) {
                    memset(visited, false, sizeof visited);
                    Q.push(source);
                    bfs_recursion_utils(Q);
                }
                
                int main(void) {
                    queue <int> Q;
                    adj[1].push_back(2);
                    adj[1].push_back(3);
                    adj[1].push_back(4);
                
                    adj[2].push_back(5);
                    adj[2].push_back(6);
                
                    adj[3].push_back(7);
                
                    bfs_recursion(1, Q);
                    return 0;
                }
                

                【讨论】:

                  【解决方案18】:

                  这是一个使用深度优先递归伪造广度优先遍历的 JavaScript 实现。我将每个深度的节点值存储在一个数组内,一个哈希值内。如果一个级别已经存在(我们有冲突),那么我们只需推送到该级别的数组。您也可以使用数组而不是 JavaScript 对象,因为我们的级别是数字的并且可以用作数组索引。您可以返回节点、值、转换为链接列表或任何您想要的。为了简单起见,我只是返回值。

                  BinarySearchTree.prototype.breadthFirstRec = function() {
                  
                      var levels = {};
                  
                      var traverse = function(current, depth) {
                          if (!current) return null;
                          if (!levels[depth]) levels[depth] = [current.value];
                          else levels[depth].push(current.value);
                          traverse(current.left, depth + 1);
                          traverse(current.right, depth + 1);
                      };
                  
                      traverse(this.root, 0);
                      return levels;
                  };
                  
                  
                  var bst = new BinarySearchTree();
                  bst.add(20, 22, 8, 4, 12, 10, 14, 24);
                  console.log('Recursive Breadth First: ', bst.breadthFirstRec());
                  /*Recursive Breadth First:  
                  { '0': [ 20 ],
                    '1': [ 8, 22 ],
                    '2': [ 4, 12, 24 ],
                    '3': [ 10, 14 ] } */
                  

                  这是一个使用迭代方法的实际广度优先遍历示例。

                  BinarySearchTree.prototype.breadthFirst = function() {
                  
                      var result = '',
                          queue = [],
                          current = this.root;
                  
                      if (!current) return null;
                      queue.push(current);
                  
                      while (current = queue.shift()) {
                          result += current.value + ' ';
                          current.left && queue.push(current.left);
                          current.right && queue.push(current.right);
                      }
                      return result;
                  };
                  
                  console.log('Breadth First: ', bst.breadthFirst());
                  //Breadth First:  20 8 22 4 12 24 10 14
                  

                  【讨论】:

                    【解决方案19】:

                    以下是我在不使用循环和队列的情况下完全递归地实现双向图的广度优先搜索的代码。

                    
                    
                    public class Graph
                    {
                        public int V;   
                        public LinkedList<Integer> adj[];
                    
                        Graph(int v)
                        {
                            V = v;
                            adj = new LinkedList[v];
                            for (int i=0; i<v; ++i)
                                adj[i] = new LinkedList<>();
                        }
                    
                        void addEdge(int v,int w)
                        {
                            adj[v].add(w);
                            adj[w].add(v);
                        }
                    
                        public LinkedList<Integer> getAdjVerted(int vertex)
                        {
                            return adj[vertex];
                        }
                    
                        public String toString()
                        {
                            String s = "";
                    
                            for (int i=0;i<adj.length;i++)
                            {
                                s = s +"\n"+i +"-->"+ adj[i] ;
                            }
                            return s;
                        }
                    }
                    
                    //BFS IMPLEMENTATION
                    
                    public static void recursiveBFS(Graph graph, int vertex,boolean visited[], boolean isAdjPrinted[])
                        {
                            if (!visited[vertex])
                            {
                                System.out.print(vertex +" ");
                                visited[vertex] = true;
                            }
                    
                            if(!isAdjPrinted[vertex])
                            {
                                isAdjPrinted[vertex] = true;
                                List<Integer> adjList = graph.getAdjVerted(vertex);
                                printAdjecent(graph, adjList, visited, 0,isAdjPrinted);
                            }
                        }
                    
                        public static void recursiveBFS(Graph graph, List<Integer> vertexList, boolean visited[], int i, boolean isAdjPrinted[])
                        {
                            if (i < vertexList.size())
                            {
                                recursiveBFS(graph, vertexList.get(i), visited, isAdjPrinted);
                                recursiveBFS(graph, vertexList, visited, i+1, isAdjPrinted);
                            }
                        }
                    
                        public static void printAdjecent(Graph graph, List<Integer> list, boolean visited[], int i, boolean isAdjPrinted[])
                        {
                            if (i < list.size())
                            {
                                if (!visited[list.get(i)])
                                {
                                    System.out.print(list.get(i)+" ");
                                    visited[list.get(i)] = true;
                                }
                                printAdjecent(graph, list, visited, i+1, isAdjPrinted);
                            }
                            else
                            {
                                recursiveBFS(graph, list, visited, 0, isAdjPrinted);
                            }
                        }
                    

                    【讨论】:

                      【解决方案20】:

                      二叉树递归广度优先搜索算法的C#实现。

                      Binary tree data visualization

                      IDictionary<string, string[]> graph = new Dictionary<string, string[]> {
                          {"A", new [] {"B", "C"}},
                          {"B", new [] {"D", "E"}},
                          {"C", new [] {"F", "G"}},
                          {"E", new [] {"H"}}
                      };
                      
                      void Main()
                      {
                          var pathFound = BreadthFirstSearch("A", "H", new string[0]);
                          Console.WriteLine(pathFound); // [A, B, E, H]
                      
                          var pathNotFound = BreadthFirstSearch("A", "Z", new string[0]);
                          Console.WriteLine(pathNotFound); // []
                      }
                      
                      IEnumerable<string> BreadthFirstSearch(string start, string end, IEnumerable<string> path)
                      {
                          if (start == end)
                          {
                              return path.Concat(new[] { end });
                          }
                      
                          if (!graph.ContainsKey(start)) { return new string[0]; }    
                      
                          return graph[start].SelectMany(letter => BreadthFirstSearch(letter, end, path.Concat(new[] { start })));
                      }
                      

                      如果您希望算法不仅适用于二叉树,而且适用于可以有两个或更多节点指向同一另一个节点的图,您必须通过保存已访问节点的列表来避免自循环。实现可能是这样的。

                      Graph data visualization

                      IDictionary<string, string[]> graph = new Dictionary<string, string[]> {
                          {"A", new [] {"B", "C"}},
                          {"B", new [] {"D", "E"}},
                          {"C", new [] {"F", "G", "E"}},
                          {"E", new [] {"H"}}
                      };
                      
                      void Main()
                      {
                          var pathFound = BreadthFirstSearch("A", "H", new string[0], new List<string>());
                          Console.WriteLine(pathFound); // [A, B, E, H]
                      
                          var pathNotFound = BreadthFirstSearch("A", "Z", new string[0], new List<string>());
                          Console.WriteLine(pathNotFound); // []
                      }
                      
                      IEnumerable<string> BreadthFirstSearch(string start, string end, IEnumerable<string> path, IList<string> visited)
                      {
                          if (start == end)
                          {
                              return path.Concat(new[] { end });
                          }
                      
                          if (!graph.ContainsKey(start)) { return new string[0]; }
                      
                      
                          return graph[start].Aggregate(new string[0], (acc, letter) =>
                          {
                              if (visited.Contains(letter))
                              {
                                  return acc;
                              }
                      
                              visited.Add(letter);
                      
                              var result = BreadthFirstSearch(letter, end, path.Concat(new[] { start }), visited);
                              return acc.Concat(result).ToArray();
                          });
                      }
                      

                      【讨论】:

                        【解决方案21】:

                        我已经使用 c++ 制作了一个程序,该程序也适用于联合和不相交图。

                            #include <queue>
                        #include "iostream"
                        #include "vector"
                        #include "queue"
                        
                        using namespace std;
                        
                        struct Edge {
                            int source,destination;
                        };
                        
                        class Graph{
                            int V;
                            vector<vector<int>> adjList;
                        public:
                        
                            Graph(vector<Edge> edges,int V){
                                this->V = V;
                                adjList.resize(V);
                                for(auto i : edges){
                                    adjList[i.source].push_back(i.destination);
                                    //     adjList[i.destination].push_back(i.source);
                                }
                            }
                            void BFSRecursivelyJoinandDisjointtGraphUtil(vector<bool> &discovered, queue<int> &q);
                            void BFSRecursivelyJointandDisjointGraph(int s);
                            void printGraph();
                        
                        
                        };
                        
                        void Graph :: printGraph()
                        {
                            for (int i = 0; i < this->adjList.size(); i++)
                            {
                                cout << i << " -- ";
                                for (int v : this->adjList[i])
                                    cout <<"->"<< v << " ";
                                cout << endl;
                            }
                        }
                        
                        
                        void Graph ::BFSRecursivelyJoinandDisjointtGraphUtil(vector<bool> &discovered, queue<int> &q) {
                            if (q.empty())
                                return;
                            int v = q.front();
                            q.pop();
                            cout << v <<" ";
                            for (int u : this->adjList[v])
                            {
                                if (!discovered[u])
                                {
                                    discovered[u] = true;
                                    q.push(u);
                                }
                            }
                            BFSRecursivelyJoinandDisjointtGraphUtil(discovered, q);
                        
                        }
                        
                        void Graph ::BFSRecursivelyJointandDisjointGraph(int s) {
                            vector<bool> discovered(V, false);
                            queue<int> q;
                        
                            for (int i = s; i < V; i++) {
                                if (discovered[i] == false)
                                {
                                    discovered[i] = true;
                                    q.push(i);
                                    BFSRecursivelyJoinandDisjointtGraphUtil(discovered, q);
                                }
                            }
                        }
                        
                        int main()
                        {
                        
                            vector<Edge> edges =
                                    {
                                            {0, 1}, {0, 2}, {1, 2}, {2, 0}, {2,3},{3,3}
                                    };
                        
                            int V = 4;
                            Graph graph(edges, V);
                         //   graph.printGraph();
                            graph.BFSRecursivelyJointandDisjointGraph(2);
                            cout << "\n";
                        
                        
                        
                        
                            edges = {
                                    {0,4},{1,2},{1,3},{1,4},{2,3},{3,4}
                            };
                        
                            Graph graph2(edges,5);
                        
                            graph2.BFSRecursivelyJointandDisjointGraph(0);
                            return 0;
                        }
                        

                        【讨论】:

                          【解决方案22】:

                          我认为这可以使用指针来完成,无需使用任何 QUEUE

                          基本上我们在任意一点维护两个指针,一个指向父母,另一个指向要处理的孩子(链表指向所有已处理的)

                          现在您只需分配子级的指针,当父级处理完成时,您只需将子级设置为父级以处理下一级

                          以下是我的代码:

                          //Tree Node
                          struct Node {
                              int val;
                              Node* left;
                              Node* right;
                              Node* next;
                          
                              Node() : val(0), left(NULL), right(NULL), next(NULL) {}
                          
                              Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
                          
                              Node(int _val, Node* _left, Node* _right, Node* _next)
                                  : val(_val), left(_left), right(_right), next(_next) {}
                          };
                              
                          

                          //算法:

                              void LevelTraverse(Node* parent,Node* chidstart,Node* childend ){
                                  if(!parent && !chidstart) return;  // we processed everything
                                  
                                  if(!parent && chidstart){ //finished processing last level
                                      parent=chidstart;chidstart=childend=NULL; // assgin child to parent for processing next level
                                      LevelTraverse(parent,chidstart,childend);
                                  }else if(parent && !chidstart){ // This is new level first node tobe processed
                                      Node* temp=parent; parent=parent->next;
                                      if(temp->left) { childend=chidstart=temp->left; }
                                      if(chidstart){
                                          if(temp->right) { childend->next=temp->right; childend=temp->right; }
                                      }else{
                                          if(temp->right) { childend=chidstart=temp->right; }
                                      }
                                      LevelTraverse(parent,chidstart,childend);
                                  }else if(parent && chidstart){ //we are in mid of some level processing
                                      Node* temp=parent; parent=parent->next;
                                      if(temp->left) { childend->next=temp->left; childend=temp->left; }
                                      if(temp->right) { childend->next=temp->right; childend=temp->right; }
                                      LevelTraverse(parent,chidstart,childend);
                                  }
                              }
                          

                          //驱动代码:

                              Node* connect(Node* root) {
                                  if(!root) return NULL;
                                  Node* parent; Node* childs, *childe; parent=childs=childe=NULL;
                                  parent=root;
                                  LevelTraverse(parent, childs, childe);
                                  return root;
                              }
                          

                          【讨论】:

                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2013-08-02
                            • 2011-01-31
                            • 1970-01-01
                            • 2018-02-18
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多