在上一篇文章 小小c#算法题 - 10 - 求树的深度中,用到了树的数据结构,树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集。但在那篇文章中,只是简单地把结点组合到了一块。

这次,我们来简单定义一棵二叉树的数据结构,并实现其先序(根)遍历、中序(根)遍历、后序(根)遍历算法。

下面先来看一下先序遍历,中序遍历,后序遍历的定义:

先序遍历:

若二叉树为空,则空操作,否则

(1)访问根结点;

(2)先序遍历左子树;

(3)先序遍历右子树;

中序遍历:

若二叉树为空,则空操作,否则

(1)中序遍历左子树;

(2)访问根结点;

(3)中序遍历右子树;

后序遍历:

若二叉树为空,则空操作,否则

(1)后序遍历左子树;

(2)后序遍历右子树;

(3)访问根结点;

从上面定义可以看出,三种遍历算法都是一个递归的过程,临界条件为根结点为null,即要遍历的树为空树。

树既然是结点的数据结构,那么就先来定义结点的数据结构,下面是二叉树的结点结构:

    public class Node
{
public int value;
public Node leftChild;
public Node rightChild;
public void Display()
{
Console.Write(value + " ");
}
}

通过代码可以看到,一个结点由以下元素组成:值(value);左孩子的引用(leftChild);右孩子的引用(rightChild);显示方法(Display),这个方法用于测试与验证工作,用来输出结点的值到终端。其中值(value)属性的定义可以为任何复杂类型的结构,这里只是为了方便,定义为了一个整型变量。

下面就来定义二叉树了,其实树可以有很多属性或方法,比如,可以有插入一个结点作为一个结点结点的左孩子,右孩子的方法,求一个结点的兄弟结点的方法,求树深度的方法,求一个结点父结点的方法等等。在这篇文章中,我们只给出1个属性和5个方法。树的根结点属性,构造函数,插入结点方法,先序遍历方法,中序遍历方法,后序遍历方法。

先给出树的完整代码:

    public class BinaryTree
{
public Node root; public BinaryTree()
{
root = null;
} public void Insert(int data)
{
Node newNode = new Node() { value = data };
if (root == null)
{
root = newNode;
}
else
{
Node current = root;
Node parent;
while (true)
{
parent = current;
if (data < current.value)
{
current = current.leftChild;
if (current == null)
{
parent.leftChild = newNode;
break;
}
}
else
{
current = current.rightChild;
if (current == null)
{
parent.rightChild = newNode;
break;
}
}
}
}
} // 先序遍历
public static void PreOrderTraverse(Node root)
{
if (root != null) //若二叉树为空,则空操作,否则
{
root.Display(); // 访问根结点
PreOrderTraverse(root.leftChild); //先序遍历左子树
PreOrderTraverse(root.rightChild); //先序遍历右子树
}
} // 中序遍历
public static void InOrderTraverse(Node root)
{
if (root != null) //若二叉树为空,则空操作,否则
{
InOrderTraverse(root.leftChild); //中序遍历左子树
root.Display(); //访问根结点
InOrderTraverse(root.rightChild); //中序遍历右子树
}
} // 后序遍历
public static void PostOrderTraverse(Node root)
{
if (root != null) //若二叉树为空,则空操作,否则
{
PostOrderTraverse(root.leftChild); //后序遍历左子树
PostOrderTraverse(root.rightChild); //后序遍历右子树
root.Display(); //访问根结点
}
}
}

上面代码中,插入结点的代码逻辑可自己定义,你也可以指定一个另外一个结点,把要插入的结点作为其左孩子或右孩子等。这里的插入结点方法比较简单,按照这个方法可以构造一棵二叉排序树,又叫平衡二叉树,其定义为:这棵树或者是一棵空树;或者是具有下列性质的二叉树:(1)若它的左子树不为空,则左子树上所有结点的值均小于它的根结点;(2)若它的右子树不为空,则右子树上所有结点的值均大于它的根结点;(3)它的左、右子树也分别为二叉排序树。

对于三种遍历算法的代码,是按照其定义去写的,由于是递归调用,所以代码异常简洁。理解递归的最好办法是用简单的数据走一遍代码,所以,如果你没能很好地理解,可以采用这个方法去读代码。

下面是main方法中的调用代码,其中包含了构造二叉树的过程:

        static void Main(string[] args)
{
BinaryTree myBinaryTree = new BinaryTree();
myBinaryTree.Insert();
myBinaryTree.Insert();
myBinaryTree.Insert();
myBinaryTree.Insert();
myBinaryTree.Insert();
myBinaryTree.Insert();
myBinaryTree.Insert(); Console.Write("PreOrder traverse: ");
BinaryTree.PreOrderTraverse(myBinaryTree.root);
Console.WriteLine();// 9 5 3 2 6 7 12
Console.Write("InOrder traverse: ");
BinaryTree.InOrderTraverse(myBinaryTree.root);
Console.WriteLine();// 2 3 5 6 7 9 12
Console.Write("PostOrder traverse: ");
BinaryTree.PostOrderTraverse(myBinaryTree.root);
Console.WriteLine();// 2 3 7 6 5 12 9 Console.ReadLine();
}

这棵二叉排序树共有7个结点组成,按照Insert方法的代码逻辑,其构成了如下的一棵树:

然后分别调用其先序遍历,中序遍历,后序遍历方法,结点输出的顺序应该如下所示:

先序遍历:9 5 3 2 6 7 12

中序遍历:2 3 5 6 7 9 12

后序遍历:2 3 7 6 5 12 9

