之前项目用到了树形存储的配置结构,多叉树除了用于配置结构外,还有用于复杂文件目录结构。
二叉树一个比较经典的应用是红黑树,查找、插入、删除的时间复杂度最坏为O(log n),效率高。
很多语言的hashmap用红黑树来做。
二叉树的基础操作学会了,多叉树的基础操作也就学会了。
原文: C#代码创建二叉树以及遍历二叉树
二叉树的前中后序遍历,是根据遍历时根节点在其左右子树中的相对顺序定义的。
对于每一个节点来说,前序是只先遍历根节点,然后左子树,然后右子树
中序是先左子树,然后根节点,然后右子树
后序是先左子树,然后右子树,然后根节点
以下图所属树来做例子。
using System;
using System.Collections.Generic;
using System.Text;
namespace BinaryDemo
{
public class TreeNode
{
/*
* 树的知识点
* 树结点 根结点 结点子树
* 结点的度 结点关系 结点层次
* 树的深度/高度
*/
//结点下标 结点下标字符串数据 左子树 右子树
private int index;
private string data;
private TreeNode leftChild;
private TreeNode rightChild;
private TreeNode parent;
///
/// 有参构造结点下标 结点下标的字符串数据
///
///
///
public TreeNode(int index, string data)
{
this.index = index;
this.data = data;
this.leftChild = null;
this.rightChild = null;
}
public int getIndex()
{
return index;
}
public void setIndex(int index)
{
this.index = index;
}
//拿到左右子串的数据
public String getData()
{
return data;
}
public void setData(String data)
{
this.data = data;
}
//拿到左子树
public TreeNode getLeftChild()
{
return leftChild;
}
public void setLeftChild(TreeNode leftChild)
{
this.leftChild = leftChild;
}
//拿到右子树
public TreeNode getRightChild()
{
return rightChild;
}
public void setRightChild(TreeNode rightChild)
{
this.rightChild = rightChild;
}
public TreeNode getParent()
{
return parent;
}
public void setParent(TreeNode parent)
{
this.parent = parent;
}
//快捷键生成的字段get和set
public int Index { get => index; set => index = value; }
public string Data { get => data; set => data = value; }
public TreeNode LeftChild { get => leftChild; set => leftChild = value; }
public TreeNode RightChild { get => rightChild; set => rightChild = value; }
public TreeNode Parent { get => parent; set => parent = value; }
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace BinaryDemo
{
public class BinaryTree
{
//根结点
public TreeNode root = null;
public static string[] str;
public static int count;
///
/// 无参构造设置根结点并赋值数据
///
public BinaryTree()
{
root = new TreeNode(1,"A");
}
///
/// 构建二叉树的方法 B C D E F
/// 手动的构建一棵二叉树 很快就可以得出这个二叉树的结构
///
public void CreateBinaryTree()
{
TreeNode nodeb = new TreeNode(2,"B");
TreeNode nodec = new TreeNode(3, "C");
TreeNode noded = new TreeNode(4, "D");
TreeNode nodee = new TreeNode(5, "E");
TreeNode nodef = new TreeNode(6, "F");
root.LeftChild = nodeb;
root.RightChild = nodec;
nodeb.LeftChild = noded;
nodeb.RightChild = nodee;
nodec.RightChild = nodef;
}
///
/// 先序遍历 --迭代
/// 若二叉树为空树直接返回,先序遍历的特点根左右
///
public void PreOrder(TreeNode node)
{
if (node == null)
{
return;
}
else
{
//node.getData()我们可以获取到二叉树的根结点的数据
Console.WriteLine("先序遍历" + "\t迭代" + node.getData());
//得到左子树的数据
PreOrder(node.LeftChild);
//得到右子树的数据
PreOrder(node.RightChild);
}
}
打印结点A,此时属于第零层递归,结点A左子树进入第一层递归
第一层递归中,打印结点A左子树的根结点B,结点B左子树进入第二层递归
第二层递归中,打印结点B左子树的根结点D,结点D左子树进入第三层递归
第三层递归中,因为结点D左子树为空,返回第二层递归,返回后,代码运行到了访问结点D右子树部分,结点D右子树进入第三层递归
因为结点D右子树为空,返回第二层递归,第二层访问的是结点D,返回后已经进入第二层函数尽头,第二层递归结束,返回第一层递归,第一层递归访问的是结点B
返回到第一层递归中,代码到了结点B右子树的部分,结点B右子树进入第二层递归
第二层递归中,打印结点B右子树的根结点E,然后结点E左子树进入第三层递归
第三层递归中,结点E左子树为空,返回第二层递归,返回时第二层递归的代码到了访问结点E右子树部分,进入第三层递归
进入第三层递归中,结点E右子树为空,返回第二层递归。
返回到第二层的时候函数已经进入结束部分,再次返回第一层递归,返回到第一层的时候,第一层递归已经遍历完B右子树,函数进入结束部分,返回第零层递归,第零层递归访问的是结点A。
返回到第零层递归后,代码进入到访问A的右子树部分,结点A右子树进入第一层递归
第一层递归中, 打印结点A右子树的根结点C,结点C左子树进入第二层递归
第二层递归中,结点C左子树为空,返回第一层递归,返回后,代码到了访问第一层递归的C的右子树部分,结点C右子树进入第二层递归
第二层递归中, 打印结点C右子树的根结点F,结点F左子树进入第三层递归
第三层递归中,结点F左子树为空,返回第二层递归
返回到第二层递归的时候,代码到了访问第二层递归的F的右子树部分,结点F右子树进入第三层递归
第三层递归中,结点F右子树为空,返回第二层递归
返回到第二层,第二层访问的是结点F,此时结点F左右子树都已经访问完毕,第二层递归函数已经进入函数结尾,第二层递归结束,返回第一层递归。
返回到第一层,第一层访问的是结点C,此时遍历完C右子树,第一层递归函数进入结束部分,返回第零层递归。
返回到第零层,第零层递归访问的是结点A,此时遍历完A右子树,第零层递归函数进入结束部分,所有遍历结束。
这些流程下来,打印的顺序依次为ABDECF,符合前序遍历的要求。
///
/// 中序遍历--递归
/// 若二叉树为空树直接返回,中序遍历的特点左根右
///
///
public void MidOrder(TreeNode node)
{
if (node == null)
{
return;
}
else
{
MidOrder(node.LeftChild);
Console.WriteLine("中序遍历" + "\t迭代" + node.getData());
MidOrder(node.RightChild);
}
}
第零层递归,访问的是结点A,结点A左子树进入第一层递归
第一层递归中,访问的是结点A左子树的根节点是结点B,结点B左子树进入第二层递归
第二层递归中,访问的是结点B左子树的根节点是结点D,结点D左子树进入第三层递归
第三层递归中,因为结点D左子树为空,返回第二层递归。
返回第二层递归后,代码进入打印当前层结点部分,打印第二层访问的结点D,然后代码运行到了访问结点D右子树部分,结点D右子树进入第三层递归。
第三层递归中,因为结点D右子树为空,返回第二层递归。
第二层访问的是结点D,返回后已经进入第二层函数尽头,第二层递归结束,返回第一层递归,第一层递归访问的是结点B
返回到第一层递归中,代码到了打印当前访问结点的部分,当前访问结点是B,打印结点B,然后结点B右子树进入第二层递归
第二层递归中,访问的是结点B右子树的根结点E,然后结点E左子树进入第三层递归
第三层递归中,结点E左子树为空,返回第二层递归,返回时第二层递归的代码到了打印当前访问结点的部分,当前访问结点是E,打印结点E,然后访问结点E右子树部分,进入第三层递归
进入第三层递归中,结点E右子树为空,返回第二层递归。
返回到第二层的时候函数已经进入结束部分,再次返回第一层递归,返回到第一层的时候,第一层递归已经遍历完B右子树,函数进入结束部分,返回第零层递归,第零层递归访问的是结点A。
返回到第零层递归后,代码到了打印当前访问结点的部分,当前访问结点是A,打印结点A,然后代码进入到访问A的右子树部分,结点A右子树进入第一层递归
第一层递归中, 访问的是结点A右子树的根节点结点C,结点C左子树进入第二层递归
第二层递归中,结点C左子树为空,返回第一层递归,返回后,代码到了打印当前访问结点的部分,当前访问结点是C,打印结点C。
访问第一层递归的C的右子树部分,结点C右子树进入第二层递归
第二层递归中, 访问的是结点C右子树的根结点F,结点F左子树进入第三层递归
第三层递归中,结点F左子树为空,返回第二层递归
返回到第二层递归的时候,代码到了打印当前访问结点的部分,当前访问结点是F,打印结点F,然后代码到了访问第二层递归的F的右子树部分,结点F右子树进入第三层递归
第三层递归中,结点F右子树为空,返回第二层递归
返回到第二层,第二层访问的是结点F, 此时结点F左右子树都已经访问完毕,第二层递归函数已经进入函数结尾,第二层递归结束,返回第一层递归。
返回到第一层,第一层访问的是结点C,此时遍历完C右子树,第一层递归函数进入结束部分,返回第零层递归。
返回到第零层,第零层递归访问的是结点A,此时遍历完A右子树,第零层递归函数进入结束部分,所有遍历结束。
这些流程下来,打印的顺序依次为DBEACF,符合中序遍历的要求。
public void LastOrder(TreeNode node)
{
if (node == null)
{
return;
}
else
{
LastOrder(node.LeftChild);
LastOrder(node.RightChild);
Console.WriteLine("后序遍历" + "\t迭代" + node.getData());
}
}
第零层递归,访问的是结点A,结点A左子树进入第一层递归
第一层递归中,访问的是结点A左子树的根节点是结点B,结点B左子树进入第二层递归
第二层递归中,访问的是结点B左子树的根节点是结点D,结点D左子树进入第三层递归
第三层递归中,因为结点D左子树为空,返回第二层递归。
返回第二层递归后,第二层递归访问的是结点D,结点D右子树进入第三层递归。
第三层递归中,因为结点D右子树为空,返回第二层递归。
第二层访问的是结点D,返回后已经进入打印当前结点部分,打印第二层递归访问的结点D,第二层递归结束,然后第二层递归结束,返回第一层递归
返回到第一层递归中,代码到了访问当前递归结点的右子树的部分,当前第一层访问结点是B, 结点B右子树进入第二层递归
第二层递归中,访问的是结点B右子树的根结点E,然后结点E左子树进入第三层递归
第三层递归中,结点E左子树为空,返回第二层递归。
返回第二层递归时,代码到了访问当前访问结点的右子树部分,当前访问结点是E,访问结点E右子树部分,进入第三层递归
进入第三层递归中,结点E右子树为空,返回第二层递归。
第二层访问的是结点E,返回后结点E的左右子树都已经遍历完成,进入打印当前结点部分,当前结点部分是E,打印E,第二层递归结束,返回第一层递归。
返回第一层递归时,第一层递归已经将当前结点的左右子树都遍历完毕,这时打印当前遍历结点,当前遍历的结点是B,打印B,然后当前层遍历结束,返回第零层遍历
第零层遍历访问的是结点A,返回时结点A的左子树遍历结束,结点A的右子树进入第一层递归。
第一层递归,访问A的右子树的根节点C,然后第二层递归访问C的左子树
第二层递归中,结点E左子树为空,返回第一层递归
返回第一层递归时,代码到了访问当前结点的右子树部分,结点C的右子树进入第二层递归
第二层递归中,访问结点C的右子树的根节点F,然后进入第三层递归,访问F的左子树
第三层递归中,F的左子树为空,返回第二层
返回第二层时,代码到了访问当前结点的右子树部分,第三层递归访问F的右子树
第三层递归中,F的右子树为空,返回第二层
返回第二层时,代码走到了打印当前访问结点的部分,当前访问结点是F,打印F,然后第二层递归结束,返回第一层递归
返回第一层时,第一层递归已经将当前结点的左右子树都遍历完毕,代码走到了打印当前访问结点的部分,当前访问结点是C,打印C,然后第一层递归结束,返回第零层递归
返回第零层时,第零层递归已经将当前结点的左右子树都遍历完毕,代码走到了打印当前访问结点的部分,当前访问结点是A,打印A,然后第零层递归结束,所有递归结束
这些流程下来,打印的顺序依次为DEBFCA,符合后序遍历的要求。