当前位置: 首页 > news >正文

数据结构基础内容(第七篇:堆、哈夫曼树)

# 堆 Heap  

优先队列(Priority Queue)  

结构性:用 *数组* 表示的完全二叉树;  

有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)  

    * “最大堆(MaxHeap)”,也称“大顶堆”:最大值  

    * “最小堆(MinHeap)”,也称“小顶堆” :最小值  

主要操作有:  

• MaxHeap Create( int MaxSize ):创建一个空的最大堆。  

• Boolean IsFull( MaxHeap H ):判断最大堆H是否已满。  

• Insert( MaxHeap H, ElementType item ):将元素item插入最大堆H。  

• Boolean IsEmpty( MaxHeap H ):判断最大堆H是否为空。  

• ElementType DeleteMax( MaxHeap H ):返回H中最大元素(高优先级)。  

## 结构

```c

typedef struct HNode *MaxHeap;

struct HNode {

    ElementType *Elements; // 存储堆元素的数组

    int Size;   // 堆的当前元素的个数

    int Capacity  // 堆的最大容量

}

```

## 创建

```c

MaxHeap CreateHeap(int Maxsize)

{

    MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));

    H->Elements = malloc((MaxSize+1) * sizeof(ElementType)); // 因为这里是一个数组,需要分配空间

    H->Size = 0; // 当前是空的

    H->Capacity = MaxSize;  // 堆的最大容量

    H->Elements[0] = MaxSize; /* 定义“哨兵”为大于堆中所有可能元素的值,便于以后操作 */

    return H;

}

```

## 插入

将新增结点插入到从其父结点到根结点的有序序列中  

将新item放在最后的位置Size+1, 然后跟他的父节点i/2比较,不断把父节点往下(子节点)移动,直到其父节点大于item

```c

void InsertHeap(MaxHeap H, ElementType item)

{

    int i;

    if (IsFull(H)){

        printf("FULL\n");

        return;

    }

    i = ++H->Size;  // H->Size++; i = H->Size; 把新增元素放在末尾H->Size++的位置上

    for(; H->Elements[i/2] < item; i/=2){  // 其父节点小于它

        H->Elements[i] = H->Elements[i/2]; // 把它的父节点,向下过滤, 插入的item向上过滤

    }

    // 当它的父结点[i/2]比它大的时候, 跳出循环

    H->Elements[i] = item;  // 填上item

}


```

# 删除  

取出根结点(最大值)元素,同时删除堆的一个结点。  

* 最后的元素替补根的位置

* 有序化, 父结点和更大的那个子结点比较,将子结点不断往上移, 直到父结点不子结点大  

```c

ElementType DeleteMax(MaxHeap H)

{

    int parent, child;

    ElementType MaxItem, temp;

    if(IsEmpty(H)){

        printf("Empty\n");

        return;

    }

    MaxItem = H->Elements[1]; // 取出最大值

    /* 用最大堆中最后一个元素从根节点开始向上过滤下层节点 */

    temp = H->Elements[Size];  // 把堆中最后一个值,交给temp

    Size--;

    for(parent=1; parent*2 <= H->Size; parent=child)

    {

        child = parent*2  // 左儿子

        /* child=左右儿子中大的那个, 当右儿子存在,且右儿子的值比左儿子大时,让child=右儿子 */

        if((child!= H->Size) &&

        (H->Elements[child] < H->Elements[child+1]))

            child++;

       

        /* 当temp比child的值大时跳出循环 */

        if (temp >= Elements[child])

            break;

        else

            H->Elements[parent] = H->Elements[child]; //当parent < child,这个parent位置上变为child的值

    }

    H->Elements[parent] = temp;

    return MaxItem;

}

```

# 堆排序  

方法1:通过插入操作,将N个元素一个个相继插入到一个初 始为空的堆中去,其时间代价最大为O(N logN)。

方法2:在线性时间复杂度下建立最大堆。O(N)  

(1)将N个元素按输入顺序存入,先满足完全二叉树的结构特性  

(2)调整各结点位置,以满足最大堆的有序特性。  

```c

void BuildHeap(MaxHeap H){

    int i;

    for(i = H->Size/2; i>0; i--)

    {// 从最后一个结点的父节点开始,到根节点为止

        PercDown(H, i);

    }

}

// 下滤函数, 将Maxheap中以H->Data[p]为根的子堆调整为最大堆

void PercDown( MaxHeap H, int p)

{

    int parent, child;

    X = H->Data[p]; // 取出根节点的值

    for(parent =  p; parent*2 <= H->Size; parent = child)

    {

        child = parent * 2;

        if( (child != H->Size) && (H->Data[child] < H->Data[child+1]))

        {

            child++;

        }

        if (X > H->Data[child])

            break;

        else // 将X下滤

            H->Data[parent] = H->Data[child];

    }

    H->Data(parent) = X;

}

```

# 哈夫曼树与哈夫曼编码

带权路径WPL Weighted Path Length)长度最小, 最优二叉树.  

数据结构:

```c

typedef struct TreeNode *HuffmanTree;

struct TreeNode{

    int Weight;

    HaffmanTree left;

    HaffmantREE Right;

}

```

利用最小堆进行构造:

