目录

  • 1、 树概念及结构
    • 1.1、树的概念
    • 1.2、树的定义
    • 1.3、树的一些基本术语
    • 1.4、树的表示
    • 1.4.1、儿子兄弟表示法
    • 1.4.2、双亲表示法
    • 1.4.3、孩子表示法
  • 2、二叉树及存储结构
      • 2.1、二叉树的定义
      • 2.2、特殊的二叉树
        • 2.2.1、斜二叉树
        • 2.2.2、完美二叉树(满二叉树)
        • 2.2.3、完全二叉树
      • 2.3、二叉树的性质
      • 2.4、顺序结构(缺点造成空间浪费)
      • 2.5、链式结构实现(有代码)
        • 2.5.1、结构体定义
        • 2.5.2、创建二叉树
        • 2.5.3、销毁二叉树
        • 2.5.3、先序遍历二叉树
        • 2.5.3、中序遍历二叉树
        • 2.5.4、后序遍历二叉树
    • 2.6、层次遍历
      • 2.7、二叉树的深度
      • 2.8、叶子节点个数
      • 2.8、完整的代码
  • 3、二叉树搜索树
    • 3.1、二叉搜索树的概念
    • 3.2、二叉搜索树的原理
    • 3.3、二叉搜索树的性质
    • 3.4、二叉搜索树的操作
      • 3.4.1、二叉搜索树的数据结构
      • 3.4.2、二叉搜索树的初始化
      • 3.4.3、查找指定值并放回地址
      • 3.4.4、查找树的最小值
      • 3.4.5、查找树的最大值
      • 3.4.6、二叉搜索树的值插入
      • 3.4.7、二叉搜索树的删除
      • 3.4.7、打印二叉搜索树
      • 3.4.7、完整代码
  • 4、平衡二叉树(AVL树)
    • 4.1平衡二叉树的概念
      • 4.2、平衡二叉树的调整(注:方框内的数值为序号,并非数值)
        • 4.2.1、RR旋转
        • 4.2.2、LL旋转
        • 4.2.3、LR旋转
        • 4.2.4、RL旋转
    • 4.3、平衡二叉树的操作
      • 4.3.1、平衡二叉树的数据结构
      • 4.3.2、寻找平衡二叉树的值,返回地址
      • 4.3.3、得到平衡二叉树的高度
      • 4.3.4、平衡二叉树LL旋转
      • 4.3.5、平衡二叉树RR旋转
      • 4.3.6、平衡二叉树LR旋转
      • 4.3.7、平衡二叉树RL旋转
      • 4.3.8、平衡二叉树数据插入
      • 4.3.9、平衡二叉树数据删除
      • 4.3.10、平衡二叉树销毁
      • 4.3.11、平衡二叉树完整代码
  • 5、哈夫曼树(WPL)的实现
    • 5.1、哈夫曼树的定义
    • 5.2、哈夫曼树的术语
    • 5.2、哈夫曼树的特点
    • 5.3、哈夫曼树用于编码
    • 5.4、哈夫曼树创建
    • 5.5、哈夫曼树的编码

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);    //创建并初始化堆栈S1Stack 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;}elsereturn 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 50typedef  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;}elsereturn 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)//空树,深度为0return 0;//树的最大深度 = 左右子树中深度较大的值 + 1return 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;}elsereturn FindMin(BST->Left);}//树的最大深度
int GetHeight(AVLTree root)
{if (root == NULL)//空树,深度为0return 0;//树的最大深度 = 左右子树中深度较大的值 + 1return 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';elsecd[--start] = '1';c = j;j = HT[j].parent;}(*HC)[i] = (char *)malloc((n-start)*sizeof(char));strcpy((*HC)[i], &cd[start]);}free(cd);
}

