C# 实现DFS(深度优先遍历)的三种方式
有一个美丽的传说:所有递归都能用循环代替——DFS、Backtracking也不例外……真的是这样吗?今天就为您揭开迷底!
正文
using System; using System.Collections.Generic; using System.Linq; namespace HelloRider { // 二叉树结点 class Node { public int Val { get; set; } // 值 public Node Right { get; set; } // 右子树 public Node Left { get; set; } // 左子树 // 结点构造函数 public Node(int val) { Val = val; } } class Program { static void Main(string[] args) { Node root = BuildTree(); DFS_BTVersion(root); Console.ReadKey(); } // 构建一个二叉树 static Node BuildTree() { var root = new Node(1); root.Left = new Node(2); root.Left.Left = new Node(4); root.Left.Right = new Node(5); root.Right = new Node(3); root.Right.Left = new Node(6); root.Right.Right = new Node(7); return root; } // 递归前序遍历 static void DFS1(Node root) { if (root == null) { return; } Console.WriteLine(root.Val); DFS1(root.Left); DFS1(root.Right); } // 循环版前序遍历,但是有个缺陷就是不能方便的实现中序和后序 static void DFS2(Node root) { var stack = new Stack<Node>(); stack.Push(root); while (stack.Count > 0) { var head = stack.Pop(); Console.WriteLine(head.Val); if(head.Right != null) stack.Push(head.Right); if(head.Left != null) stack.Push(head.Left); } } // 先了解一下BackTrading 回溯获取所有路径,再来学习用BT做DFS static List<List<Node>> BT(Node root) { var result = new List<List<Node>>(); // 存储所有路径 var stack = new Stack<Node>(); // 存储单条路径 var set = new HashSet<Node>(); // 单独创建一个集合用来判断某个结点是否访问过 stack.Push(root); while (stack.Count > 0) { var head = stack.Peek(); // 获取栈顶结点,peek不会删除栈顶结点(与pop的区别) // 如果左结点不为空且没有访问过 if (head.Left != null && set.Add(head.Left)) { // 则将结点加入路径 stack.Push(head.Left); continue; } if (head.Right != null && set.Add(head.Right)) { stack.Push(head.Right); continue; } // 左右结点都为空,则为一条完整的路径 if (head.Left == null && head.Right == null) { var path = new List<Node>(stack); path.Reverse();// 由于栈是先进后出的,里面的结点是逆序存储的,所以需要将路径反转一下 result.Add(path); } // 返回上一结点 stack.Pop(); } return result; } // BT版的DFS(完整版,可以方便的做先序、中序、后序) static void DFS_BTVersion(Node root) { var stack = new Stack<Node>(); var set = new HashSet<Node>(); var accessed = new HashSet<Node>(); // 控制结点的输出,如果该结点之前已经输出过则不再输出 stack.Push(root); while (stack.Count > 0) { var head = stack.Peek(); // 此处输出为先序遍历 if (accessed.Add(head)) Console.WriteLine(head.Val); if (head.Left != null && set.Add(head.Left)) { stack.Push(head.Left); continue; } // 此处输出为中序遍历 // if (accessed.Add(head)) // Console.WriteLine(head.Val); if (head.Right != null && set.Add(head.Right)) { stack.Push(head.Right); continue; } // 此处输出为后序遍历 // if (accessed.Add(head)) // Console.WriteLine(head.Val); stack.Pop(); } } } }