C#数据结构(四)----树和二叉树
1)C#数据结构(一)----线性表
2)C#数据结构(二)----栈和队列
3)C#数据结构(三)----串和数组
前面的几篇折腾完了线性结构,这篇开始学习非线性结构中“树”。下面是树的一些特点:
一、树的概念
1)树形结构是一对多的非线性结构。
2)树形结构有树和二叉树两种,树的操作实现比较复杂,但树可以转换为二叉树进行处理。
3)树的定义:树(Tree)是 n(n≥0)个相同类型的数据元素的有限集合。
4)树中的数据元素叫结点(Node)。
5)n=0 的树称为空树(Empty Tree);
6)对于 n>0 的任意非空树 T 有:
(1)有且仅有一个特殊的结点称为树的根(Root)结点,根没有前驱结点;
(2)若n>1,则除根结点外,其余结点被分成了m(m>0)个互不相交的集合
T1,T2,。。。,Tm,其中每一个集合Ti(1≤i≤m)本身又是一棵树。树T1,T2,。。。,Tm称为这棵树的子树(Subtree)。
7)树的定义是递归的,用树来定义树。因此,树(以及二叉树)的许多算法都使用了递归。
二、树的相关术语
1)、结点(Node):树中的数据元素,由数据项和元素之间的关系组成。 在图中,共有 10 个结点。
2)、结点的度(Degree of Node):结点所拥有的子树的个数,在图中,结点 A的度为3。
3)、树的度(Degree of Tree):树中各结点度的最大值。在图中,树的度为3。
4)、叶子结点(Leaf Node):度为 0 的结点,也叫终端结点。在图中,结点 E)、F)、G)、H)、I)、J 都是叶子结点。
5)、分支结点(Branch Node):度不为 0 的结点,也叫非终端结点或内部结点。在图中,结点 A)、B)、C)、D 是分支结点。
6)、孩子(Child):结点子树的根。在图中,结点 B)、C)、D 是结点 A 的孩子。
7)、双亲(Parent):结点的上层结点叫该结点的双亲。在图中,结点 B)、C)、D的双亲是结点A。
8)、祖先(Ancestor):从根到该结点所经分支上的所有结点。在图中,结点 E的祖先是 A和 B。
9)、子孙(Descendant):以某结点为根的子树中的任一结点。在图中,除A之外的所有结点都是A的子孙。
10)、兄弟(Brother):同一双亲的孩子。在图中,结点 B)、C)、D互为兄弟。
11)、结点的层次(Level of Node):从根结点到树中某结点所经路径上的分支数称为该结点的层次。根结点的层次规定为1,其余结点的层次等于其双亲结点的层次加 1。
12)、堂兄弟(Sibling):同一层的双亲不同的结点。在图中,G 和 H 互为堂兄弟。
13)、树的深度(Depth of Tree):树中结点的最大层次数。在图中,树的深度为 3。
14)、无序树(Unordered Tree):树中任意一个结点的各孩子结点之间的次序构成无关紧要的树。通常树指无序树。
15)、有序树(Ordered Tree):树中任意一个结点的各孩子结点有严格排列次序的树。二叉树是有序树,因为二叉树中每个孩子结点都确切定义为是该结点的左孩子结点还是右孩子结点。
16)、森林(Forest):m(m≥0)棵树的集合。自然界中的树和森林的概念差别很大,但在数据结构中树和森林的概念差别很小。从定义可知,一棵树有根结点和m 个子树构成,若把树的根结点删除,则树变成了包含 m 棵树的森林。当然,根据定义,一棵树也可以称为森林。
三、树的基本操作
1)、Root():求树的根结点,如果树非空,返回根结点,否则返回空;
2)、Parent(t):求结点 t的双亲结点。如果 t 的双亲结点存在,返回双亲结点,否则返回空;
3)、Child(t,i):求结点 t 的第 i 个子结点。如果存在,返回第 i 个子结点,否则返回空;
4)、RightSibling(t):求结点 t 第一个右边兄弟结点。如果存在,返回第一个右边兄弟结点,否则返回空;
5)、Insert(s,t,i):把树 s插入到树中作为结点 t 的第 i 棵子树。成功返回 true,否则返回 false;
6)、Delete(t,i):删除结点t 的第i棵子树。成功返回第i 棵子树的根结点,否则返回空;
7)、Traverse(TraverseType):按某种方式遍历树;
8)、Clear():清空树;
9)、IsEmpty():判断树是否为空树。如果是空树,返回 true,否则返回false;
10)、GetDepth():求树的深度。如果树不为空,返回树的层次,否则返回0。
四、二叉树
重点介绍二叉树,下面的程序也都是关于二叉树的。
1) 二叉树(Binary Tree)是n(n≥0)个相同类型的结点的有限集合。
2)n=0 的二叉树称为空二叉树(Empty Binary Tree);
3) 对于 n>0 的任意非空二叉树有:
(1)有且仅有一个特殊的结点称为二叉树的根(Root)结点,根没有前驱结点;
(2)若n>1,则除根结点外,其余结点被分成了 2 个互不相交的集合TL,
TR,而TL、TR本身又是一棵二叉树,分别称为这棵二叉树的左子树(Left Subtree)和右子树(Right Subtree)。
4)5种二叉树
空二叉树、只有根结点的二叉树、右子树为空的二叉树、左子树为空的二叉树和左、右子树非空的二叉树
五、二叉树的二叉链表存储结构
二叉树的二叉链表存储结构是指二叉树的结点有三个域:一个数据域和两个引用域,数据域存储数据,两个引用域分别存放其左、右孩子结点的地址。
5.1二叉链表节点:
//二叉数的二叉链表的结点类
public class TNode<T>
{
private T data;
private TNode<T> lChild;
private TNode<T> rChild;
public TNode(T val, TNode<T> l, TNode<T> r)
{
this.data = val;
this.lChild = l;
this.rChild = r;
}
public TNode(TNode<T> l, TNode<T> r)
{
this.data=default(T);
this.lChild = l;
this.rChild = r;
}
public TNode(T val)
{
this.data = val;
this.lChild = null;
this.rChild = null;
}
//数据
public T Data
{
get
{
return this.data;
}
set
{
this.data = value;
}
}
//左孩子
public TNode<T> LChild
{
get
{
return this.lChild;
}
set
{
this.lChild = value;
}
}
//右孩子
public TNode<T> RChild
{
get
{
return this.rChild;
}
set
{
this.rChild = value;
}
}
}
5.2二叉链表类:
//不带头结点的二叉树的二叉链表的类 BiTree<T>
public class BiTree<T>
{
private TNode<T> head;//头
public TNode<T> Head
{
get
{
return this.head;
}
set
{
this.head = value;
}
}
public BiTree()
{
this.head = null;
}
public BiTree(T val)
{
this.head = new TNode<T>(val);
}
public BiTree(T val, TNode<T> l, TNode<T> r)
{
this.head = new TNode<T>(val, l, r);
}
//判空
public bool IsEmpty()
{
if (this.head == null)
return true;
else
return false;
}
//根节点
public TNode<T> Root()
{
return this.head;
}
//左节点
public TNode<T> GetLChild(TNode<T> p)
{
return p.LChild;
}
//右节点
public TNode<T> GetRChild(TNode<T> p)
{
return p.RChild;
}
//将结点p的左子树插入值为val的新结点,
//原来的左子树成为新结点的左子树
public void InsertL(T val,TNode<T> p)
{
TNode<T> t = new TNode<T>(val);
t.LChild = p.LChild;
p.LChild = t;
}
//将结点p的右子树插入值为val的新结点,
//原来的右子树成为新结点的右子树
public void InsertR(T val, TNode<T> p)
{
TNode<T> t = new TNode<T>(val);
t.RChild = p.RChild;
p.RChild = t;
}
//若p非空,删除p的左子树
public TNode<T> DeleteL(TNode<T> p)
{
if ((p == null) || (p.LChild == null))
return null;
TNode<T> t = p.LChild;
p.LChild = null;
return t;
}
//若p非空,删除p的右子树
public TNode<T> DeleteR(TNode<T> p)
{
if ((p == null) || (p.RChild == null))
return null;
TNode<T> t = p.RChild;
p.RChild = null;
return t;
}
public bool IsLeaf(TNode<T> p)
{
if (p != null && p.LChild == null && p.RChild == null)
return true;
else
return false;
}
}
5.3二叉树的遍历
1)一棵二叉树由根结点、左子树和右子树三部分组成,
2) D、L、R 分别代表遍历根结点、遍历左子树、遍历右子树,则二叉树的
3) 遍历方式有6 种:DLR、DRL、LDR、LRD、RDL、RLD。
先左或先右算法基本一样,所以就剩下三种DLR(先序)、LDR(中序)、LRD(后序)
public class SortBiTree<T>
{
//DLR:根--左---右
public void PreOrder(TNode<T> root)
{
//跟节点为空
if (root == null)
return;
//处理跟节点
Console.WriteLine(root.Data);
//先序遍历左子树
PreOrder(root.LChild);
//先序遍历右子树
PreOrder(root.RChild);
}
//LDR:左--跟---右
public void InOrder(TNode<T> root)
{
//根结点为空
if (root == null)
return;
//中序遍历左子树
InOrder(root.LChild);
//处理根结点
Console.WriteLine(root.Data);
//中序遍历右子树
InOrder(root.RChild);
}
//LRD:左--右--跟
public void PostOrder(TNode<T> root)
{
//根结点为空
if (root == null)
return;
//后序遍历左子树
PostOrder(root.LChild);
//后序遍历右子树
PostOrder(root.RChild);
//处理根结点
Console.WriteLine(root.Data);
}
//层次遍历 先遇到的结点先访问
public void LevelOrder(TNode<T> root)
{
//根结点为空
if (root == null)
{
return;
}
//设置一个队列保存层序遍历的结点
SeqQueue<TNode<T>> sq = new SeqQueue<TNode<T>>(50);
//根结点入队
sq.In(root);
//队列非空,结点没有处理完
while (!sq.IsEmpty())
{
//结点出队
TNode<T> tmp = sq.Out();
//处理当前结点
Console.WriteLine(tmp.Data);
//将当前结点的左孩子结点入队
if (tmp.LChild != null)
{
sq.In(tmp.LChild);
}
if (tmp.RChild != null)
{
sq.In(tmp.RChild);
}
}
}
}
5.4 实例操作
public class OpBiTree
{
public void Run()
{
TNode<string> node = new TNode<string>("root");
TNode<string> node1 = new TNode<string>("1");
TNode<string> node2 = new TNode<string>("2");
TNode<string> node3 = new TNode<string>("3");
TNode<string> node4 = new TNode<string>("4");
TNode<string> node5 = new TNode<string>("5");
TNode<string> node6 = new TNode<string>("6");
node1.LChild = node3;
node1.RChild = node4;
node2.LChild = node5;
node2.RChild = node6;
node.LChild = node1;
node.RChild = node2;
BiTree<string> tree = new BiTree<string>();
tree.Head = node;
Console.WriteLine("--PreOrder--");
new SortBiTree<string>().PreOrder(node);
Console.WriteLine("--InOrder--");
new SortBiTree<string>().InOrder(node);
Console.WriteLine("--PostOrder--");
new SortBiTree<string>().PostOrder(node);
Console.WriteLine("--LevelOrder--");
new SortBiTree<string>().LevelOrder(node);
}
}
结果