1、树
1、1树的概念
树是由一个或者一个以上的结点组成,存在一个特殊的节点,称为树根,每个结点由数据和指针构成。
当节点数量为0时,称为空树。
1、2树的专有名词介绍
结点的度(degree):每个结点所有字数的个数。
树的度:树的所有结点中最大的度数。
结点层数:规定在根结点在1层,其它任一结点的层数是其父结点的层数加1 。
叶节点:度为0的结点。
兄弟结点:具有同一个父结点的各个结点彼此是兄弟结点。
路径和路径长度:从结点n1到 到nk的路径为一个结点序列n1 , n2 ,… , nk , ni是ni+1的父结点。路径所包含边的个数为路径的长度。
结点的层次:根节点的层数为1,其他结点的层数为父结点层数+1.
树的深度:树中所有结点的最大层次是树的深度。
2、二叉树及其存储结构
2.1二叉树的定义
一个又穷的结点的集合,可以为空,若不为空,则它由根节点和成为其左子树和右子树的两个不相交的二叉树组成。
2.2特殊的二叉树
斜二叉树、完美二叉树、完全二叉树
2.3二叉树的性质
1.二叉树的第i层的最大节点数为2^(i-1).i>=1
2.深度为k的二叉树最多有2^k-1个结点
3.对于任何非空二叉树,n0=n2+1(n0为度为0的结点,n2为度为2的结点).
2.4二叉树的存储结构
1.顺序存储:按照从上到下、从左到右的顺序存储(完全二叉树)
2.链表存储
struct TreeNode
{
ElementType Data;
TreeNode* Lchild;
TreeNode* Rchild;
};
3、二叉树的遍历
3.1递归遍历
3.1.1先序遍历
根节点->左子树->右子树
void Traverse(TreeNode* T)
{
if (T)
{
dosomething();
Traverse(T->Lchild);
Traverse(T->Rchild);
}
}
3.1.2中序遍历
左子树->根节点->右子树
void Traverse(TreeNode* T)
{
if (T)
{
Traverse(T->Lchild);
dosomething();
Traverse(T->Rchild);
}
}
3.1.3后序遍历
左子树->右子树->根节点
void Traverse(TreeNode* T)
{
if (T)
{
Traverse(T->Lchild);
Traverse(T->Rchild);
dosomething();
}
}
3.2非递归遍历
3.2.1非递归先序遍历
//非递归实现先序遍历
void PreTraverse(TreeNode* T)
{
if (!T)
return;
Stack<TreeNode*> s;
while (!s.IsEmpty()||T)
{
while(T)
{
s.Push(T);
visit(T);
T = T->Lchild;
}
if(!s.IsEmpty())
{
T = s.GetTop();
s.Pop();
T = T->Rchild;
}
}
}
3.2.2非递归中序遍历
//非递归实现中序遍历
void MidTraverse(TreeNode* T)
{
if (!T)
return;
Stack<TreeNode*> s;
while (!s.IsEmpty()||T)
{
while (T)
{
s.Push(T);
T = T->Lchild;
}
if (!s.IsEmpty())
{
T = s.GetTop();
visit(T);
s.Pop();
T = T->Rchild;
}
}
}
3.2.3非递归后序遍历
//非递归实现后序遍历
//后序遍历的顺序为(左->右->根),根据(根->右->左)的顺序遍历,在将结果逆序输出即是后序遍历了
void LastTraverse(TreeNode* T)
{
if (!T)
return;
Stack<TreeNode*> s;
Stack<TreeNode*> v;
while (!s.IsEmpty()||T)
{
while (T)
{
s.Push(T);
v.Push(T);
T = T->Rchild;
}
if (!s.IsEmpty())
{
T = s.GetTop();
s.Pop();
T = T->Lchild;
}
}
while (!v.IsEmpty())
{
T = v.GetTop();
visit(T);
v.Pop();
}
}
3.2.4非递归层序遍历
//非递归实现层序遍历
void LevelTraverse(TreeNode* T)
{
if (!T)
return;
Queue<TreeNode*> q;
q.En_Queue(T);
while (!q.IsEmpty())
{
T = q.De_Queue();
visit(T);
if (T->Lchild)
q.En_Queue(T->Lchild);
if (T->Rchild)
q.En_Queue(T->Rchild);
}
}
4、二叉搜索树
4.1什么是二叉搜索树
二叉搜索树又称二叉排序树、二叉查找树
若二叉排序树非空,则满足以下条件
1.非空左子树的所有键值小于其根结点
2.非空右子树的所有键值大于其根结点
3.左右子树都是二叉搜索树
4.2二叉树的操作集
4.2.1创建树节点
//创建结点
TreeNode* CreatNode(ElementType X)
{
TreeNode* T = new TreeNode;
T->Data = X;
T->Lchild = NULL;
T->Rchild = NULL;
}
4.2.2插入
//插入元素X到二叉树,返回根节点,如果二叉树中已有改元素,则插入失败,返回空指针
TreeNode* Insert(TreeNode* T,ElementType X)
{
if (!T)
T = CreatNode(X);
else if (X < T->Data)
T->Lchild = Insert(T->Lchild, X);
else if (X > T->Data)
T->Rchild = Insert(T->Rchild, X);
else
T = NULL;
return T;
}
4.2.3删除
//删除元素X
TreeNode* Delete(TreeNode* T,ElementType X)
{
TreeNode* temp = NULL;
if (!T)
return NULL;
if (X < T->Data)
T->Lchild = Delete(T->Lchild, X);
else if(X>T->Data)
T->Rchild = Delete(T->Rchild, X);
else
{
if (T->Lchild && T->Rchild)
{
temp = FindMin(T->Rchild);
T->Data = temp->Data;
T->Rchild=Delete(T->Rchild, T->Data);
}
else
{
temp = T;
if (!T->Lchild)
T = T->Rchild;
else if (!T->Rchild)
T = T->Lchild;
delete temp;
}
}
return T;
}
4.2.4查找
4.2.4.1查找元素X
//查找元素X,不存在返回NULL
TreeNode* Find(TreeNode* T,ElementType X)
{
if (!T)
return NULL;
else if (X < T->Data)
return Find(T->Lchild, X);
else if (X > T->Data)
return Find(T->Rchild, X);
else
return T;
}
4.2.4.2查找最大的元素
//查找二叉树中最大的元素
TreeNode* FindMax(TreeNode* T)
{
if (!T)
return NULL;
if (T->Rchild)
return FindMax(T->Rchild);
else
return T;
}
4.2.4.3查找最小的元素
//查找二叉树中最小的元素
TreeNode* FindMin(TreeNode* T)
{
if (!T)
return NULL;
if (T->Lchild)
return FindMin(T->Lchild);
else
return T;
}
5、二叉平衡树(AVL树)
5.1什么是二叉平衡树
1.平衡因子(BF)=hL-hR
2.平衡二叉树的任意节点的平衡因子的绝对值不超过1,即 | BF |<=1
5.2平衡二叉树的调整
5.2.1插入
TreeNode_AVL* Insert_AVL(TreeNode_AVL* T,ElementType X)
{
if (!T)
{
T = CreatNode(X);
}
else if (X < T->Data)
{
T->Lchild = Insert_AVL(T->Lchild, X);
if (Geth(T->Lchild) - Geth(T->Rchild) == 2)
if (T->Lchild->Data > X)
T = LL(T);
else
T = LR(T);
}
else if (X > T->Data)
{
T->Rchild = Insert_AVL(T->Rchild, X);
if (Geth(T->Lchild) - Geth(T->Rchild) == -2)
if (T->Rchild->Data > X)
T = RL(T);
else
T = RR(T);
}
T->height = Max(Geth(T->Lchild),Geth(T->Rchild)) + 1;
return T;
}
5.2.2 RR旋转
TreeNode_AVL* RR(TreeNode_AVL* A)
{
TreeNode_AVL* B = A->Rchild;
A->Rchild = B->Lchild;
B->Lchild = A;
A->height = Max(Geth(A->Lchild), Geth(A->Rchild)) + 1;
B->height = Max(Geth(B->Lchild), Geth(B->Rchild)) + 1;
return B;
}
5.2.3 LL旋转
TreeNode_AVL* LL(TreeNode_AVL* A)
{
TreeNode_AVL* B = A->Lchild;
A->Lchild = B->Rchild;
B->Rchild = A;
A->height = Max(Geth(A->Lchild), Geth(A->Rchild)) + 1;
B->height = Max(Geth(B->Lchild), Geth(B->Rchild)) + 1;
return B;
}
5.2.4 RL旋转
TreeNode_AVL* RL(TreeNode_AVL* A)
{
TreeNode_AVL* B = A->Rchild;
TreeNode_AVL* C = A->Rchild->Lchild;
A->Rchild = C->Lchild;
B->Lchild = C->Rchild;
C->Rchild = B;
C->Lchild = A;
A->height = Max(Geth(A->Lchild), Geth(A->Rchild)) + 1;
B->height = Max(Geth(B->Lchild), Geth(B->Rchild)) + 1;
C->height = Max(Geth(C->Lchild), Geth(C->Rchild)) + 1;
return C;
}
5.2.5 LR旋转
TreeNode_AVL* LR(TreeNode_AVL* A)
{
TreeNode_AVL* B = A->Lchild;
TreeNode_AVL* C = A->Lchild->Rchild;
A->Lchild = C->Rchild;
B->Rchild = C->Lchild;
C->Lchild = B;
C->Rchild = A;
A->height = Max(Geth(A->Lchild), Geth(A->Rchild)) + 1;
B->height = Max(Geth(B->Lchild), Geth(B->Rchild)) + 1;
C->height = Max(Geth(C->Lchild), Geth(C->Rchild)) + 1;
return C;
}
6、堆
6.1什么是堆
优先队列(Priority Queue):特殊的“ 队列”,取出元素的顺序是依照元素的优先权(关键字)。
大顶堆(最大堆):堆顶元素是最大值;
小顶堆(最小堆):堆顶元素是最小值;
从根节点到任意节点的路径上的值是有序的
6.2堆的创建
1.采用数组的方式
1)普通数组
插入:元素插入到数组尾部 ~O(1)
删除:查找最大(小)的元素—>删除–>后面的元素向前移动 ~O(n)+O(n)
2)有序数组
插入:找位置–>移动元素–>插入 ~ O(n)或O(log2n)+O((n)+O(1)
删除:删除最后一个 ~O(1)
2.采用链表的方式
1)普通链表
插入:插入链表头 ~O(1)
删除:查找最大(小)值–>删除 ~O(n)+O(1)
2)有序链表
插入:查找位置值–>插入节点 ~O(n)+O(1)
删除:删除链表头 ~O(1)
6.3二叉树堆
1)用完全二叉树的方式存储到数组中
2)特征:
满足以下条件的才能称为堆
~任意节点到根节点都满足有序性,即该序列递增或递减
~二叉树是完全二叉树
~任意节点大于其左右子树的所有结点(若其不为空)
6.4堆的操作集(二叉树)
6.4.1最大堆创建
typedef struct Heap Heap;
struct Heap
{
Elementtype* Element;
int size;
int capacity;
};
Heap* CreatHeap()
{
Heap* heap = new Heap;
heap->capacity = CAPACITY;
heap->size = 0;
heap->Element = new Elementtype[heap->capacity];
heap->Element[0] = MAXDATA;
return heap;
}
6.4.2最大堆插入
bool IsFull(Heap* heap)
{
if (heap->size < (heap->capacity) - 1)
return false;
else
return true;
}
void InsertHeap(Elementtype x,Heap* heap)
{
if (IsFull(heap))
return;
int i = ++heap->size;
for (; heap->Element[i / 2] < x; i /= 2)
heap->Element[i] = heap->Element[i / 2];
heap->Element[i] = x;
}
6.4.3最大堆删除
bool IsEmpty(Heap* heap)
{
if (heap->size <= 0)
return true;
else
return true;
}
Elementtype Delete(Heap* heap)
{
if (IsEmpty(heap))
throw "最大堆空,不能删除";
Elementtype x = heap->Element[1], temp = heap->Element[heap->size--];
int parent, child;
for (parent=1;parent*2<=heap->size; parent = child)
{
child = parent * 2;
if ((parent * 2) != heap->size && heap->Element[child] < heap->Element[child + 1])
child++;
if (temp > heap->Element[child])
break;
else
heap->Element[parent] = heap->Element[child];
}
heap->Element[parent] = temp;
return x;
}
6.4.4已有元素下生成最大堆的方法
-
将元素一个一个插入最大堆 ~O(N log N)
-
先将元素放入完全二叉树(数组)中,在调整使其成为最大堆 ~O(N)
思路:从序号最小的根节点开始,使其成为一个最大堆,再调整前一个,使其成为最大堆,一直调整到根节点,这时整棵树成为最小堆。
7、哈夫曼树(最优二叉树)与哈夫曼编码
7.1什么是哈夫曼树
- 带权路径长度(WPL):设二叉树有n个叶子结点。每个叶子节点带有权值wk,从根节点到叶子结点的长度为lk,则每个叶子节点的带权路径长度之和就是:WPL= ∑ i = 1 n \sum_{i=1}^n ∑i=1nwk * lk
- 哈夫曼树(最优二叉树):WPL最小的树
7.2哈夫曼树的构造
思路:所有结点建立一个集合,{ 从集合中取两个权重最小的根节点node1、node2,创建一个新节点node3,权重等于node1、node2权重之和,将node1和node2连接到node3上,将node3放入节点集合结点,} 重复花括号中的过程直到节点集合中只有一个根节点,这个节点就是哈夫曼树的根节点
HuffmanTree* CreatHuffmanTree();
typedef struct TreeNode* HuffmanTree;
struct TreeNode {
int Weight;
HuffmanTree Left, Right;
}
HuffmanTree Huffman(MinHeap H)
{ /* 假设H->Size 个权值已经存在H->Elements[]->Weight 里 */
int i; HuffmanTree T;
BuildMinHeap(H); /* 将H->Elements[] 按权值调整为最小堆*/
for (i = 1; i < H->Size; i++) { /* 做H->Size-1 次合并*/
T = malloc(sizeof(struct TreeNode)); /* 建立新结点*/
T->Left = DeleteMin(H);
/* 从最小堆中删除一个结点 , 作为新T 的左子结点*/
T->Right = DeleteMin(H);
/* 从最小堆中删除一个结点 , 作为新T 的右子结点*/
T->Weight = T->Left->Weight + T->Right->Weight;
/* 计算新权值*/
Insert(H, T); /* 将新T 插入最小堆*/
}
T = DeleteMin(H);
return T;
}
7.3哈夫曼编码
- 创建结点,结点权重就是该符号的出现频率,节点包含该字符,非叶子节点字符值为空
- 建立哈夫曼树
- 设左子树为0,右子树为1,从根节点到叶子结点的路径就是该叶子结点字符的编码(字符只会出现在叶子结点)
7.4前缀码
- 前缀码prefix code:任何字符的编码都不是另一字符编码的前缀(可以无二义地解码)
- 哈夫曼编码就是一种前缀码
8、集合及运算
8.1集合的表示(数组)
思路:创建一个一维结构体数组,结构体中包含Data(数据)和Parent(集合第一个元素的数组下标),若其自身就是第一个元素,则为-1;
8.2集合运算
8.2.1查找元素所在的集合
#define MaxSize 100 //数组的容量
#define ElementType int //数据类型
typedef struct Node NOde;
struct Node
{
ElementType Data;
int Parent;
}
int Find( Node S[ ], ElementType X )
{
int i;
for ( i=0; i < MaxSize && S[i].Data != X; i++) ; //找元素
if( i >= MaxSize )
return -1;
for( ; S[i].Parent >= 0; i = S[i].Parent ) ; //找爹
return i;
}
8.2.2集合的并运算
void Union( Node S[ ], ElementType X1, ElementType X2 )
{
int Root1, Root2;
Root1 = Find(S, X1);
Root2 = Find(S, X2);
if ( Root1 != Root2 )S[Root2].Parent = Root1;
}
9、红黑树
建议去看看这个大神写的:https://www.zhihu.com/question/312327402/answer/1560653215
ps:写的非常详细,建议耐心看完,你会有不一样的理解,深入浅