```c

HuffmanTree Huffman(MinHeap H)

{

    int i;

    HuffmanTree T;

    BuildMinHeap(H); // 将H->Elements[]按照权重调整为最小堆

    for(i=1; i < H->Size; i++) // 做size-1次合并

    {

        T = malloc(sizeof(struct TreeNode)); // 建立新结点

        /*从最小堆中删除一个结点,作为新T的左子结点*/

        T->Left = DeleteMin(H);

        T->Right = DeleteMin(H);

        /*计算新权值*/

        T->Weight = T->Left->Weight+T->Right->Weight;

        Insert( H, T ); /*将新T插入最小堆*/

    }

    T = Deletemin(H); // 最小堆中的最后一个元素就是指向Huffman树根节点的指针

    return T;

}

```

哈夫曼树的特点:

1. 没有度为1的结点

2. n个叶子结点的哈夫曼树共有2n-1个结点;  

    因为: n2= n0 -1,

    总结点数 = n0 + n2 = 2n0 - 1

3. 哈夫曼树的任意非叶节点的左右子树交换后仍是哈夫曼树;

4. 对同一组权值{w1 ,w2 , ...... , wn},存在不同构的两

# 集合及运算  

双亲表示法: 孩子指向父节点  

数据结构

```c

typedef struct

{

    ElementType Data;

    int Parent;  // 其父节点的下标

} SetType;

```

## 查找某个元素所在的集合  

用根节点表示

```c

int Find(SetType S[], ElementType X)

{

    /* 在数组S中查找值为X的元素所属的集合, MaxSize为数组最大长度 */

    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;

}

```

## 集合的并运算  

如果它们不同根,则将其中一个根结点的父结点指针设置成

   另一个根结点的数组下标。

```c

void Union( SetType S[], ElementType X1, ElementType X2 )

{

    int Root1, Root2;

    Root1 = Find(S, X1);

    Root2 = Find(S, X2);

    if( Root1 != Root2 )

        S[Root2].Parent = Root1;

}

```

为了使树更矮,合并时按秩合并  

直接用一个数组表示,不用之前的数据结构了

```c

#define MAXN 1000                  /* 集合最大元素个数 */

typedef int ElementType;           /* 默认元素可以用非负整数表示 */

typedef int SetName;               /* 默认用根结点的下标作为集合名称 */

typedef ElementType SetType[MAXN]; /* 假设集合元素下标从0开始 */

SetName Find(SetType S, ElementType X){

    for(; S[X] > 0; X=S[X]);

    return X.

}

void OldUnion(SetType S, SetName Root1, SetName Root2){

    S[Root2] = Root1;

}

void Union( SetType S, SetName Root1, SetName Root2 )

{ /* 这里默认Root1和Root2是不同集合的根结点 */

    /* 保证小集合并入大集合 */

    if ( S[Root2] < S[Root1] ) { /* 如果集合2比较大 */

        S[Root2] += S[Root1];     /* 集合1并入集合2  */

        S[Root1] = Root2;

    }

    else {                         /* 如果集合1比较大 */

        S[Root1] += S[Root2];     /* 集合2并入集合1  */

        S[Root2] = Root1;

    }

}

```

路径压缩

```c

SetName Find( SetType S, ElementType X )

{

    if ( S[X] < 0 ) /* 找到集合的根 */

        return X;

    else

        return S[X] = Find( S, S[X] ); /* 路径压缩 */

}

```

http://www.lryc.cn/news/601615.html

相关文章:

  • 电子电气架构 --- 软件bug的管理模式
  • 「iOS」————MRC
  • Flink2.0学习笔记:Stream API 常用转换算子
  • 第十八章:AI的“通感”:揭秘图、文、音的共同语言——CLIP模型
  • 常见认证机制详解
  • Unity FXAA
  • 设计模式(六)创建型:单例模式详解
  • 五、搭建springCloudAlibaba2021.1版本分布式微服务-gateway网关
  • 新手开发 App,容易陷入哪些误区?
  • c++加载qml文件
  • 【学习笔记】DexMimicGen:通过模仿学习实现双臂灵巧操作的自动化数据生成
  • 小白成长之路-Ansible自动化(一)
  • 小白投资理财 - 从换手率和成交量分析股票趋势
  • 【机器学习深度学习】NLP评价指标 BLEU 和 ROUGE
  • 扩展组件(uni-ui)之uni-group
  • Dify 本地化部署深度解析与实战指南
  • C语言自定义数据类型详解(四)——联合体
  • Baumer工业相机堡盟工业相机如何通过YoloV8深度学习模型实现PCB上二维码检测识别(C#代码UI界面版)
  • 2.安装CUDA详细步骤(含安装截图)
  • JavaEE--3.多线程
  • [N1盒子] 斐讯盒子N1 T1通用刷机包(可救砖)
  • [硬件电路-96]:什么是闭环反馈?什么是闭环正反馈控制?什么是闭环负反馈控制?
  • Java面试精进:测试、监控与序列化技术全解析
  • 【模电笔记】—— 波形发生电路(波形振荡器)
  • Redisson的布隆过滤器
  • 安卓打包遇到问题
  • 重温经典,小巧方便的 WinXP 来啦!提供离线驱动
  • net8.0一键创建支持(Kafka)
  • 深度学习在自动驾驶车辆车道检测中的应用
  • 命令行和neovim的git操作软件-lazygit