1、 树概念及结构
1.1、树的概念
树是一种数据结构,它是由n(n≥1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
每个节点有零个或多个子节点;没有父节点的节点称为根节点;每一个非根节点有且只有一个父节点;除了根节点外,每个子节点可以分为多个不相交的子树。
1.2、树的定义
树(Tree) :n (n>=0) 个结点构成的有限集合。
当n=0时,称为空树;
对于任一棵非空树(n>0),它具备以下性质:
树中有一个称为“根(Root)”的特殊结点,用r表示;
其余结点可分为m(m>0)个互不相交的有限集T1, T2,…,Tm,其
中每个集合本身又是一棵树,称为原来树的“子树(SubTree)”
1.3、树的一些基本术语
1.结点的度(Degree) :结点的子树个数
2.树的度:树的所有结点中最大的度数
3.叶结点(Leaf) :度为0的结点
4.父结点(Parent) :有子树的结点是其子树的根结点的父结点
5.子结点(Child) :若A结点是B结点的父结点,则称B结点是A结点的子结点;子结点也称孩子结点。
6.兄弟结点(Sibling) :具有同一父结点的各结点彼此是兄弟结点。
7. 路径和路径长度:从结点n1到n的路径为个结点序列n1, N2… ,∩k, n,是n+1的父结点。路径所包含边的个数为路径的长度。
9.祖先结点(Ancestor):沿树根到某- -结点路径_上的所有结点都是这个结点的祖先结点。
10.子孙结点(Descendant):某一结 点的子树中的所有结点是这个结点的子孙。
11.结点的层次(Level) :规定根结点在1层,其它任一结点的层数是其父结点的层数加1。
12.树的深度(Depth) :树中所有结点中的最大层次是这棵树的深度。
1.4、树的表示
1.4.1、儿子兄弟表示法
树结构中,位于同一层的节点之间互为兄弟节点。
孩子兄弟表示法,采用的是链式存储结构,其存储树的实现思想是:从树的根节点开始,依次用链表存储各个节点的孩子节点和兄弟节点。即一个节点第一个指针指向第一个儿子,第二个指针指向另一个兄弟。
数据结构表示为:
节点的值;
指向孩子节点的指针;
指向兄弟节点的指针;
typedef struct CSNode{
ElemType data;
struct CSNode * firstchild,*nextsibling;
}
1.4.2、双亲表示法
双亲表示法采用顺序表存储普通树,其实现的核心思想是:顺序存储各个节点的同时,给各节点附加一个记录其父节点位置的变量。
1.4.3、孩子表示法
孩子表示法存储普通树采用的是 “顺序表+链表” 的组合结构,其存储过程是:从树的根节点开始,使用顺序表依次存储树中各个节点,需要注意的是,与双亲表示法不同,孩子表示法会给各个节点配备一个链表,用于存储各节点的孩子节点位于顺序表中的位置。
2、二叉树及存储结构
2.1、二叉树的定义
二叉树T:一个有穷的结点集合。
这个集合可以为空
若不为空,则它是由根结点和称为其左子树TL和右子树Tp的
两个不相交的二叉树组成。
二叉树具体五种基本形态,二叉树的子树有左右顺序之分。
2.2、特殊的二叉树
2.2.1、斜二叉树
2.2.2、完美二叉树(满二叉树)
2.2.3、完全二叉树
有n个结点的二叉树,对树中结点按从上至下、从左到右顺序进行编号,
编号为i (1≤i≤n)结点与满二叉树中编号为i结点在二叉树中位置相同
2.3、二叉树的性质
一个二叉树第i层的最大结点数为: 2-1,i≥1。
深度为k的二叉树有最大结点总数为:2 k_1, k≥1。
对任何非空二叉树T,若n。表示叶结点的个数、n,是度为2的非叶结点个数,那么两者满足关系n。=n, +1。
2.4、顺序结构(缺点造成空间浪费)
完全二叉树:按从_上至下、从左到右顺序存储n个结 点的完全二叉树的结点父子关系:
非根结点(序号i> 1)的父结点的序号是li/2];
结点(序号为i )的左孩子结点的序号是2i,(若2i<=n,否则没有左孩子) ;
结点(序号为i)的右孩子结点的序号是2i+1,(若2i+1<=n, 否则没有右孩子)
2.5、链式结构实现(有代码)
2.5.1、结构体定义
typedef int ElementType;
typedef struct TNode{ /* 树结点定义 */
ElementType Data; /* 结点数据 */
struct TNode *Left; /* 指向左子树 */
struct TNode *Right; /* 指向右子树 */
}BiTNode,*BinTree;
2.5.2、创建二叉树
BinTree CreateBinTree(BinTree *T) //BinTree是个指针类型,指向一个结构体 所以传入的参数是一个结构体指针的指针
{
int ch;
scanf("%d",&ch);
if (ch == -1) //当ch输入为-1时,代表指针域为NULL
{
*T = NULL; //将指针域设为空,这里*T为 TNode类型指针
return; //不返回任何值
}
else
{
*T = (BinTree)malloc(sizeof(BiTNode));
(*T)->Data = ch;
printf("请输入%d的左子节点:",ch);
CreateBinTree(&((*T)->Left)); //利用递归创建二叉树
printf("请输入%d的右子节点:",ch);
CreateBinTree((&(*T)->Right)); //(*T)->Right是个TNode类型指针 ,&为取地址符。&(*T)->Right)取指针的地址
}
return;
}
2.5.3、销毁二叉树
void DestroyBiTree(BinTree *T) //销毁一个二叉树
{
if(*T)
{
if((*T)->Left) // 有左孩子
DestroyBiTree(&(*T)->Left); //销毁左孩子子树
if((*T)->Right) // 有右孩子
DestroyBiTree(&(*T)->Right); // 销毁右孩子子树
free(*T); // 释放根结点
*T=NULL; // 空指针赋0
}
}
2.5.3、先序遍历二叉树
先序遍历:
遍历过程:
1.先访问根节点
2.先序遍历其左子树
3.先序遍历其右子树
先序遍历结果为:ABDFECGHI
先序递归实现:
void PreOrderTraversalBiTree(BinTree T) //先序遍历二叉树
{
if (T == NULL)
{
return;
}
else
{
printf("%d ",T->Data);
PreOrderTraversalBiTree(T->Left);
PreOrderTraversalBiTree(T->Right);
}
}
先序非递归实现:
遇到一个节点,就把它压栈,并访问这个节点,并去遍历它的左子树;
当左子树遍历结束后;
然后按其有指针再去前序遍历该节点的右子树。
int PreOrderTraver(BinTree T){ //先序遍历非递归法
BinTree temp = T;
Stack s = CreatStack(MAX_SIZE);
while(temp != NULL || s.top != 0 ){
while(temp != NULL)// 先遍历左子树;
{
Push(s,temp);
printf("%d ", temp -> Data);
temp = temp ->Left;
}
if (s.top != 0){
temp = Pop(s);
temp = temp ->Right;
}
}
return 0;
}
2.5.3、中序遍历二叉树
先序遍历:
遍历过程:
1.中序遍历其左子树
2.先访问根节点
3.中序遍历其右子树
中序遍历结果为:DBEFAGHCI
中序遍历递归实现:
void MiddleOrderTraversalBiTree(BinTree T) //中序遍历二叉树
{
if (T == NULL)
{
return;
}
else
{
MiddleOrderTraversalBiTree(T->Left);
printf("%d ",T->Data);
MiddleOrderTraversalBiTree(T->Right);
}
}
中序遍历非递归实现:
遇到一个节点,就把它压栈,并去遍历它的左子树;
当左子树遍历结束后,从栈顶弹出这个节点并访问它;
然后按其有指针再去前序遍历该节点的右子树。
int MiddleOrder(BinTree T){ //中序遍历非递归法
BinTree temp = T;
Stack s = CreatStack(MAX_SIZE);
while(temp != NULL || s.top != 0 ){
while(temp != NULL)// 先遍历左子树;
{
Push(s,temp);
temp = temp ->Left;
}
if (s.top != 0){
temp = Pop(s);
printf("%d ", temp -> Data);
temp = temp ->Right;
}
}
return 0;
}
2.5.4、后序遍历二叉树
后序遍历:
遍历过程:
1.后序遍历其左子树
2.后序遍历其右子树
3.再访问根节点
遍历结果:DEFBHGICA
后序遍历递归实现:
void PostOrderTraversaBiTree(BinTree T) //后续遍历二叉树
{
if (T == NULL)
{
return;
}
else
{
PostOrderTraversaBiTree(T->Left);
PostOrderTraversaBiTree(T->Right);
printf("%d ",T->Data);
}
}
后序遍历非递归代码
实现思路:先创建两个堆。
1.S1作为调整堆,开始先从根节点先右子树开始把节点压入栈,当最后一个节点的右子树为空时,将该节点出栈,并访问该节点的左子树;
2.当左子树为空时:继续出栈
3.当左子树不为空时:将该节点入栈
4.s2记录着入栈的顺序,将这里节点依次出栈即完成后序遍历。
s1:ACI入栈 I出C出 G入 H入 H出,G出,B入 F入 F出 E入 E出 B出 D入
s2 ACI入栈 G入栈 H入 B入 F入 E入 D入
在把s2出栈得到顺序为:DEFBHGICA
void PostOrderTraversal(BinTree BT)
{
BinTree T = BT;
Stack s1 = CreatStack(MAX_SIZE); //创建并初始化堆栈S1
Stack s2 = CreatStack(MAX_SIZE); //创建并初始化堆栈S2
while(T || !IsEmpty(S1))
{
while(T) //一直向右并将沿途节点访问(压入S2)后压入堆栈S1
{
Push(s2, T);
Push(s1, T);
T = T->Right;
}
if (!IsEmpty(S1))
{
T = Pop(s1) ; //节点弹出堆栈
T = T->Left; //转向左子树
}
}
while(!IsEmpty(s2)) //访问(打印)S2中元素
{
T = Pop(s2);
printf("%d\n", T->Data);
}
}
2.6、层次遍历
队列实现:遍历从根节点开始,首先根节点入队,然后开始执行循环:节点出队、访问该节点、其左右儿子入队。
层次遍历基本过程:先根节点入队,然后:
1.从队列中取出一个元素;
2.访问该节点指向的节点;
3.若该元素所指的左、右孩子节点非空,则将其左、右孩子的指针顺序入队。
void LevelorderTraversal ( BinTree BT )
{
Queue Q;
BinTree T;
if ( !BT ) return; /* 若是空树则直接返回 */
Q = CreatQueue(); /* 创建空队列Q */
AddQ( Q, BT );
while ( !IsEmpty(Q) ) {
T = DeleteQ( Q );
printf("%d ", T->Data); /* 访问取出队列的结点 */
if ( T->Left ) AddQ( Q, T->Left );
if ( T->Right ) AddQ( Q, T->Right );
}
}
2.7、二叉树的深度
int TreeDeep( BinTree T) //二叉树的深度
{
int deep = 0;
if (T != NULL)
{
int leftdeep = TreeDeep(T->Left);
int rightdeep = TreeDeep(T->Right);
deep = leftdeep >= rightdeep?leftdeep+1:rightdeep+1;
}
return deep;
}
2.8、叶子节点个数
int LeafCount(BinTree T) //叶子节点个数
{
static int count;
if (T != NULL)
{
if (T->Left == NULL && T->Right == NULL)
{
count++;
}
LeafCount(T->Left);
LeafCount(T->Right);
}
return count;
}
2.8、完整的代码
#include <stdio.h>
#include <stdlib.h>
typedef int ElementType;
typedef struct TNode{ /* 树结点定义 */
ElementType Data; /* 结点数据 */
struct TNode *Left; /* 指向左子树 */
struct TNode *Right; /* 指向右子树 */
}BiTNode,*BinTree;
//先序创建二叉树
BinTree CreateBinTree(BinTree *T) //BinTree是个指针类型,指向一个结构体 所以传入的参数是一个结构体指针的指针
{
int ch;
scanf("%d",&ch);
if (ch == -1) //当ch输入为-1时,代表指针域为NULL
{
*T = NULL; //将指针域设为空,这里*T为 TNode类型指针
return; //不返回任何值
}
else
{
*T = (BinTree)malloc(sizeof(BiTNode));
(*T)->Data = ch;
printf("请输入%d的左子节点:",ch);
CreateBinTree(&((*T)->Left)); //利用递归创建二叉树
printf("请输入%d的右子节点:",ch);
CreateBinTree((&(*T)->Right)); //(*T)->Right是个TNode类型指针 ,&为取地址符。&(*T)->Right)取指针的地址
}
return;
}
void DestroyBiTree(BinTree *T) //销毁一个二叉树
{
if(*T)
{
if((*T)->Left) /* 有左孩子 */
DestroyBiTree(&(*T)->Left); /* 销毁左孩子子树 */
if((*T)->Right) /* 有右孩子 */
DestroyBiTree(&(*T)->Right); /* 销毁右孩子子树 */
free(*T); /* 释放根结点 */
*T=NULL; /* 空指针赋0 */
}
}
void PreOrderTraversalBiTree(BinTree T) //先序遍历二叉树
{
if (T == NULL)
{
return;
}
else
{
printf("%d ",T->Data);
PreOrderTraversalBiTree(T->Left);
PreOrderTraversalBiTree(T->Right);
}
}
void MiddleOrderTraversalBiTree(BinTree T) //中序遍历二叉树
{
if (T == NULL)
{
return;
}
else
{
MiddleOrderTraversalBiTree(T->Left);
printf("%d ",T->Data);
MiddleOrderTraversalBiTree(T->Right);
}
}
void PostOrderTraversaBiTree(BinTree T) //后续遍历二叉树
{
if (T == NULL)
{
return;
}
else
{
PostOrderTraversaBiTree(T->Left);
PostOrderTraversaBiTree(T->Right);
printf("%d ",T->Data);
}
}
int TreeDeep( BinTree T) //二叉树的深度
{
int deep = 0;
if (T != NULL)
{
int leftdeep = TreeDeep(T->Left);
int rightdeep = TreeDeep(T->Right);
deep = leftdeep >= rightdeep?leftdeep+1:rightdeep+1;
}
return deep;
}
int LeafCount(BinTree T) //叶子节点个数
{
static int count;
if (T != NULL)
{
if (T->Left == NULL && T->Right == NULL)
{
count++;
}
LeafCount(T->Left);
LeafCount(T->Right);
}
return count;
}
//主函数
int main(int argc,const char *argv[])
{
BinTree T;
int depth,leafCount = 0;
printf("请输入第一个节点的值,-1表示没有叶节点:\n");
CreateBinTree(&T);
printf("先序遍历二叉树:");
PreOrderTraversalBiTree(T);
printf("\n");
printf("中序遍历二叉树:");
MiddleOrderTraversalBiTree(T);
printf("\n");
printf("后续遍历二叉树:");
PostOrderTraversaBiTree(T);
printf("\n");
depth = TreeDeep(T);
printf("树的深度为:%d\n",depth);
leafCount = LeafCount(T);
printf("叶子节点个数:%d\n",leafCount);
DestroyBiTree(&T); //销毁一个树
return 0;
}
3、二叉树搜索树
3.1、二叉搜索树的概念
二叉查找树(Binary Search Tree):(二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
3.2、二叉搜索树的原理
二叉搜索树(BST)又称二叉查找树或二叉排序树。一棵二叉搜索树是以二叉树来组织的,可以使用一个链表数据结构来表示,其中每一个结点就是一个对象。一般地,除了key和位置数据之外,每个结点还包含属性lchild、rchild和parent,分别指向结点的左孩子、右孩子和双亲(父结点)。如果某个孩子结点或父结点不存在,则相应属性的值为空(NIL)。根结点是树中唯一父指针为NULL的结点,而叶子结点的孩子结点指针也为NULL。
3.3、二叉搜索树的性质
1.若任意结点的左子树不空,则左子树上所有结点的值均不大于它的根结点的值。
2. 若任意结点的右子树不空,则右子树上所有结点的值均不小于它的根结点的值。
3.任意结点的左、右子树也分别为二叉搜索树
3.4、二叉搜索树的操作
3.4.1、二叉搜索树的数据结构
typedef int ElemenType;
typedef struct TreeNode {
struct TreeNode *Left; //指向左子树
struct TreeNode *Right; //指向右子树
ElemenType data;
int level;
}TreeNode, *BinTree;
3.4.2、二叉搜索树的初始化
int initTree(BinTree *T){ //初始化搜索二叉树
*T = NULL;
return 0;
}
3.4.3、查找指定值并放回地址
BinTree Find(ElemenType X,BinTree BST) //二叉树搜索指定的值
{
if(!BST)
{
return NULL; //查找失败
}
if(X > BST->data)
{
return Find(X,BST->Right);
}
else if(X < BST->data)
{
return Find(X,BST->Left);
}
else
{
return BST;
}
}
3.4.4、查找树的最小值
BinTree FindMin(BinTree BST) //查找最小值
{
if(!BST)
{
return NULL; //查找失败
}
else if(!BST->Left)
{
return BST;
}
else
return FindMin(BST->Left);
}
3.4.5、查找树的最大值
BinTree FindMax(BinTree BST) //查找最大值
{
if(BST)
{
while(BST->Right)
{
BST=BST->Right;
}
}
return BST;
}
3.4.6、二叉搜索树的值插入
void insertNode(BinTree *T,int value,int level){ //搜索二叉树的插入
level ++;
if (*T == NULL){
// 树为空时
*T = malloc(sizeof(TreeNode));
if (*T != NULL){
(*T)->data = value;
(*T)->Left = NULL;
(*T)->Right = NULL;
(*T)->level = level;
//printf("Insert value: %d success, lelevl:%d\n",value,level-1);
//return 0;
} else{
printf("error occured\n");
}
}
else{
// 树不为空
// 插入到左子树
if(value < (*T)->data){
insertNode(&(*T)->Left,value,level);
}
else{
// 插入右子树
if (value > (*T)->data){
insertNode(&(*T)->Right,value,level);
}
else {
printf("结点已存在 值域: %d; 层数:%d", (*T)->data,(*T)->level);
}
}
}
}
3.4.7、二叉搜索树的删除
BinTree Delete( BinTree BST, ElemenType X )
{
BinTree Tmp;
if( !BST )
printf("要删除的元素未找到");
else {
if( X < BST->data )
BST->Left = Delete( BST->Left, X ); /* 从左子树递归删除 */
else if( X > BST->data )
BST->Right = Delete( BST->Right, X ); /* 从右子树递归删除 */
else { /* BST就是要删除的结点 */
/* 如果被删除结点有左右两个子结点 */
if( BST->Left && BST->Right ) {
/* 从右子树中找最小的元素填充删除结点 */
Tmp = FindMin( BST->Right );
BST->data = Tmp->data;
/* 从右子树中删除最小元素 */
BST->Right = Delete( BST->Right, BST->data );
}
else { /* 被删除结点有一个或无子结点 */
Tmp = BST;
if( !BST->Left ) /* 只有右孩子或无子结点 */
BST = BST->Right;
else /* 只有左孩子 */
BST = BST->Left;
free( Tmp );
}
}
}
return BST;
}
3.4.7、打印二叉搜索树
int printTree(BinTree T){
while (T != NULL){
printTree(T->Left);
printf("%d ",T->data);
printTree(T->Right);
T = NULL;
}
return 0;
}
3.4.7、完整代码
#include "stdio.h"
#include "stdlib.h"
#define MAXSIZE 50
typedef int ElemenType;
typedef struct TreeNode {
struct TreeNode *Left; //指向左子树
struct TreeNode *Right; //指向右子树
ElemenType data;
int level;
}TreeNode, *BinTree;
int initTree(BinTree *T){ //初始化搜索二叉树
*T = NULL;
return 0;
}
BinTree Find(ElemenType X,BinTree BST) //二叉树搜索指定的值
{
if(!BST)
{
return NULL; //查找失败
}
if(X > BST->data)
{
return Find(X,BST->Right);
}
else if(X < BST->data)
{
return Find(X,BST->Left);
}
else
{
return BST;
}
}
BinTree FindMin(BinTree BST) //查找最小值
{
if(!BST)
{
return NULL; //查找失败
}
else if(!BST->Left)
{
return BST;
}
else
return FindMin(BST->Left);
}
BinTree FindMax(BinTree BST) //查找最大值
{
if(BST)
{
while(BST->Right)
{
BST=BST->Right;
}
}
return BST;
}
void insertNode(BinTree *T,int value,int level){ //搜索二叉树的插入
level ++;
if (*T == NULL){
// 树为空时
*T = malloc(sizeof(TreeNode));
if (*T != NULL){
(*T)->data = value;
(*T)->Left = NULL;
(*T)->Right = NULL;
(*T)->level = level;
//printf("Insert value: %d success, lelevl:%d\n",value,level-1);
//return 0;
} else{
printf("error occured\n");
}
}
else{
// 树不为空
// 插入到左子树
if(value < (*T)->data){
insertNode(&(*T)->Left,value,level);
}
else{
// 插入右子树
if (value > (*T)->data){
insertNode(&(*T)->Right,value,level);
}
else {
printf("结点已存在 值域: %d; 层数:%d", (*T)->data,(*T)->level);
}
}
}
}
BinTree Delete( BinTree BST, ElemenType X )
{
BinTree Tmp;
if( !BST )
printf("要删除的元素未找到");
else {
if( X < BST->data )
BST->Left = Delete( BST->Left, X ); /* 从左子树递归删除 */
else if( X > BST->data )
BST->Right = Delete( BST->Right, X ); /* 从右子树递归删除 */
else { /* BST就是要删除的结点 */
/* 如果被删除结点有左右两个子结点 */
if( BST->Left && BST->Right ) {
/* 从右子树中找最小的元素填充删除结点 */
Tmp = FindMin( BST->Right );
BST->data = Tmp->data;
/* 从右子树中删除最小元素 */
BST->Right = Delete( BST->Right, BST->data );
}
else { /* 被删除结点有一个或无子结点 */
Tmp = BST;
if( !BST->Left ) /* 只有右孩子或无子结点 */
BST = BST->Right;
else /* 只有左孩子 */
BST = BST->Left;
free( Tmp );
}
}
}
return BST;
}
int printTree(BinTree T){
while (T != NULL){
printTree(T->Left);
printf("%d ",T->data);
printTree(T->Right);
T = NULL;
}
return 0;
}
int main(){
BinTree T = NULL;
BinTree min,max,Finddata;
int temp = 0;
int i;
int arr[MAXSIZE] = {6,3,10,3,6,4,9,7,13,9,5};
int len = 9;
for ( i = 0; i < len; i++) {
insertNode(&T,arr[i],0); //插入
}
printf("搜索二叉树构建成功\n");
printf("请输入要插入的数字:\n");
scanf("%d",&temp);
insertNode(&T,temp,0); //插入数组
printf("\n中序遍历:\n");
printTree(T); //打印这个二叉树
printf("\n");
min=FindMin(T); //寻找最小值
max =FindMax(T); //寻找最大值
Finddata = Find(3,T); //查找3的地址
printf("最小值%d\n",min->data) ;
printf("最大值%d\n",max->data) ;
printf("找到的值的地址%d,找到的地址的值为%d\n",Finddata,Finddata->data) ;
Delete( T, 7 ); //删除7
printf("删除后的结果");
printTree(T); //打印这个二叉树
return 0;
}
4、平衡二叉树(AVL树)
4.1平衡二叉树的概念
平衡因子(Balance Factor,简称BT):BT(T) = Hl-Hr,其中Hl和Hr分别为T的左、右子树的高度。
平衡二叉树(Balance Binary Tree) (AVL树) 空树 或者任意节点左、右高度差的绝对值不超过1,即|BT(T)|<=1
给定接点水为n的AVL树的最大高度为O(log2N)。
4.2、平衡二叉树的调整(注:方框内的数值为序号,并非数值)
4.2.1、RR旋转
(注:方框内的数值为序号,并非数值)
不平衡的“发现者”是2,“麻烦节点” 3在发现者的右子树的右边,因而叫RR插入,需要RR旋转(右单旋)
4.2.2、LL旋转
(注:方框内的数值为序号,并非数值)
“发现者”是2,“麻烦节点”5在发现者的左子树的左边,因而叫LL插入,需要LL旋转(左单旋)
4.2.3、LR旋转
(注:方框内的数值为序号,并非数值)
“发现者”是1,“麻烦节点”为6,6在左子树的右边,因而叫LR插入,需要LR旋转。
4.2.4、RL旋转
(注:方框内的数值为序号,并非数值)
“发现者”是5,“麻烦制造节点是”9,9在有子树的左边,因而叫RL插入,需要RL旋转
4.3、平衡二叉树的操作
4.3.1、平衡二叉树的数据结构
typedef int ElemenType;
typedef struct TreeNode {
struct TreeNode *Left; //指向左子树
struct TreeNode *Right; //指向右子树
ElemenType data;
int Height;
}AVLNode,*AVLTree;
4.3.2、寻找平衡二叉树的值,返回地址
AVLTree Find(ElemenType X,AVLTree BST) //二叉树搜索指定的值
{//利用递归的思想
if(!BST)
{
return NULL; //查找失败
}
if(X > BST->data)
{
return Find(X,BST->Right);
}
else if(X < BST->data)
{
return Find(X,BST->Left);
}
else
{
return BST;
}
}
4.3.3、得到平衡二叉树的高度
int GetHeight(AVLTree root)
{
if (root == NULL)//空树,深度为0
return 0;
//树的最大深度 = 左右子树中深度较大的值 + 1
return Max(GetHeight(root->Left), GetHeight(root->Right)) + 1;
}
4.3.4、平衡二叉树LL旋转
AVLTree SingleLeftRotation ( AVLTree A ) //LL
{ /* 注意:A必须有一个左子结点B */
/* 将A与B做左单旋,更新A与B的高度,返回新的根结点B */
AVLTree B = A->Left;
A->Left = B->Right;
B->Right = A;
A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) + 1;
B->Height = Max( GetHeight(B->Left), A->Height ) + 1;
return B;
}
4.3.5、平衡二叉树RR旋转
AVLTree SingleRightRotation ( AVLTree A ) //RR
{
AVLTree B = A->Right;
A->Right =B->Left;
B->Left = A;
A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) + 1;
B->Height = Max( GetHeight(B->Left), A->Height ) + 1;
}
4.3.6、平衡二叉树LR旋转
AVLTree DoubleLeftRightRotation ( AVLTree A ) //LR
{ /* 注意:A必须有一个左子结点B,且B必须有一个右子结点C */
/* 将A、B与C做两次单旋,返回新的根结点C */
/* 将B与C做右单旋,C被返回 */
A->Left = SingleLeftRotation(A->Left);
/* 将A与C做左单旋,C被返回 */
return SingleLeftRotation(A);
}
4.3.7、平衡二叉树RL旋转
AVLTree DoubleRightLeftRotation ( AVLTree A ) //rl
{ /* 注意:A必须有一个左子结点B,且B必须有一个右子结点C */
/* 将A、B与C做两次单旋,返回新的根结点C */
/* 将B与C做右单旋,C被返回 */
A->Right = SingleRightRotation(A->Right);
/* 将A与C做左单旋,C被返回 */
return SingleRightRotation(A);
}
4.3.8、平衡二叉树数据插入
AVLTree Insert( AVLTree T, ElemenType X )
{ /* 将X插入AVL树T中,并且返回调整后的AVL树 */
if ( !T ) { /* 若插入空树,则新建包含一个结点的树 */
T = (AVLTree)malloc(sizeof(AVLNode));
T->data = X;
T->Height = 0;
T->Left = T->Right = NULL;
} /* if (插入空树) 结束 */
else if ( X < T->data ) {
/* 插入T的左子树 */
T->Left = Insert( T->Left, X);
/* 如果需要左旋 */
if ( GetHeight(T->Left)-GetHeight(T->Right) == 2 )
if ( X < T->Left->data )
T = SingleLeftRotation(T); /* 左单旋 */
else
T = DoubleLeftRightRotation(T); /* 左-右双旋 */
} /* else if (插入左子树) 结束 */
else if ( X > T->data ) {
/* 插入T的右子树 */
T->Right = Insert( T->Right, X );
/* 如果需要右旋 */
if ( GetHeight(T->Right)-GetHeight(T->Left) == -2 )
if ( X > T->Right->data )
T = SingleRightRotation(T); /* 右单旋 */
else
T = DoubleRightLeftRotation(T); /* 右-左双旋 */
} /* else if (插入右子树) 结束 */
/* else X == T->Data,无须插入 */
/* 别忘了更新树高 */
T->Height = Max( GetHeight(T->Left), GetHeight(T->Right) ) + 1;
return T;
}
4.3.9、平衡二叉树数据删除
AVLTree Delete( AVLTree BST, ElemenType X )
{
AVLTree Tmp;
if( !BST )
printf("要删除的元素未找到");
else {
if( X < BST->data )
BST->Left = Delete( BST->Left, X ); /* 从左子树递归删除 */
else if( X > BST->data )
BST->Right = Delete( BST->Right, X ); /* 从右子树递归删除 */
else { /* BST就是要删除的结点 */
/* 如果被删除结点有左右两个子结点 */
if( BST->Left && BST->Right ) {
/* 从右子树中找最小的元素填充删除结点 */
Tmp = FindMin( BST->Right );
BST->data = Tmp->data;
/* 从右子树中删除最小元素 */
BST->Right = Delete( BST->Right, BST->data );
}
else { /* 被删除结点有一个或无子结点 */
Tmp = BST;
if( !BST->Left ) /* 只有右孩子或无子结点 */
BST = BST->Right;
else /* 只有左孩子 */
BST = BST->Left;
free( Tmp );
}
}
}
return BST;
}
4.3.10、平衡二叉树销毁
void DestroyBiTree(AVLTree *T) //销毁一个二叉树
{
if(*T)
{
if((*T)->Left) /* 有左孩子 */
DestroyBiTree(&(*T)->Left); /* 销毁左孩子子树 */
if((*T)->Right) /* 有右孩子 */
DestroyBiTree(&(*T)->Right); /* 销毁右孩子子树 */
free(*T); /* 释放根结点 */
*T=NULL; /* 空指针赋0 */
}
4.3.11、平衡二叉树完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef int ElemenType;
typedef struct TreeNode {
struct TreeNode *Left; //指向左子树
struct TreeNode *Right; //指向右子树
ElemenType data;
int Height;
}AVLNode,*AVLTree;
AVLTree Find(ElemenType X,AVLTree BST) //二叉树搜索指定的值
{
if(!BST)
{
return NULL; //查找失败
}
if(X > BST->data)
{
return Find(X,BST->Right);
}
else if(X < BST->data)
{
return Find(X,BST->Left);
}
else
{
return BST;
}
}
int Max(int a, int b)
{
return a > b ? a : b;
}
AVLTree FindMin(AVLTree BST) //查找最小值
{
if(!BST)
{
return NULL; //查找失败
}
else if(!BST->Left)
{
return BST;
}
else
return FindMin(BST->Left);
}
//树的最大深度
int GetHeight(AVLTree root)
{
if (root == NULL)//空树,深度为0
return 0;
//树的最大深度 = 左右子树中深度较大的值 + 1
return Max(GetHeight(root->Left), GetHeight(root->Right)) + 1;
}
AVLTree SingleLeftRotation ( AVLTree A ) //LL
{ /* 注意:A必须有一个左子结点B */
/* 将A与B做左单旋,更新A与B的高度,返回新的根结点B */
AVLTree B = A->Left;
A->Left = B->Right;
B->Right = A;
A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) + 1;
B->Height = Max( GetHeight(B->Left), A->Height ) + 1;
return B;
}
AVLTree SingleRightRotation ( AVLTree A ) //RR
{
AVLTree B = A->Right;
A->Right =B->Left;
B->Left = A;
A->Height = Max( GetHeight(A->Left), GetHeight(A->Right) ) + 1;
B->Height = Max( GetHeight(B->Left), A->Height ) + 1;
}
AVLTree DoubleLeftRightRotation ( AVLTree A ) //LR
{ /* 注意:A必须有一个左子结点B,且B必须有一个右子结点C */
/* 将A、B与C做两次单旋,返回新的根结点C */
/* 将B与C做右单旋,C被返回 */
A->Left = SingleLeftRotation(A->Left);
/* 将A与C做左单旋,C被返回 */
return SingleLeftRotation(A);
}
AVLTree DoubleRightLeftRotation ( AVLTree A ) //rl
{ /* 注意:A必须有一个左子结点B,且B必须有一个右子结点C */
/* 将A、B与C做两次单旋,返回新的根结点C */
/* 将B与C做右单旋,C被返回 */
A->Right = SingleRightRotation(A->Right);
/* 将A与C做左单旋,C被返回 */
return SingleRightRotation(A);
}
AVLTree Insert( AVLTree T, ElemenType X )
{ /* 将X插入AVL树T中,并且返回调整后的AVL树 */
if ( !T ) { /* 若插入空树,则新建包含一个结点的树 */
T = (AVLTree)malloc(sizeof(AVLNode));
T->data = X;
T->Height = 0;
T->Left = T->Right = NULL;
} /* if (插入空树) 结束 */
else if ( X < T->data ) {
/* 插入T的左子树 */
T->Left = Insert( T->Left, X);
/* 如果需要左旋 */
if ( GetHeight(T->Left)-GetHeight(T->Right) == 2 )
if ( X < T->Left->data )
T = SingleLeftRotation(T); /* 左单旋 */
else
T = DoubleLeftRightRotation(T); /* 左-右双旋 */
} /* else if (插入左子树) 结束 */
else if ( X > T->data ) {
/* 插入T的右子树 */
T->Right = Insert( T->Right, X );
/* 如果需要右旋 */
if ( GetHeight(T->Right)-GetHeight(T->Left) == -2 )
if ( X > T->Right->data )
T = SingleRightRotation(T); /* 右单旋 */
else
T = DoubleRightLeftRotation(T); /* 右-左双旋 */
} /* else if (插入右子树) 结束 */
/* else X == T->Data,无须插入 */
/* 别忘了更新树高 */
T->Height = Max( GetHeight(T->Left), GetHeight(T->Right) ) + 1;
return T;
}
AVLTree Delete( AVLTree BST, ElemenType X )
{
AVLTree Tmp;
if( !BST )
printf("要删除的元素未找到");
else {
if( X < BST->data )
BST->Left = Delete( BST->Left, X ); /* 从左子树递归删除 */
else if( X > BST->data )
BST->Right = Delete( BST->Right, X ); /* 从右子树递归删除 */
else { /* BST就是要删除的结点 */
/* 如果被删除结点有左右两个子结点 */
if( BST->Left && BST->Right ) {
/* 从右子树中找最小的元素填充删除结点 */
Tmp = FindMin( BST->Right );
BST->data = Tmp->data;
/* 从右子树中删除最小元素 */
BST->Right = Delete( BST->Right, BST->data );
}
else { /* 被删除结点有一个或无子结点 */
Tmp = BST;
if( !BST->Left ) /* 只有右孩子或无子结点 */
BST = BST->Right;
else /* 只有左孩子 */
BST = BST->Left;
free( Tmp );
}
}
}
return BST;
}
void DestroyBiTree(AVLTree *T) //销毁一个二叉树
{
if(*T)
{
if((*T)->Left) /* 有左孩子 */
DestroyBiTree(&(*T)->Left); /* 销毁左孩子子树 */
if((*T)->Right) /* 有右孩子 */
DestroyBiTree(&(*T)->Right); /* 销毁右孩子子树 */
free(*T); /* 释放根结点 */
*T=NULL; /* 空指针赋0 */
}
}
int printTree(AVLTree T){
while (T != NULL){
printTree(T->Left);
printf("%d ",T->data);
printTree(T->Right);
T = NULL;
}
return 0;
}
int main(){
AVLTree T = NULL,Temp;
AVLTree Finddata;
int temp = 0;
int arr[] = {3,6,4,9,7,13,5,12,1};
int i;
int len = (int) sizeof(arr) / sizeof(*arr);
for ( i = 0; i < len; i++) {
T= Insert(T,arr[i]); //插入
}
printTree(T); //打印这个二叉树
printf("\n");
printf("该二叉树的高度为:");
printf("%d",GetHeight(T));
Temp =Find(6,T); //寻找值 返回地址
printf("寻找值的地址为%x,值为%d\n",Temp,Temp->data);
printf("\n中序遍历:\n");
Delete(T,5);
printf("删除的值为5\n");
printTree(T);
printf("\n");
DestroyBiTree(&T);
printf("销毁成功\n");
printTree(T);
return 0;
}
5、哈夫曼树(WPL)的实现
5.1、哈夫曼树的定义
给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
5.2、哈夫曼树的术语
1、路径和路径长度
在一棵树中,从一个结点往下可以达到的孩子或孙子结点之间的通路,称为路径。通路中分支的数目称为路径长度。若规定根结点的层数为1,则从根结点到第L层结点的路径长度为L-1。
2、结点的权及带权路径长度
若将树中结点赋给一个有着某种含义的数值,则这个数值称为该结点的权。结点的带权路径长度为:从根结点到该结点之间的路径长度与该结点的权的乘积。
3、树的带权路径长度
树的带权路径长度规定为所有叶子结点的带权路径长度之和,记为WPL。
5.2、哈夫曼树的特点
1.没有度为1的节点。
n个叶子节点的哈夫曼树共有2n-1个节点;
哈夫曼树的任意非叶节点的左右子树交换后仍然是哈夫曼树。
5.3、哈夫曼树用于编码
用二叉树进行编码:
1.左右分支:0、1;
2.字符只在叶子节点上
5.4、哈夫曼树创建
void CreateHuffmanTree(HuffmanTree *HT, ElemenType *w, ElemenType n)
{ int i,m;
if(n<=1) return;
m = 2*n-1;
*HT = (HuffmanTree) malloc((m+1) * sizeof(HTNode)); // 0号位置不用
HuffmanTree p = *HT;
for(i = 1; i <= n; i++)
{
(p+i)->weight = *(w+i-1);
(p+i)->parent = 0;
(p+i)->left = 0;
(p+i)->right = 0;
}
for( i = n+1; i <= m; i++)
{
(p+i)->weight = 0;
(p+i)->parent = 0;
(p+i)->left = 0;
(p+i)->right = 0;
}
//构建哈夫曼树
for( i = n+1; i <= m; i++)
{
int s1, s2;
Select(*HT, i-1, &s1, &s2);
(*HT)[s1].parent = (*HT)[s2].parent = i;
(*HT)[i].left = s1;
(*HT)[i].right = s2;
(*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;
}
}
5.5、哈夫曼树的编码
void HuffmanCoding(HuffmanTree HT, HuffmanCode *HC,ElemenType n){
int i,start,c,j;
*HC = (HuffmanCode) malloc((n+1) * sizeof(char *));
char *cd = (char *)malloc(n*sizeof(char)); //存放结点哈夫曼编码的字符串数组
cd[n-1] = '\0';//字符串结束符
for( i=1; i<=n; i++){
start = n-1;
c = i;
j = HT[i].parent;
while(j != 0){
if(HT[j].left == c)
cd[--start] = '0';
else
cd[--start] = '1';
c = j;
j = HT[j].parent;
}
(*HC)[i] = (char *)malloc((n-start)*sizeof(char));
strcpy((*HC)[i], &cd[start]);
}
free(cd);
}