树的应用

1.并查集

  并查集是一种将元素划分到不同集合的数据结构,采用树的双亲表示法来实现。

/**@数据结构:树->并查集 
 **@作者:9761滴  
 **@存储结构:双亲表示法 
 **/
 
//本文件中实现了并查集的
    /*
        1.初始化 
        2.查找 
        3.合并 
    */
     
//等操作
#include<cstdio>
#define Max_Size 100
int father[Max_Size]; 
//初始化
void init(){
    for(int i=0;i<Max_Size;i++){
        father[i]=i;
    }
} 
//查找元素x所在的集合 
int findParent(int x){
    int a=x;
    while(x!=father[x]){
        x=father[x];
    }
    //压缩路径
     while(a!=x){
         int z=a;
         a=father[a];
         father[z]=x;
     }
    return x;
} 
//合并两个集合
void unify(int a,int b){
    int fa=findParent(a);
    int fb=findParent(b);
    if(a!=b)
        father[fa]=fb;
} 

 

2.二叉排序树BST  ->  AVL树

  定义:首先它是一颗二叉树,其次每个结点的左子树上的结点要小于该结点的值,右子树上结点的值要大于该结点的值。

  性质:由于二叉排序树结点之间大小关系的特性,使得在二叉排序树中查找的效率会比较高(O(Logn)),但是如果对一棵BST进行一定次数的插入删除操作之后,它就会变成一个渐线性结构,从而使得查找效率降低(O(N)),因此插入删除结点之后需要保持二叉排序树的平衡,从而出现了AVL(平衡二叉树)树。

  AVL树的实现:

/**@数据结构:树->AVL树 
 **@作者:9761滴  
 **@存储结构:链表 
 **/
 
//本文件中实现了AVL树的
    /*
        1.查找
        2.插入
        3.创建 
    */
     
//等操作
#include<cstdio>
#include<stdlib.h>
typedef int ElementType;
typedef struct node{
    int height;
    ElementType data;
    node* lchild;
    node* rchild;
}node,AVLTree;
//生成一个新的结点 
node* newNode(ElementType x){
    node* p=(node*)malloc(sizeof(node));
    if(p==NULL)
        return NULL;
    p->data=x;
    p->height=1;
    p->lchild=NULL;
    p->rchild=NULL;
    return p;
}
//返回以root为根节点的子树的高度 
int getHeight(node* root){
    if(root==NULL)
        return 0;
    return root->height;
} 
//计算root的平衡因子
int getBalanceFactor(node* root){
    return getHeight(root->lchild)-getHeight(root->rchild);
} 
int max(int a,int b){
    if(a>b)
        return a;
    else
        return b;
}
//跟新以root为根节点的子树的高度
void updateHeight(node* root){
    root->height=max(getHeight(root->lchild),getHeight(root->rchild))+1;
}
//查找AVL树中数据域为x的结点 
node* search(node* root,ElementType x){
    if(root==NULL)
        return NULL;
    if(root->data==x)
        return root;
    if(x>root->data) 
        return search(root->rchild,x);
    else
        return search(root->lchild,x);
}
//左旋操作
void LeftRotation(node* &root){
    node* temp=root->rchild;
    root->rchild=temp->lchild;
    temp->lchild=root;
    updateHeight(root);
    updateHeight(temp);
    root=temp;
} 
//右旋操作
void RightRotation(node* &root){
    node* temp=root->lchild;
    root->lchild=temp->rchild;
    temp->rchild=root;
    updateHeight(root);
    updateHeight(temp);
    root=temp; 
} 
//往AVL树中插入一个数据域为x的新结点,并保持AVL树特性
bool insert(node* &root,ElementType x){
    node* p=newNode(x);
    if(p==NULL)
        return false;
    if(root==NULL){
        root=p;
        return true;
    }
    if(x>root->data){
        insert(root->rchild,x);
        updateHeight(root);
        if(getBalanceFactor(root)==-2){
            if(getBalanceFactor(root->rchild)==-1){
                LeftRotation(root);
            }
            else if(getBalanceFactor(root->rchild)==1){
                RightRotation(root->rchild);
                LeftRotation(root);
            }
        }
    }
    else{
        insert(root->lchild,x);
        updateHeight(root);
        if(getBalanceFactor(root)==2){
            if(getBalanceFactor(root->lchild)==1){
                RightRotation(root);
            }
            else if(getBalanceFactor(root->lchild)==-1){
                LeftRotation(root->lchild);
                RightRotation(root);
            }
        }
    }
}
//创建AVL树
node* Create(ElementType data[],int n){
    node* root=NULL;
    for(int i=0;i<n;i++){
        insert(root,data[i]);
    }
    return root;
} 
int main(){
    return 0;
}

 

3.哈夫曼树

  首先搞清楚几个概念:

    权:在很多应用中,树中结点常常被赋予一个表示某种意义的数值,称为该结点的权。

    带权路径长度:从根节点到某个结点的路径长度(经过边的条数)与该结点权值的乘积

    树的带权路径长度WPL:树中所有叶结点(注意是叶结点哦)的带权路径长度之和称为该树的带权路径长度。

    哈夫曼树:含有n个带权叶节点的二叉树中,WPL最小的二叉树称为哈夫曼树。

  将n个权值分别为w1,w2...wn的结点构造成一棵哈夫曼树的方法:

    1.将这n个结点分别作为n棵仅含有一个结点的二叉树,构成森林F

    2.构造一个新的结点,从F中选取两棵根节点权值最小的树作为新结点的左、右孩子,并且将新结点的权值置为左右子树上根节点的权值之和

    3.从F中删除刚才选出的两棵树,同时将新得到的树加入F中

    4.重复步骤 2、3, 直到F中只剩下一棵树。

  哈夫曼树性质:

    每个初始结点最终都称为叶节点,且权值越小的结点到根节点的路径长度越大

    构造过程中一共新建了n-1个结点,因此哈夫曼树的结点总数为2n-1

    每次构造都选择2棵树作为新结点的孩子,因此哈夫曼树中不存在度为1的结点

    n个权值结点构造的哈夫曼树不唯一

4.哈夫曼编码

  固定长度编码:在字符编码中,常用长度相等的二进制位表示每个字符,这种编码方式称为固定长度编码。

  可变长度编码:用不等长的二进制位表示不同字符

  哈夫曼编码:可变长度编码的一种,用于数据压缩。

  前缀编码:没有一个编码是另一个编码的前缀,这样的编码叫做前缀编码。如对a,b,c编码,a=0,b=101,c=100, 那么串码就00101100就可以唯一的识别为aabc,而不产生歧义

  哈夫曼编码过程:

    将每个字符当作一个独立的结点,其权值为它出现的频度,构造哈夫曼树。

    构造好哈夫曼树之后,所有字符都出现在了叶节点中,可以将字符的编码解释为从根到该节点字符的路径上边标记的序列,其中0表示“转向左孩子”的边,1表示转向有孩子的边。

 

posted @ 2020-11-16 17:36  9761滴  阅读(164)  评论(0编辑  收藏  举报