在学习堆排序之前,首先需要了解堆的含义:在含有 n 个元素的序列中,如果序列中的元素满足下面其中一种关系时,此序列可以称之为堆。
- ki ≤ k2i 且 ki ≤ k2i+1(在 n 个记录的范围内,第 i 个关键字的值小于第 2*i 个关键字,同时也小于第 2*i+1 个关键字),这是小顶堆
- ki ≥ k2i 且 ki ≥ k2i+1(在 n 个记录的范围内,第 i 个关键字的值大于第 2*i 个关键字,同时也大于第 2*i+1 个关键字),这是大顶堆
对于堆的定义也可以使用完全二叉树来解释,因为在完全二叉树中第 i 个结点的左孩子恰好是第 2i 个结点,右孩子恰好是 2i+1 个结点。如果该序列可以被称为堆,则使用该序列构建的完全二叉树中,每个根结点的值都必须不小于(或者不大于)左右孩子结点的值。
堆排序过程的代码实现需要解决两个问题:
- 如何将得到的无序序列转化为一个堆?
- 在输出堆顶元素之后(完全二叉树的树根结点),如何调整剩余元素构建一个新的堆?
给定一个数组,怎么将数组用堆排序。目前网上大部分资料都是从数组下标1开始,这样就需要另外申请一个数组,索引为0的元素叫哨兵。
下面直接给出代码
#include <stdio.h>
#include <stdlib.h>
void adjustHeap(int *a,int s,int n)
{
int rc = a[s];
int j=0;
for(j=2*s;j<=n;j=2*j)
{
if(j+1<=n && a[j] < a[j+1])
j++;
if(rc > a[j])
break;
a[s] = a[j];
s = j;
}
a[s] = rc;
}
void HeapSort(int *a,int m)
{
int i=0,temp=0;
for(i=m/2;i>0;i--)
{
adjustHeap(a,i,m);
}
for(i=m;i>0;i--)
{
temp = a[1];
a[1] = a[i];
a[i] = temp;
adjustHeap(a,1,i-1);
}
}
int main()
{
int a[11] = {-1,1,3,5,7,9,0,2,6,4,8};
HeapSort(a,10);
for(int i=1;i<11;i++)
printf("%d ",a[i]);
return 0;
}
结果
[root@localhost heapsort]# gcc -o heapsort heapsort.c -std=gnu99
[root@localhost heapsort]#
[root@localhost heapsort]# ./heapsort
0 1 2 3 4 5 6 7 8 9
下面给出我自己琢磨的从数组下标0进行排序的堆排序算法
#include <stdio.h>
#include <stdlib.h>
void adjustHeap(int *a,int s,int n)
{
int rc = a[s];
int j=0;
for(j=2*s+1;j<n;j=2*j+1)
{
if(j+1<n && a[j] < a[j+1])
j++;
if(rc > a[j])
break;
a[s] = a[j];
s = j;
}
a[s] = rc;
}
void HeapSort(int *a,int m)
{
int i=0,temp=0;
for(i=m/2-1;i>=0;i--)//从数组下标0开始算,则具有叶子节点的计算需要重新调整为m/2-1
{
adjustHeap(a,i,m);
}
for(i=m-1;i>0;i--)
{
temp = a[0];
a[0] = a[i];
a[i] = temp;
adjustHeap(a,0,i);//此处i不再是上面的i-1,因为i直接从m-1开始,adjustHeap函数中j<n,不能再等于n
}
}
int main()
{
int a[10] = {1,3,5,7,9,0,2,6,4,8};
HeapSort(a,10);
for(int i=0;i<10;i++)
printf("%d ",a[i]);
return 0;
}
现在用堆排序来做leecode上的题
题目提示用快速排序的思想,后面会给出用快排的做法,这里用堆排序快速找出第K大的数,使用大顶堆,每次调整后,交换出来的a[0]就是当前最大的数,这样进行K次调整后,出来的数就是第K大的数。
/**
*
* @param a int整型一维数组
* @param aLen int a数组长度
* @param n int整型
* @param K int整型
* @return int整型
*/
void adjustHeap(int *a,int s,int n)
{
int rc = a[s];
int j=0;
for(j=2*s+1;j<n;j=2*j+1)
{
if(j+1<n && a[j] < a[j+1])
j++;
if(rc > a[j])
break;
a[s] = a[j];
s = j;
}
a[s] = rc;
}
int findKth(int* a, int aLen, int n, int K )
{
// write code here
int i = 0;
for(i=aLen/2-1;i>=0;i--)
{
adjustHeap(a,i,aLen);
}
for(i=aLen-1;i>0;i--)
{
if(aLen - i == K)
break;
a[0] = a[i];
adjustHeap(a,0,i);
}
return a[0];
}
下面用快速排序的思想来解这道题
int getIndex(int *a,int low,int high)
{
int rc = a[low];
while(low<high)
{
while(low<high && a[high] >= rc)
high--;
a[low] = a[high];
while(low<high && a[low] <= rc)
low++;
a[high] = a[low];
}
a[low] = rc;
return low;
}
void quickSort(int *a,int low, int high)
{
if(low<high)
{
int index = getIndex(a,low,high);
quickSort(a,low,index-1);
quickSort(a,index+1,high);
}
}
int findKth(int* a, int aLen, int n, int K )
{
//int KthNum = aLen - K;
//int retKnum;
quickSort(a,0,aLen-1);
return a[aLen - K];
}
此题我原本的思路就是当getIndex的返回值index,在aLen-K的位置上即可终止递归,这个值我想用一个全局变量带出来,但是编译不过,只有排完再取值了