树的各种遍历写法总结(C#版)

这篇博客汇总了树的前序、中序、后序遍历的C#实现,包括递归和迭代两种方法,并特别介绍了Morris遍历的思路和代码,旨在为C#开发者提供便捷的参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树的各种遍历写法总结(C#版)

兄弟们,我总结了各种各样树的遍历。方便大家去快速的查找,而且我发现C sharp的内容在网络上真的相较于JAVA和C++来说确实要少好多一些。咱也得出点力呀

树的遍历非常重的确需要的牢记。以下内容整理自《代码随想录》。参加算法训练营的一个小总结。因为在写各种数的便利的时候,发现树的便利花样繁多。有各种各样的方法。卡哥已经按方法不同前中后续讲完了。此处便是将前序遍历,中序遍历,后续遍历把他们的方法列出来,加以总结。方便在有需要的时候查阅。Morris遍历是在leeetcode抄的不一定对(我都能发现力扣有错别字,有错完全可能,运行不了也正常)。代码稍加修改可能就可以运行了。

前序、中序、后序的递归写法

树的遍历,最常用的还得是递归的遍历方法。好写也好记。最开始对于递归看起来可能有些吃力,但是只要看明白的原理就非常简单了。

这里有个理解的小技巧。这个理解方式有点类似于俄罗斯套娃的全部打开。我们可以把俄罗斯套娃看作一个大的套娃外壳和里面一系列套娃外壳所组成的一个小套娃。而我们可以把最外层套娃外壳拿去,里面的套娃也可以看做一个大的套娃外壳。和一系列套娃外壳所组成的小套娃。反复把外壳拿去。最终所有的套娃会被我们拆成一排也就完成了套娃的“遍历”。

同样树可以看作一个大套娃,里边的两枝看做两个小套娃。不同的遍历顺序就相当于把最大的那个套娃外壳放在两个小套娃的左边,中间还是右边。以前序为例。

第一步,将大套娃拆出来放在左边两个小套娃依次排开。

下一步,将未拆完的套娃。拆一层将拆出来的壳,放在所拆位置的左边,两个小小套娃第一个放在刚才拆的位置,第二个放在所拆位置右边。

下N步,重复操作上一步直至所有套娃都只有一层。遍历结束

下面是递归写法代码

public class Solution
{
    //此函数包含了前中后序稍加修改即可
    public IList<int> Traversal(TreeNode root)
    {
        IList<int> result = new IList<int>();
        preorder(root, result);
        return result;

        void preorder(TreeNode root, IList<int> result)
        {
            if (root == null)
            {
                return;
            }
            result.Add(root.val);
            preorder(root.left, result);
            preorder(root.right, result);
        }

        void inorder(TreeNode root, IList<int> result)
        {
            if (root == null)
            {
                return;
            }

            inorder(root.left, result);
            result.Add(root.val);
            inorder(root.right, result);
        }
        void postorder(TreeNode root, IList<int> result)
        {
            if (root == null)
            {
                return;
            }
            postorder(root.left, result);
            postorder(root.right, result);
            result.Add(root.val);
        }
    }
}

前序、中序、后序的迭代写法(前中后写法不统一)

很多递归的程序都可经过一定的修改为非递归的写法,以下是树的非递归的写法——树的迭代遍历。下面的三种写法前中,后续都不太一致。

前序遍历

public class Solution
{
    public IList<int> PreorderTraversal(TreeNode root)
    {
        IList<int> traversal = new List<int>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode node = root;
        while (stack.Count > 0 || node != null)
        {
            while (node != null)
            {
                traversal.Add(node.val);
                stack.Push(node);
                node = node.left;
            }
            node = stack.Pop().right;
        }
        return traversal;
    }
}

中序遍历

public class Solution
{
    public IList<int> InorderTraversal(TreeNode root)
    {
        IList<int> result = new List<int>();
        if (root == null)
        {
            return result;
        }
        Stack<TreeNode> stack = new Stack<TreeNode>();
        TreeNode node = root;
        while (node != null || stack.Count() != 0)
        {
            if (node != null)
            {
                stack.Push(node);
                node = node.left;
            }
            else
            {
                node = stack.Pop();
                result.Add(node.val);
                node = node.right;
            }
        }
        return result;
    }
}

后序遍历

public class Solution {
        public IList<int> PostorderTraversal(TreeNode root) {
            List<int> result = new();
            if (root == null) return result;

            Stack<TreeNode> stack = new();
            stack.Push(root);
            TreeNode tempNode;
            while(stack.Count() != 0) {
                tempNode = stack.Pop();
                result.Add(tempNode.val);
                if (tempNode.left != null) stack.Push(tempNode.left);
                if (tempNode.right != null) stack.Push(tempNode.right);
            }
            result.Reverse();

            return result;
        }
}

前序、中序、后序的迭代写法(前中后写法统一)

以下是树的非递归的写法——树的迭代遍历。前面的三种写法前中后规律不太一致。使用了在需要遍历的地方。用空指针做标记的方式。完成了前,中,后续三种写法的统一。

前序遍历

class Solution
{
    public IList<int> PreorderTraversal(TreeNode root)
    {
        IList<int> result = new List<int>();
        if (root == null)
        {
            return result;
        }
        Stack<TreeNode> stack = new Stack<TreeNode>();

        stack.Push(root);

        while (stack.Count() != 0)
        {
            TreeNode node = stack.Peek();
            if (node != null)
            {
                stack.Pop();
                if (node.right != null)
                {
                    stack.Push(node.right);
                }
                if (node.left != null)
                {
                    stack.Push(node.left);
                }
                stack.Push(node);
                stack.Push(null);
            }
            else
            {
                stack.Pop();
                node = stack.Pop();
                result.Add(node.val);
            }
        }
        return result;
    }
}

中序遍历

class Solution
{
    public IList<int> InorderTraversal(TreeNode root)
    {
        IList<int> result = new List<int>();
        if (root == null)
        {
            return result;
        }
        Stack<TreeNode> stack = new Stack<TreeNode>();

        stack.Push(root);

        while (stack.Count() != 0)
        {
            TreeNode node = stack.Peek();
            if (node != null)
            {
                stack.Pop();
                if (node.right != null)
                {
                    stack.Push(node.right);
                }
              
                stack.Push(node);
                stack.Push(null);

                  if (node.left != null)
                {
                    stack.Push(node.left);
                }
            }
            else
            {
                stack.Pop();
                node = stack.Pop();
                result.Add(node.val);
            }
        }
        return result;
    }
}

后序遍历

class Solution
{
    public IList<int> PostorderTraversal(TreeNode root)
    {
        IList<int> result = new List<int>();
        if (root == null)
        {
            return result;
        }
        Stack<TreeNode> stack = new Stack<TreeNode>();

        stack.Push(root);

        while (stack.Count() != 0)
        {
            TreeNode node = stack.Peek();
            if (node != null)
            {
                stack.Pop();
                
                 stack.Push(node);
                stack.Push(null);

                if (node.right != null)
                {
                    stack.Push(node.right);
                }
              
               
                  if (node.left != null)
                {
                    stack.Push(node.left);
                }

            }
            else
            {
                stack.Pop();
                node = stack.Pop();
                result.Add(node.val);
            }
        }
        return result;
    }
}

前序、中序、后序的迭代写法(Morris 遍历)

Morris 遍历是一种只占用常数空间来实现前序遍历。
这种方法由 J. H. Morris 在 1979 年的论文「Traversing Binary Trees Simply and Cheaply」中首次提出,因此被称为 Morris 遍历。Morris 遍历的核心思想是利用树的大量空闲指针,实现空间开销的极限缩减。其前序遍历规则总结如下:
新建临时节点,令该节点为 root;
如果当前节点的左子节点为空,将当前节点加入答案,并遍历当前节点的右子节点;如果当前节点的左子节点不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点:如果前驱节点的右子节点为空,将前驱节点的右子节点设置为当前节点。然后将当前节点加入答案,并将前驱节点的右子节点更新为当前节点。当前节点更新为当前节点的左子节点。如果前驱节点的右子节点为当前节点,将它的右子节点重新设为空。当前节点更新为当前节点的右子节点。重复步骤 2 和步骤 3,直到遍历结束。这样我们利用 Morris 遍历的方法,前序遍历该二叉树,即可实现线性时间与常数空间的遍历。

前序遍历

class Solution
{
    public IList<int> PreorderTraversal(TreeNode root)
    {
        IList<int> result = new List<int>();
        if (root == null)
        {
            return result;
        }

        TreeNode curr = root;
        TreeNode currLeft = null;
        
        while (curr != null)
        {
            currLeft = curr.left;
            if (currLeft != null)
            {
                while (currLeft.right != null && currLeft.right != curr)
                {
                    currLeft = currLeft.right;
                }

                if (currLeft.right == null)
                {
                    result.Add(curr.val);
                    currLeft.right = curr;
                    curr = curr.left;
                    continue;
                }
                else
                {
                    currLeft.right = null;
                }
                
            }
            else
            {
                result.Add(curr.val);
            }
            curr = curr.right;
        }
        return result;
    }
}

中序遍历

//中序 Morris
class Solution {
    public IList<int> InorderTraversal(TreeNode root) {
        IList<int>  list = new List<int>();
        if (root == null) {
            return list;
        }
        TreeNode p1 = root;
        TreeNode p2 = null;
        while (p1 != null) {
            p2  = p1.left;
            if (p2 != null) {
                while (p2.right != null && p2.right != p1) {
                    p2 = p2.right;
                }
                if (p2.right == null) {
                    p2.right = p1;
                    p1 = p1.left;
                    continue;                   
                } else {
                    p2.right  = null;
                }
            }
            list.Add(p1.val);
            p1 = p1.right;
        }
        return list;
    }
}

后序遍历

class Solution { 
    IList<int> list = new List<int>();
    public IList<int> PostorderTraversal(TreeNode root) {
        if (root == null) {
            return list;
        }
        TreeNode p1 = root;
        TreeNode p2 = null;
        while (p1 != null) {
            p2 = p1.left;
            if (p2 != null) {
                 while (p2.right != null && p2.right != p1) {
                     p2 = p2.right;
                 }
                 if (p2.right == null) {
                     p2.right = p1;
                     p1 = p1.left;
                     continue;
                 } else {
                     p2.right = null;
                     PostMorris(p1.left);
                 }
            } 
            p1 = p1.right;
        }
        //以根节点为起点的链表
        PostMorris(root);
        return list;
    }
    public void PostMorris(TreeNode root) {
        //翻转链表
        TreeNode reverseNode = ReverseList(root);
        //从后往前遍历
        TreeNode cur = reverseNode;
        while (cur != null) {
            list.Add(cur.val);
            cur = cur.right;
        }
        //翻转回来
        ReverseList(reverseNode);
    }
    public TreeNode ReverseList(TreeNode head) {
        TreeNode cur = head;
        TreeNode pre = null;
        while (cur != null) {
            TreeNode next = cur.right;
            cur.right = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
}            
<think>好的,我需要帮助用户了解C#中的语法糖以及LINQ语句的使用方法。首先,我应该先定义什么是语法糖,让用户有个基本的概念。语法糖指的是那些让代码更简洁、易读的语法特性,比如自动属性、对象初始化器等。然后,再引入LINQ,作为C#中重要的语法糖之一。 接下来,我需要详细讲解LINQ的基础用法。根据用户提供的引用内容,LINQ可以用于查询各种数据源,包括数组、列表、数据库等。我应该分点说明,比如基本查询、条件过滤、排序、分组和连接查询,每个部分都给出示例代码,并解释这些代码的作用。例如,在基本查询中,使用from...in...select结构,这可能和SQL类似,用户容易理解。 然后,高级特性部分需要包括匿名类型、延迟执行和Lambda表达式结合。特别是Lambda表达式,因为LINQ的方法语法经常和Lambda一起使用,比如Where(x => x > 5)。这里需要说明两种写法的区别和适用场景。 LINQ to SQL部分,用户提供的引用中有示例,我需要解释DbContext的使用,以及如何通过LINQ查询数据库,并强调延迟执行和实际执行查询的时机,比如在foreach循环时才会真正执行查询。 此外,用户可能对语法糖的其他例子感兴趣,比如自动属性、空值判断等,这些可以作为补充内容,帮助用户更全面理解C#的语法糖。 在编写过程中,要确保所有的数学表达式用$...$格式,虽然这个问题可能不涉及太多数学内容,但如果有变量或表达式,比如在解释表达式时,可能需要用到。同时,独立公式需要用$$...$$,但这里可能不太需要。 最后,生成相关问题,考虑用户可能想深入了解的方面,比如性能优化、不同LINQ提供器的区别、与Lambda的关系等。这些问题应该自然延伸当前内容,帮助用户进一步学习。 需要检查引用是否正确,用户提供的引用1、2、3都是关于LINQ的,特别是引用3提到了LINQ to SQL的示例,应该在相应部分引用。确保每个引用标识符正确对应,比如在讲解LINQ to SQL时添加[^3]。 可能还需要注意代码格式的正确性,比如用户示例中的代码块使用三个反引号加语言标识,需要保持统一。同时,避免使用Markdown格式,但根据用户示例,代码块是允许的,所以没问题。 总结下来,结构应该是: 1. 语法糖的定义和常见例子 2. LINQ的基础用法,分点举例 3. LINQ的高级特性 4. LINQ to SQL的示例和解释 5. 其他语法糖的例子 6. 相关问题 确保每个部分简明扼要,示例代码清晰,解释到位,引用正确。同时,保持语言口语化,避免过于技术化的术语,让用户容易理解。</think>### C# 语法糖与 LINQ 语句使用教程 #### 一、语法糖简介 语法糖(Syntactic Sugar)是编程语言中用于简化代码书写的语法特性,它不改变语言功能,但能提升代码可读性和编写效率。例如,C# 的**自动属性**、**对象初始化器**、**空值条件运算符**(`?.`)等均属于语法糖。 --- #### 二、LINQ 基础用法 LINQ(Language Integrated Query)是 C# 中重要的语法糖,支持类似 SQL 的语法对数据集合进行查询[^1][^2]。 ##### 1. 基本查询 ```csharp var numbers = new List<int> { 1, 2, 3, 4, 5 }; var evenNumbers = from num in numbers where num % 2 == 0 select num; // 输出:2, 4 ``` ##### 2. 条件过滤(`Where`) ```csharp var filtered = numbers.Where(x => x > 3); // 输出:4, 5 ``` ##### 3. 排序(`OrderBy`) ```csharp var sorted = from num in numbers orderby num descending select num; // 输出:5, 4, 3, 2, 1 ``` ##### 4. 分组(`GroupBy`) ```csharp var words = new[] { "apple", "banana", "cherry" }; var grouped = from word in words group word by word.Length; // 按单词长度分组 ``` ##### 5. 连接查询(`Join`) ```csharp var students = new List<Student> { /* 学生数据 */ }; var courses = new List<Course> { /* 课程数据 */ }; var query = from s in students join c in courses on s.CourseId equals c.Id select new { s.Name, c.CourseName }; ``` --- #### 三、LINQ 高级特性 1. **匿名类型** 通过 `select new { ... }` 动态创建临时对象: ```csharp var result = from num in numbers select new { Number = num, Square = num * num }; ``` 2. **延迟执行** LINQ 查询默认延迟执行,直到实际遍历结果时才触发查询[^2]: ```csharp var query = numbers.Where(x => x > 2); numbers.Add(6); // 修改数据后,query 结果会包含 6 ``` 3. **与 Lambda 表达式结合** 使用方法语法简化代码: ```csharp var sum = numbers.Sum(); var max = numbers.Max(x => x * 2); ``` --- #### 四、LINQ to SQL 示例 通过 LINQ 查询关系数据库(需配置 `DbContext`)[^3]: ```csharp using (var db = new MyDbContext()) { var expensiveProducts = from p in db.Products where p.Price > 100 select p; foreach (var product in expensiveProducts) { Console.WriteLine($"{product.Name}: {product.Price}"); } } ``` --- #### 五、其他语法糖示例 1. **空值条件运算符** ```csharp string name = person?.Name; // 若 person 为 null,返回 null ``` 2. **集合初始化器** ```csharp var list = new List<int> { 1, 2, 3 }; ``` 3. **模式匹配**(C# 7+) ```csharp if (obj is int count && count > 5) { ... } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Javie Deng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值