0.允许为空
1.每个节点最大度为2
2.左右子树有序
3.每颗子树亦为二叉树
0.第i层有$2^{i-1}$个节点
1.高度为h的二叉树最多$2^{h}-1$个节点
2.
总节点数$N=(n_0+n_1+n_2)$
总边数$T=n_1+2n_2$
关系:$N=T+1$
推论:$N_0-N_2=1$
3.
n个节点的二叉树最低高度为$int(log_2n+1)$,此时为完全二叉树
4.
n个节点从1到n编号:
(1)i>1:则结点i的双亲编号为⌊i/2⌋;否则无双亲。
(2)2i<=n:则结点i的左孩子的编号为2i;否则无左孩子。
(3)2i+1<=n,则结点i的右孩子的编号为2i+1;否则无右孩子。
5.
n个节点构成的二叉树数目为卡塔兰数
真二叉树 | 度或为0,或为2 |
满二叉树 | 节点数为$2^(i - 1)$ |
完全二叉树 | 叶子节点只会出现最后2 层,最后1 层的叶子节点都靠左 对齐 |
顺序存储结构:堆(优先队列)
链式存储结构:二叉链表,三叉链表,线索二叉树
/// <summary> /// 节点类 /// </summary> /// <typeparam name="T">数据类型</typeparam> class Node<T> { /// <summary> /// 左子树 /// </summary> public Node<T> Left { get; set; } /// <summary> /// 右子树 /// </summary> public Node<T> Right { get; set; } /// <summary> /// 数据域 /// </summary> public T Data { get; set; } /// <summary> /// 构造器 /// </summary> /// <param name="data">数据域</param> /// <param name="left">左子树</param> /// <param name="right">右子树</param> public Node(T data,Node<T> left=null,Node<T> right=null) { this.Data = data; this.Left = left; this.Right = right; } }
/// <summary> /// 从数组创建完全二叉树 /// </summary> /// <param name="data">数组</param> /// <param name="index">索引</param> /// <returns>返回根节点</returns> private Node<T> Init1(T[]data,int index=0) { Node<T> root=null; if(index<data.Length) { root = new Node<T>(data[index]); root.Left=Init1(data, index * 2+1); root.Right = Init1(data, index * 2 + 2); } return root; }
关于start1,end1,start2,end2表达式可以看图
简单说就是中序序列以跟为界,分为两段,一段为左子树,一段为右子树。我们可以求出左子树在中序中的长度,从而将前序序列也分为两段。
这样一来,左右子树的开始结束索引都有了
/// <summary> /// 从先序和中序序列构建二叉树 /// </summary> /// <param name="previous">先序</param> /// <param name="start1">先序开始</param> /// <param name="end1">先序结束</param> /// <param name="center">中序</param> /// <param name="start2">中序开始</param> /// <param name="end2">中序结束</param> /// <returns>返回根</returns> private Node<T> Init2(T[]previous,int start1,int end1, T[] center, int start2,int end2) { //保证数据安全,递归结束点 if (start1 > end1 || start2 > end2) return null; //建立节点 Node<T> root = new Node<T>(previous[start1]); //在中序序列里找到根 int i; for (i = start2; i < end2; i++) if (center[i].Equals(previous[start1])) break; root.Left = Init2(previous, start1 + 1, i - start2 + start1, center, start2, i - 1); root.Right = Init2(previous, i - start2 + start1 + 1, end1, center, i + 1, end2); return root; }
同上
/// <summary> /// 从后序和中序序列构建二叉树 /// </summary> /// <param name="last">后序</param> /// <param name="start1">后序开始</param> /// <param name="end1">后序结束</param> /// <param name="center">中序</param> /// <param name="start2">中序开始</param> /// <param name="end2">中序结束</param> /// <returns>返回根</returns> private Node<T> Init3(T[]last,int start1,int end1,T[]center,int start2,int end2) { //递归结束点 if (start1 > end1 || start2 > end2) return null; //先找到根节点在中序序列中的索引 int i; for (i = start2; i < end2; i++) { if (center[i].Equals(last[end1])) break; } //创建根节点 Node<T> root = new Node<T>(last[end1]); root.Left=Init3(last, start1, start1 + i - 1 - start2, center, start2, i - 1); root.Right=Init3(last, start1 + i - start2 ,end1-1, center, i + 1, end2); return root; }
二叉树的深度优先遍历主要有前序,中序,后续三种
为了方便后续利用,此处传入函数指针来实现对节点的访问
/// <summary> /// 先序遍历 /// </summary> /// <param name="p">节点</param> /// <param name="f">访问函数</param> public void DLR(Node<T> p,Action<Node<T>> f) { if(p!=null) { f(p.Data); DLR(p.Left,f); DLR(p.Right,f); } }
/// <summary> /// 中序遍历 /// </summary> /// <param name="p">节点</param> /// <param name="f">访问函数</param> public void LDR(Node<T> p, Action<Node<T>> f) { if (p != null) { LDR(p.Left, f); f(p.Data); LDR(p.Right, f); } }
/// <summary> /// 后序遍历 /// </summary> /// <param name="p">节点</param> /// <param name="f">访问函数</param> public void LRD(Node<T> p, Action<Node<T>> f) { if (p != null) { LRD(p.Left, f); LRD(p.Right, f); f(p.Data); } }
我打算把层序遍历当做一个模板来使用,它可以实现很多赛艇的事情
这里的for循环为了分开每一层,这样可以分层打印,求各层宽度等等
/// <summary> /// 层序遍历 /// </summary> /// <param name="p">节点</param> /// <param name="f">访问函数</param> /// <returns>返回层数</returns> public int Level(Node<T> p,Action<Node<T>> f) { //一个队列 IQueue<Node<T>> q = new LinkQueue<Node<T>>(); //进队一个元素 q.Enter(p); //记录层数 var cnt = 0; //队列不空 while(!q.IsEmpty()) { //获取本层的宽度 int size = q.Count; //层数增加 cnt++; //注意:这个循环是为了区分开每一层 for (int i = 0; i < size; i++) { //出队并访问 var t = q.Out(); f(t); if (t.Left != null) q.Enter(t.Left); if(t.Right!=null) q.Enter(t.Right); } } return cnt; }
class Test { public static void Main(string[] args) { var arr = new int[] { 1, 2, 3, 4, 5, 6 }; var num1 = "abdec".ToCharArray(); var num2 = "dbeac".ToCharArray(); var num3 = "debca".ToCharArray(); var t = new LinkBTree<char>(num3,num2,false); t.LRD(t.Root,t=>WriteLine(t.Data)); } }