文章目录
堆排序
堆排序的由来还得说到简单选择排序,由简单选择排序中的在进行剩余的n -2个数据比较时若能利用前n-1次比较的所得信息,则可以减少以后各趟排序的比较次数,由此联想出锦标赛排序也就是树形排序,但是树形排序的辅助存储空间较多,和“最大值”进行比较多余的比较等缺点,因此,在1964年威洛姆斯提出了堆排序,堆排序灵活的应用了最堆的特性来达到选择的目的。
堆排序只需要一个记录大小的辅助空间,每个待排序的记录仅占有一个存储空间。
- 要进行堆排序首先我们要先建一个最大(小)堆,(这里关于如何建最堆以下代码中有体现,)
- 图示:
- 然后是在输出堆顶元素后再次调整剩余元素为一个新的最堆这个过程也叫作“筛选”。
- 图示:
- 代码:
Sort.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#define N 10
void InputData(int* d);//输入数据
void PrintData(int* d);//输出数据
void InsertSort(int* d);//直接插入排序
void ShellSort(int* d, int length);//希尔排序
void SelectSort(int* d, int len);//选择排序
typedef int HPDataType;
typedef struct Heap
{
HPDataType* a;
int num;
int capacity;
}Heap, *pHeap;//创建堆
void HeapSort(HPDataType* d, int n);//堆排序
pHeap HeapCreat(HPDataType* d, int n);//创建大堆
void HeapDestory(pHeap hp);//销毁堆
void HeapPrint(Heap hp);//堆打印
void AdjustDown(HPDataType* a, int n, int root);//堆内渗透函数
Sort.c
#include "Sort.h"
void InputData(int* d)
{
int i = 0;
printf("输入数据:\n");
for (i = 0; i < N; i++)
{
printf(" d[%d]:", i);
scanf("%d", &d[i]);
printf(" ");
}
printf("\n\n");
}
void PrintData(int* d)
{
int i = 0;
printf("输出数据:");
for (i = 0; i < N; i++)
{
printf("%d ", d[i]);
}
printf("\n\n");
}
void Swap(int* d1, int *d2)
{
assert(d1 && d2);
int tmp = 0;
tmp = *d1;
*d1 = *d2;
*d2 = tmp;
}
//直接插入排序
//假设待排序的记录存放在数组d[0..N-1]中。
//初始时,d[0]自成1个有序区,无序区为R[1.N-1]。
//从i=1起直至i=N-1为止,依次将d[i]插入当前的有序区d[1..N-1]中,
//生成含N个记录的有序区。
void InsertSort(int * d)
{
assert(d);
int i = 0;
int j = 0;
int right = 0;
for (i = 1; i < N; i++)
{
//在原来的数组里操作,比较一次移动一次
right = i;//记录要载入数据的位置,哨兵
for (j = right - 1; j >= 0; j--)
{
if (d[right] < d[j])
{
Swap(&d[right], &d[j]);
}
right--;
}
}
printf("排序成功!\n\n");
}//InsertSort
//基础:插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。
//通过分组使数组渐变有序
//最后在直接插入排序
void ShellSort(int *d, int length)//希尔排序
{
assert(d);
int i = 0, j = 0;
int gap = 0;//步长即分组的个数
int temp = 0;//哨兵
for (gap = length / 2; gap > 0; gap /= 2)//完成分组
{
for (i = gap; i < length; i++)//寻找分组后的元素
{
temp = d[i];//哨兵位
for (j = i - gap; j >= 0 && temp < d[j]; j -= gap)//根据下一个分组的下标寻找上个分组的元素
{
d[j + gap] = d[j];
}
d[j + gap] = temp;//填补上一个分组的相应位置元素
}
}
printf("排序成功!\n\n");
}//ShellSort()
int SelectMinKey(int* d, int i, int len)
{
assert(d);
int min = i;
for (int cur = i + 1; cur < len; ++cur)
{
if (d[min] > d[cur])
{
min = cur;
}
}
return min;
}
void SelectSort(int* d, int len)//选择排序
{
assert(d);
for (int i = 0; i < len; ++i)//选择第i小的元素
{
int j = SelectMinKey(d, i, len);
if (i != j)
{
Swap(&d[i], &d[j]);//找到最小值并与i位只交换
}
}
printf("排序成功!\n\n");
}//SelectSort();
void HeapSort(HPDataType* d, int n)//堆排序
{
assert(d);
int i = 0;
Heap hp;
//通过堆的属性来寻找最大(小)值,升序:大堆, 降序:小堆
//创建大堆
hp.a = (HPDataType*)malloc(sizeof(HPDataType)* n);//堆元素开辟空间
assert(hp.a);//防止开辟失败
hp.capacity = n;
hp.num = n - 1;//元素下标0 - (n-1)
for (i = 0; i < n; i++)
{
hp.a[i] = d[i];
}
//向下调整为大堆
for (i = ((n - 2) / 2); i >= 0; --i)
{
AdjustDown(hp.a, hp.num + 1, i);
}
printf("大堆:");
for (i = 0; i < hp.num; i++)
{
printf("%d ", hp.a[i]);
}
printf("\n\n");
//排序
int end = hp.num;
while (end >= 0)
{
//交换root和最后一个节点的数据
Swap(&(hp.a[0]), &(hp.a[end]));
--end;//下标前进1
n--;//数据减一
AdjustDown(hp.a, n, 0);
}
printf("排序后:");
for (i = 0; i <= hp.num; i++)
{
printf("%d ", hp.a[i]);
}
printf("\n\n");
}
void AdjustDown(HPDataType* a, int n, int root)//堆内渗透函数
{
int parent = root;
int child = parent * 2 + 1;
while (child < n)
{
//选左右中大的一个
if (a[child + 1] > a[child] && child + 1 < n)
{
child += 1;
}
if (a[child] > a[parent])
{
Swap(&a[child], &a[parent]);//在函数外部进行交换要传址
parent = child;
child = child * 2 + 1;
}
else
{
break;
}
}
}
main.c
#include "Sort.h"
void Test()
{
int d[N] ={15, 45, 23, 78, 63, 5, 23, 58, 31, 10};
/*int d[N];
InputData(d);*/
PrintData(d);
/*InsertSort(d);
PrintData(d);*/
/*ShellSort(d, sizeof(d)/sizeof(d[0]));
PrintData(d);*/
/*SelectSort(d, sizeof(d) / sizeof(d[0]));
PrintData(d);*/
HeapSort(d, sizeof(d) / sizeof(d[0]));
}
int main()
{
Test();
system("pause");
return 0;
}