小小c#算法题 - 11 - 二叉树的构造及先序遍历、中序遍历、后序遍历的更多相关文章

  1. JS刷算法题:二叉树

    Q1.翻转二叉树(easy) 如题所示 示例: 输入: 4 / \ 2 7 / \ / \ 1 3 6 9 输出: 4 / \ 7 2 / \ / \ 9 6 3 1 来源:力扣(LeetCode) ...

  2. LeetCode 145. 二叉树的后序遍历 (用栈实现后序遍历二叉树的非递归算法)

    题目链接:https://leetcode-cn.com/problems/binary-tree-postorder-traversal/ 给定一个二叉树,返回它的 后序 遍历. 示例: 输入: [ ...

  3. DS实验题 Order 已知父节点和中序遍历求前、后序

    题目: 思路: 这题是比较典型的树的遍历问题,思路就是将中序遍历作为位置的判断依据,假设有个节点A和它的父亲Afa,那么如果A和Afa的顺序在中序遍历中是先A后Afa,则A是Afa的左儿子,否则是右儿 ...

  4. 小小c#算法题 - 10 - 求树的深度

    树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集.关于树的基本概念不再作过多陈述,相信大家都有了解,如有遗忘,可翻书或去其他网页浏览以温习. 树中 ...

  5. [面试算法题]比较二叉树异同-leetcode学习之旅(5)

    问题描述 Given two binary trees, write a function to check if they are equal or not. Two binary trees ar ...

  6. 小小c#算法题 - 12 - Joseph Circle(约瑟夫环)

    约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数(从1开始报数),数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又 ...

  7. 小小c#算法题 - 9 - 基数排序 (Radix Sort)

    基数排序和前几篇博客中写到的排序方法完全不同.前面几种排序方法主要是通过关键字间的比较和移动记录这两种操作来实现排序的,而实现基数排序不需要进行记录项间的比较.而是把关键字按一定规则分布在不同的区域, ...

  8. 小小c#算法题 - 8 - 归并排序 (Merging Sort)

    “归并”的含义是将两个或两个以上的有序序列组合成一个新的有序序列.这个“归并”可以在O(n+m)的数量级上实现,但这同时也需要O(n+m)的空间复杂度.具体为:首先分配一个新的长度为n+m的空序列,然 ...

  9. 小小c#算法题 - 7 - 堆排序 (Heap Sort)

    在讨论堆排序之前,我们先来讨论一下另外一种排序算法——插入排序.插入排序的逻辑相当简单,先遍历一遍数组找到最小值,然后将这个最小值跟第一个元素交换.然后遍历第一个元素之后的n-1个元素,得到这n-1个 ...

随机推荐

  1. C#进阶之路(六):表达式进行类的赋值

    好久没更新这个系列了,最近看.NET CORE源码的时候,发现他的依赖注入模块的很多地方用了表达式拼接实现的.比如如下代码 private Expression<Func<ServiceP ...

  2. CODE FESTIVAL 2017 qual A--C - Palindromic Matrix(模拟所有情况,注意细节)

    个人心得:其实本来这题是有规律的不过当时已经将整个模拟过程都构思出来了,就打算试试,将每个字符和总和用优先队列 装起来,然后枚举每个点,同时进行位置标志,此时需要多少个点的时候拿出最大的和出来,若不满 ...

  3. 三、Jmeter--HTTP请求默认值(HTTP Request Defaults)和访问地址参数化

    一.HTTP请求默认值(HTTP Request Defaults) 1. 在本地搭建了一个wordpress开源论坛,那么我每次访问论坛的地址(服务器名称或IP)是不变的,端口也是不变的,协议也是不 ...

  4. 第一章计算机网络和因特网-day02

    1.互联网中的时延:处理时延.排队时延.传输时延.传播时延. 处理时延:检查分组首部和决定该分组导向何处的时间. 排队时延:分组在链路上等待传输的时延. 传输时延:分组经过路由器与交换机的过程的时延. ...

  5. SpringCloud基础教程学习记录

    这个学习记录是学习自翟永超前辈的SpringCloud的基础教程. 自己写这个教程的目的主要是在于,想要更凝练总结一些其中的一些实用点,顺便做个汇总,这样自己在复习查看的时候更加方便,也能顺着自己的思 ...

  6. phyton方面相关书籍

    0基础:<简明PYTHON教程>http://linux.chinaitlab.com/manual/Python_chinese/<与孩子一起学编程>http://book. ...

  7. 机器学习:使用scikit-learn库中的网格搜索调参

    一.scikit-learn库中的网格搜索调参 1)网格搜索的目的: 找到最佳分类器及其参数: 2)网格搜索的步骤: 得到原始数据 切分原始数据 创建/调用机器学习算法对象 调用并实例化scikit- ...

  8. 二叉搜索树的结构(30 分) PTA 模拟+字符串处理 二叉搜索树的节点插入和非递归遍历

    二叉搜索树的结构(30 分) PTA 模拟+字符串处理 二叉搜索树的节点插入和非递归遍历   二叉搜索树的结构(30 分) 二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则 ...

  9. 【OK210试用体验】进阶篇(2)视频图像采集之MJPG-streamer编译(arm移植)

    上一篇([OK210试用体验]进阶篇(1)视频图像采集之MJPG-streamer编译(Ubuntu系统下))进行了MJPG-streamer在Ubuntu下的编译及测试,这一篇针对OK210,进行a ...

  10. 侯捷STL学习(五)--allocator和容器之间的实现关系

    第十一节 分配器 STL源码学习----内存管理 分配器的好坏影响到容器的性能 operator new()里面调用malloc D:\Program Files (x86)\Microsoft Vi ...