堆排序:使用堆数组这个数据结构来实现数组的排序,堆本身和一个完全二叉树类似,可以通过二叉树的层序遍历来创建一个堆。(“堆”一词来源于堆排序,但是现在很多都被引用为“垃圾存储机制”)。
堆排序算法时间复杂度:O(nlgn)。
由于堆排序是直接在原址上进行排序的,所以只需要额外的常数量级的存储空间。即空间复杂度为O(1).
用于排序的堆可分为最大堆和最小堆:
最大堆:孩子节点的值不大于其父节点的值–根为最大节点
最小堆:孩子节点的值不小于其父节点的值–根为最小节点
以下将使用最大堆来实现递增排序:
堆排序的创建过程:
一,维护最大堆的性质:堆中的元素在进行递归交换数值的时候可能会导致新堆不符合最大堆的性质,因此需要在每一个递归交换数值之后都对堆进行维护,以满足最大堆的性质。(在后面的堆排序代码中将会看到其必要性)。
代码实现:
/**
* 时刻用来维护最大堆的性质
* @param A 堆数组
* @param pos 需要维护堆的节点处(以此节点为根节点,建立最大堆
*/
void MaxHeapIfy(int *A,int pos,int len)
{
int leftchild = LeftChild(pos); //用来返回左孩子节点(2*pos)
int rightchild = RightChild(pos); //用来返回右孩子节点(2*pos+1)
int largest_num_pos; //保存更大值的位置
int temp;
//比较左孩子节点和根节点的大小,要符合最大堆的性质(注意这里左孩子节点不能越界),由于这里位置1为根节点和数组下标差1,因此要减去1
if(leftchild <= len && (A[leftchild - 1] >= A[pos - 1]))
largest_num_pos = leftchild;
else
largest_num_pos = pos;
//比较右孩子节点和左孩子节点,将更大的值和父节点;进行替换
if(rightchild <= len && (A[rightchild - 1] >= A[largest_num_pos - 1]))
largest_num_pos = rightchild;
if(largest_num_pos != pos) //位置改变,则交换两者的位置
{
temp = A[largest_num_pos - 1];
A[largest_num_pos - 1] = A[pos - 1];
A[pos - 1] = temp;
MaxHeapIfy(A,largest_num_pos,len); //交换完成后继续维护最大堆的性质
}
}
二:创建二叉堆(最大堆):这里用来创建一个二叉堆,并且满足最大二叉堆的性质。
代码实现:
/**
* 构建最大堆
* @param A 堆数组
* @param n 堆数组的最大数据量
*/
void Build_Max_Heap(int *A,int len){
for(int i = len/2;i >= 1;i--) //从最后一个叶节点的根节点开始维护最大堆的性质,
MaxHeapIfy(A,i,len);
}
三:堆排序,这里使用创建好的堆进行堆排序,由于创建是最大堆,因此根节点的值是最大的,可以将根节点的值和最后一个节点的值进行原址交换(最大值放于数组的最后一位),在维护最大堆,将倒数第二大的值放于数组倒数第二位,以下是代码实现:
/**
* 堆排序的
* @param A 需要排序的数组
* @param len 二叉堆的长度
*/
void HeapSort(int *A,int len)
{
Build_Max_Heap(A,len); //创建最大堆
int temp;
for(int i = len - 1;i >= 1;i--)
{
temp = A[0]; //将最大堆的第一个元素和最后一个元素进行交换
A[0] = A[i];
A[i] = temp;
len -= 1; //堆的长度-1,后面维护的时候不会维护到已将交换的最大数据
MaxHeapIfy(A,1,len);
}
}
以下是主函数的测试:
代码:
int main()
{
int A[10] = {2,3,19,1,39,22,10,4,4,15};
printf("排序前:\n");
for(int i = 0;i < 10;i++)
{
printf("%d\t",A[i]);
}
Build_Max_Heap(A,10);
printf("\n创建最大堆:\n");
for(int i = 0;i < 10;i++)
{
printf("%d\t",A[i]);
}
printf("\n排序后:\n");
HeapSort(A,10);
for(int i = 0;i < 10;i++)
{
printf("%d\t",A[i]);
}
return 0;
}
结果:
写的时候出现的问题:在写维护堆的性质的时候关于左右孩子大小的比较问题,没有进行比较,而都将左右孩子的值和父节点的值进行比较,这样可能会导致维护堆没有起到作用,或者说维护堆不到位,导致不能最大堆并没有创建成功,最后的堆排序也没有实现。