堆排序
1、介绍
堆排序是对直接选择排序法的一种改进。从前面的讨论中可以看到,采用直接选择排序时,为了从 n 个关键字中找出最小的关键字需要进行 n-1 次比较,然后再在余下的 n-2 个关键字中找出次小关键字,需要进行 n-2 次比较。事实上,在查找次小关键字的 n-2 次比较中,有许多比较可能在之前的 n-1 次比较中已经做过,只是当时没有把结果保存下来,因此,在后续的排序中又重复进行了这些比较。树形排序可以克服这一点。
堆排序是一种树形排序,它的基本思想是:将记录数组 R[1......n] 看成一颗完全二叉树的顺序存储结构,利用完全二叉树的双亲节点和孩子节点之间的内在关系,在当前无序区中选择关键字最大的记录。
堆的定义如下: n 个记录的关键字序列 k1,k2,k3,......,kn 称为堆,当且仅当满足一下关系:
k[i] <= k[2i] 且 k[i] <= k[2i + 1] ,或 k[i] >= k[2i] 且 k[i] >= k[2i + 1] (1 <= i <= n/2)
前者称为 小根堆 ,后者称为 大根堆 。
堆排序正是利用大根堆来选取当前无序区中关键字最大的记录实现排序的。每一趟排序的操作是:将当前无序区调整为一个大根堆,选取关键字最大的堆顶记录,将它和当前无序区中的最后一个记录交换,这正好与选择排序相反。堆排序就是一个不断建堆的过程。
因此,堆排序的关键就是如何构建堆。其具体做法是:把待排序的文件的关键字存放在数组 R[1......n] 中,将 R 看做一颗完全二叉树的存储结构,每个节点代表一个记录,源文件的第一个记录 R[1] 作为二叉树的根,以下各记录 R[2......n] 依次逐层从左到右顺序排列,构成一颗完全二叉树,任意节点 R[i] 的左孩子是 R[2i] , 右孩子是 R[2i+1] ,双亲节点是 R[i/2] 。
实现算法如下:
//建立大根堆
void Sift(int List[],int i,int h)
{
int j = i*2;//i的右孩子
List[0] = List[i];
while(j<=h)
{
if(j<h && List[j]<List[j+1])
{
j = j+1;
}
if(List[0]>List[j])
{
break;
}
List[i] = List[j];
i = j;
j = 2*i;
}
List[i] = List[0];
}
//堆排序
void HeapSort(int List[],int n)
{
int i = n/2;
int index = 1;
for(;i>=1;i--) //建立初始大根堆
{
Sift(List,i,n);
display("init heap",index,List,n);
index++;
}
index = 1;
for(i=n;i>1;i--)
{
List[0] = List[i];
List[i] = List[1];
List[1] = List[0];
Sift(List,1,i-1);
display("Heap Sort",index,List,n);
index++;
}
}
浙公网安备 33010602011771号