之前我们讨论的堆,都把最大键值存储在根节点中,这种类型的堆可以看作是最大堆。
我们还可以创建另一种堆,把最键小值存储在根节点中,根节点以外的节点的键值,大于或等于存储在它父节点中的键值,这种类型的堆可以看作是最小堆。
具体是用最大堆还是最小堆,就根据我们自身的需要来选择了。
堆的算法到这里就讲完了,下面这段代码包括了之前讲过的堆的所有算法:
#include <stdio.h>
#include <stdlib.h>
void siftUp(int* heap, int index);
void siftDown(int* heap, int index);
void siftDown(int* heap, int siftDownTo, int index);
int* insertElement(int* heap, int element);
int* deleteElement(int* heap, int index);
int* makeHeap(int* array, int arrayLength);
void heapSort(int* heap);
void print(char* description, int* heap);
void siftUp(int* heap, int index)
{
int heapLength = heap[0];
if (index < 1 || index > heapLength)
return;
bool done = false;
while(!done && index > 1)
{
if (heap[index] > heap[index / 2])
{
int temp = heap[index];
heap[index] = heap[index / 2];
heap[index / 2] = temp;
}
else
{
done = true;
}
index /= 2;
}
}
void siftDown(int* heap, int index)
{
int heapLength = heap[0];
siftDown(heap, heapLength, index);
}
void siftDown(int* heap, int siftDownTo, int index)
{
if (index < 1 || index * 2 > siftDownTo)
return;
bool done = false;
while(!done && index * 2 <= siftDownTo)
{
index *= 2;
if (index + 1 <= siftDownTo && heap[index + 1] > heap[index])
index += 1;
if (heap[index] > heap[index / 2])
{
int temp = heap[index];
heap[index] = heap[index / 2];
heap[index / 2] = temp;
}
else
{
done = true;
}
}
}
int* insertElement(int* heap, int element)
{
int heapLength = heap[0];
int resultHeapLength = heapLength + 1;
// 创建一个长度为resultHeapLength+1的int数组
int* resultHeap = (int*)malloc(sizeof(heap) * (resultHeapLength + 1));
if (resultHeap == NULL)
return NULL;
resultHeap[0] = resultHeapLength;
for (int i = 1; i <= heapLength; i++)
{
resultHeap[i] = heap[i];
}
resultHeap[resultHeapLength] = element;
siftUp(resultHeap, resultHeapLength);
return resultHeap;
}
int* deleteElement(int* heap, int index)
{
int heapLength = heap[0];
if (index < 1 || index > heapLength)
return heap;
int x = heap[index];
int y = heap[heapLength];
int resultHeapLength = heapLength - 1;
// 创建一个长度为resultHeapLength+1的int数组
int* resultHeap = (int*)malloc(sizeof(heap) * (resultHeapLength + 1));
if (resultHeap == NULL)
return NULL;
resultHeap[0] = resultHeapLength;
for (int i = 1; i <= resultHeapLength; i++)
{
resultHeap[i] = heap[i];
}
if (index == resultHeapLength)
return resultHeap;
resultHeap[index] = y;
if (y > x)
{
siftUp(resultHeap, index);
}
else
{
siftDown(resultHeap, index);
}
return resultHeap;
}
int* makeHeap(int* array, int arrayLength)
{
if (array == NULL || arrayLength <= 0)
return NULL;
int heapLength = arrayLength;
int* heap = (int*)malloc(sizeof(array) * (heapLength + 1));
if (heap == NULL)
return NULL;
// heap[0]用来存储数组中堆数据的长度,堆数据heap[1]...heap[heapLength]
// 所以数组的实际长度是heapLength+1,我们只对从数组索引为1开始的heapLength个数据进行操作
heap[0] = heapLength;
for (int i = 0; i < arrayLength; i++)
{
heap[i + 1] = array[i];
}
for (int i = heapLength / 2; i >= 1; i--)
{
siftDown(heap, i);
}
return heap;
}
void heapSort(int* heap)
{
if (heap == NULL)
return;
int heapLength = heap[0];
int temp = 0;
for (int i = heapLength; i >= 2; i--)
{
temp = heap[1];
heap[1] = heap[i];
heap[i] = temp;
siftDown(heap, i - 1, 1);
}
}
void print(char* description, int* heap)
{
printf("%s:\t", description);
int heapLength = heap[0];
for(int i = 1; i <= heapLength; i++)
{
printf("%d ", heap[i]);
}
printf("\n");
}
void main()
{
const int ARRAY_LENGTH = 10;
int array[ARRAY_LENGTH] = { 4, 3, 8, 10, 11, 13, 7, 30, 17, 26 };
int* heap = makeHeap(array, ARRAY_LENGTH);
print("从数组初始化堆", heap);
int* heapAfterInsert = insertElement(heap, 20);
print("插入新数据20", heapAfterInsert);
free(heap);
int* heapAfterDelete = deleteElement(heapAfterInsert, 2);
print("删除第2个数据", heapAfterDelete);
free(heapAfterInsert);
heapSort(heapAfterDelete);
print("按非降序排序", heapAfterDelete);
free(heapAfterDelete);
getchar();
}
更多内容请参考:
算法 之 堆 - 简介
算法 之 堆 - Sift-up和Sift-down
算法 之 堆 - 插入和删除
算法 之 堆 - 创建堆
算法 之 堆 - 排序
分享到:
相关推荐
查找算法和排序算法小结 本文总结了常见的查找算法和排序算法,包括顺序查找、二分查找、选择排序、冒泡排序、二分排序、插入排序、希尔排序、堆排序、归并排序等。 一、查找算法 1. 顺序查找(Sequential Search...
6. **冒泡排序**:冒泡排序是最基础的排序算法之一,通过不断交换相邻的不正确顺序的元素来排序。由于其效率低,仅在数据规模较小或特定情况下使用。 7. **交换排序和选择排序**:这两者都是基于交换元素的O(n^2)...
本篇文章将概述几种常见的排序算法,包括快速排序、归并排序、堆排序、Shell排序、插入排序、冒泡排序、交换排序、选择排序以及基数排序。 1. **快速排序**(QuickSort): - 快速排序采用了分治策略,以一个支点...
根据提供的信息,《算法导论》是一本经典的计算机科学教材,...### 小结 以上是针对《算法导论》一书中部分章节的部分习题及其涉及知识点的总结。《算法导论》这本书系统地介绍了算法的设计与分析方法,包括但不限于...
3.4.8 小结 96 3.5 间接寻址 99 3.5.1 基本概念 99 3.5.2 操作 100 3.6 模拟指针 102 3.6.1 SimSpace的操作 103 3.6.2 采用模拟指针的链表 106 3.7 描述方法的比较 110 3.8 应用 111 3.8.1 箱子排序 111 3.8.2 基数...
3.4.8 小结 96 3.5 间接寻址 99 3.5.1 基本概念 99 3.5.2 操作 100 3.6 模拟指针 102 3.6.1 SimSpace的操作 103 3.6.2 采用模拟指针的链表 106 3.7 描述方法的比较 110 3.8 应用 111 3.8.1 箱子排序 111 3.8.2 基数...
### 排序算法的稳定性和时间复杂度小结 #### 一、引言 排序算法是计算机科学中的基本算法之一,广泛应用于各种场景之中。排序算法不仅关注排序的速度(时间复杂度),还关注排序过程中是否能够保持相等元素原有的...
3.4.8 小结 96 3.5 间接寻址 99 3.5.1 基本概念 99 3.5.2 操作 100 3.6 模拟指针 102 3.6.1 SimSpace的操作 103 3.6.2 采用模拟指针的链表 106 3.7 描述方法的比较 110 3.8 应用 111 3.8.1 箱子排序 111 3.8.2 基数...
3.4.8 小结 96 3.5 间接寻址 99 3.5.1 基本概念 99 3.5.2 操作 100 3.6 模拟指针 102 3.6.1 SimSpace的操作 103 3.6.2 采用模拟指针的链表 106 3.7 描述方法的比较 110 3.8 应用 111 3.8.1 箱子排序 111 3.8.2 基数...
高级排序算法通常具有更好的时间复杂度,例如快速排序(Quick Sort)、归并排序(Merge Sort)和堆排序(Heap Sort),它们的时间复杂度为O(n log n)。这些算法通过分治策略或利用数据结构特性,实现了更高效的排序...
3.4.8 小结 96 3.5 间接寻址 99 3.5.1 基本概念 99 3.5.2 操作 100 3.6 模拟指针 102 3.6.1 SimSpace的操作 103 3.6.2 采用模拟指针的链表 106 3.7 描述方法的比较 110 3.8 应用 111 3.8.1 箱子排序 111 ...
小结 练习 参考文献第6章 优先队列(堆) 6.1 模型 6.2 一些简单的实现 6.3 二叉堆 6.3.1 结构性质 6.3.2 堆序性质 6.3.3 基本的堆操作 6.3.4 其他的堆操作 6.4 优先队列的应用 6.4.1 选择问题...
小结 练习 参考文献第6章 优先队列(堆) 6.1 模型 6.2 一些简单的实现 6.3 二叉堆 6.3.1 结构性质 6.3.2 堆序性质 6.3.3 基本的堆操作 6.3.4 其他的堆操作 6.4 优先队列的应用 6.4.1 选择问题...
1.4小结 第2章数组 2.1数组 2.2Delphi中的数组类型 2.3TList类和指针数组 2.4磁盘数组 2.5小结 第3章链表、栈和队列 3.1单链表 3.2双向链表 3.3链表的优缺点 3.4栈 3.5队列 3.6小结 .第4章查找 4.1比较...
1.4小结 第2章数组 2.1数组 2.2Delphi中的数组类型 2.3TList类和指针数组 2.4磁盘数组 2.5小结 第3章链表、栈和队列 3.1单链表 3.2双向链表 3.3链表的优缺点 3.4栈 3.5队列 3.6小结 .第4章查找 4.1比较...
【华为OD机考小结及算法编程试题解析】 华为OD机考是华为公司针对软件开发和测试岗位的招聘环节,其重要性不言而喻,因为它直接影响候选人的定级和薪资。机考主要包含三道题目,两道简单,一道中等,涵盖50%的软件...
小结 练习 参考文献 第2章 算法分析 2.1 数学基础 2.2 模型 2.3 要分析的问题 2.4 运行时间计算 2.4.1 一个简单的例子 2.4.2 一般法则 2.4.3 最大子序列...