1.对堆的排序
之前我们学习过了以顺序表为结构基础的对堆的改变,向上调整,向下调整。
都是基于对数组下标的调整。今天我们来学习一下对 堆的排序。
对堆该如何排序,如果我们对堆实现我们的升序,该如何实现呢。
当然堆的底层结构就是我们的数组,我们当然可以是用冒泡排序,时间复杂度是O( n^2 );
我们可以尝试用堆的功能来实现。假如我们使用建设小堆的原理来排序,那么堆顶一定是最小的元素,剩下的按照左子树比右子树小的原则进行排列,但不一定是我们要的升序。
建立的是大堆,那我们的堆顶就是最大的元素,既然最大的我们有了,加上我们之前学过一次如何删掉堆顶元素,所以我们建设大堆,接着交换堆顶和堆尾,像删掉堆顶那样,再进行不加入刚才最大元素,重新排列,找出此时的堆的最大元素,然后再交换。。。好了我们可以尝试实现一下。
void HeapSort(innt* arr, int x)
{
//排升序 用向下调整建大堆 然后交换首尾 再减小sz
for (int i = (x - 2) / 2; i >= 0; i--)
{
adjustdown(arr, x, i);
}
while (x)
{
Swap(&arr[0], &arr[x - 1]);
x--;
adjustdown(arr, x, 0);
}
}//这个的时间复杂度和向上调整一样,都是O(N*logN)
值得一提的是升序建大堆,降序建小堆,因为我们需要排出最大的,次打的。。。
2.TopK问题
这个问题就是假设我们在一万个数里面找到前十个元素
我们可以想到的方法是快速排序,它的时间复杂是O(N*logN)
冒泡排序时间复杂度是O( n^2 ),n是一万还好,如果是n是一亿甚至更大呢,那时间将非常大,所以我们将用堆实现一种更快的。
方法:要最大的前多少个元素我们就建一个小堆,注意了和我们刚才的不一样,这次我们要建立的是小堆,怎么会是小堆呢,我们明明需要的是最大的数,假设我们就挑出了这一万个数的前十个任意的数,放进我们的小堆里面,堆顶是不是最小的数字呢,如果我们开始遍历剩下的数字,如果有比堆顶大的元素我们就把堆顶元素将这个大数存进去,再重新对这个堆排序,那么堆顶又出现了此时堆里面最小的元素,那么我们依次遍历下去,我们的堆里面如果存进去最大的十个数之一,他是永远被换出堆的情况的,因为没有比他们十个数还大的数被换,只要进堆了,每次的排序都是将不是最大的十个数往出筛选,将大数往堆里面放。虽然建立的小堆,但我们把比堆顶大的数往进放。
我们此时需要一万个数,那我们实现在文件里面,这样更加直观清晰。我们先把一万个随机的数字,在文件夹中生成好,一会再调用。
void CreatHeap()
{
int m = 10000;
srand(time(0));
char* file = "ss.text";
FILE* fin = fopen(file, "w");
//先创建数
while (m)
{
int x = rand();
fprintf(fin, "%d\n", x);
m--;
}
fclose(fin);
}
建立好一万个数字,接着我们找出最大的前n位数
void TopK()
{
printf("输入你要的top");
int m = 0;
scanf("%d", &m);
char* file = "ss.text";
FILE* fout = fopen(file, "r");
if (fout == NULL)
{
perror("fopen fail");
exit(1);
}
//建小堆
innt* arr = (innt*)malloc(sizeof(innt) * m);
if (arr == NULL)
{
perror("malloc fail");
exit(1);
}
for (int i = 0; i < m; i++)
{
fscanf(fout, "%d", &arr[i]);
}
//向下调整
for (int j = (m - 2) / 2; j < m; j++)
{
adjustdown(arr, m, j);
}
int x = 0;
while (fscanf(fout, "%d", &x) != EOF)//将大数往堆里面放
{
if (x > arr[0])
{
arr[0] = x;
adjustdown(arr, m, 0);
}
}
for (int i = 0; i < m; i++)
{
printf("%d\n", arr[i]);
}
fclose(fout);
}
感谢观看,欢迎指出错误