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

 

posted @ 2022-04-22 13:21  SilverFox8588  阅读(475)  评论(0编辑  收藏  举报