数据结构-二叉树、搜索树、平衡二叉树详解及C语言实现相关推荐

  1. HBase数据结构与基本语法详解

    HBase数据结构与基本语法详解.背景: 阅读新闻 [日期:2019-01-06] 来源:Linux社区 作者:Linux [字体:大 中 小] HBase中的表一般有这样的特点: 1 大:一个表可以 ...

  2. 二叉树层次遍历算法 python_二叉树的遍历详解:前、中、后、层次遍历(Python实现)...

    二叉树的遍历详解:前.中.后.层次遍历(Python实现) 二叉树是一种常见的数据结构,而它的常见遍历方法有前序遍历.中序遍历.后续遍历.层次遍历--掌握这几种遍历方法是很有必要的. 假设我们二叉树节 ...

  3. 二叉树遍历算法详解(递归法+非递归法)

    二叉树遍历算法详解 在上一篇C语言实现二叉树中有提到对于二叉树的遍历,包括前序,中序和后续遍历,以及层次遍历 大家已经熟悉了二叉树的前中后序遍历过程,大部分都采用了递归的思想来实现 在leetcode ...

  4. JavaScript数据结构与算法——链表详解(下)

    在JavaScript数据结构与算法--链表详解(上)中,我们探讨了一下链表的定义.实现原理以及单链表的实现.接下来我们进一步了解一下链表的其他内容. 1.双向链表 双向链表实现原理图: 与单向链表不 ...

  5. JavaScript数据结构与算法——链表详解(上)

    注:与之前JavaScript数据结构与算法系列博客不同的是,从这篇开始,此系列博客采用es6语法编写,这样在学数据结构的同时还能对ECMAScript6有进一步的认识,如需先了解es6语法请浏览ht ...

  6. JavaScript数据结构与算法——队列详解(下)

    接下来会借助本人另一篇文章JavaScript数据结构与算法--队列详解(上)中实现的队列类及其方法实现一个应用. 配对问题 需求分析:在一个文件中保存着一份男女混合的数据,名称前以B开头表示男士,以 ...

  7. JavaScript数据结构与算法——列表详解(下),基于Nodejs实现一个列表应用

    1.上篇回顾: 上篇我们实现了一个列表类,并添加了一些属性,实现了比较多的方法,本文章将与大家一起使用列表实现一个图书借阅查询系统.需要使用JavaScript数据结构与算法--列表详解(上)中写好的 ...

  8. JavaScript数据结构与算法——列表详解(上)

    列表是一组有序的数据,每个数组中的数据项称为元素.数组相关知识不够了解的伙伴可以阅读本人上篇博客在JavaScript中,列表的元素可以是任意数据类型.列表中可以保存不定数量的元素,实际使用时元素的数 ...

  9. JavaScript数据结构与算法——数组详解(下)

    1.二维与多维数组 JavaScript只支持一维数组,但是通过在数组里保存数组元素的方式,可以轻松创建多维数组. 1.1 创建二维数组 二维数组类似一种由行和列构成的数组表格,在JavaScript ...

最新文章

  1. Get传递的最大长度
  2. php restful yii,yii2 restful 风格搭建(一)
  3. Dapr牵手.NET学习笔记:Actor小试
  4. 工业级以太网交换机-管理型
  5. Google Map API学习1
  6. Swift on Linux —— 从源码开始安装
  7. 单片机控制电机原理以及程序怎么写
  8. 提高网站性能之 —— 减少图片HTTP 请求的方案
  9. python计算器功能介绍_python计算器功能如何实现?这篇文章给你最实用的代码
  10. Wipe In and Wipe Out 抹进、抹出(动画显示、动画隐藏)(展开、折叠)(拉下、收起)
  11. marlin固件烧录教程_i3型3D打印机制作详解――Marlin固件介绍
  12. 微信小程序分页功能实现
  13. 河南大学计算机科学与技术排名,2021年河南省高校最新排名:信息工程大学进入榜单,河南大学第3...
  14. oracle12 expdb,12c导出导入用expdp
  15. html文字自动消失了,为什么从网页上复制的文字到word上一修改后面的字就自动消失了...
  16. 批量识别图片大致不相同图片_电脑图片太多,其中不少是重复的,有无什么软件可以识别相同图片!...
  17. 小程序毕设作品之微信积分商城小程序毕业设计成品(1)开发概要
  18. WinEdt中英文字体调节
  19. 通用量子计算实用化又进一步,俞大鹏团队实现量子纠错领域重大进展,首次超越盈亏平衡点|Nature...
  20. 怎么用Python批量将ppt转换为pdf

热门文章

  1. 为什么虚拟机上一运行就显示程序停止_五分钟学Java:如何学习Java面试必考的JVM虚拟机||CSDN博文精选...
  2. mysql first value_开窗函数 First_Value 和 Last_Value
  3. java考前复习之数组
  4. html表单的首要标记是form,关于html中表单form标记的介绍
  5. java限流器_Smaphor(信号量)实现限流器
  6. python numpy 写入、读取 .npz 压缩文件
  7. python取两个set的并集、交集、差集
  8. 排序算法(3)----归并排序
  9. java中factory_Java后台面试--Spring中FactoryBean与BeanFactory的使用及区别
  10. js截取字符串区分汉字字母代码