首先堆排序要承认几个事实:
1. 数组已经给你,相当于一颗完全二叉树已经给出。只是这棵二叉树是以数组形式存储。
2. 结点之间的关系:结点i的孩子的下标是2*i+1和2*i+2(如果有右孩子的话)。
3. 完全二叉树的性质:一个非叶子结点只有一个左孩子或者有两个孩子。
4. 如果以最小堆建堆,最后的数组排序输出结果是倒序(从大到小)
5. 建堆的概念:不是一个一个结点插入堆中。而是一个堆已经给出,我们需要把它调整成最小堆或最大堆。
6. 完全二叉树的最后一个结点的父亲的下标为n/2-1.(数组以0开始,n为数组的长度。)
堆排序的思想:
1. 下标为i=n/2-1的元素开始向下调整,保证以此结点为跟结点的子树满足最小堆的性质。
2. 调整:如果父节点大于两孩子结点的某一个p,则父节点与p交换。然后沿着p往下走。
堆排序的性能分析:
建堆需要O(n)。之后进行n-1次向下调整操作,每次调整的时间为O(h)。由于是完全二叉树,所以时间复杂度始终为O(n*logn)。与
数组的初试状态无关。
1. 数组已经给你,相当于一颗完全二叉树已经给出。只是这棵二叉树是以数组形式存储。
2. 结点之间的关系:结点i的孩子的下标是2*i+1和2*i+2(如果有右孩子的话)。
3. 完全二叉树的性质:一个非叶子结点只有一个左孩子或者有两个孩子。
4. 如果以最小堆建堆,最后的数组排序输出结果是倒序(从大到小)
5. 建堆的概念:不是一个一个结点插入堆中。而是一个堆已经给出,我们需要把它调整成最小堆或最大堆。
6. 完全二叉树的最后一个结点的父亲的下标为n/2-1.(数组以0开始,n为数组的长度。)
堆排序的思想:
1. 下标为i=n/2-1的元素开始向下调整,保证以此结点为跟结点的子树满足最小堆的性质。
2. 调整:如果父节点大于两孩子结点的某一个p,则父节点与p交换。然后沿着p往下走。
3. i--;重复步骤1,直到下标为0.
堆排序的性能分析:
建堆需要O(n)。之后进行n-1次向下调整操作,每次调整的时间为O(h)。由于是完全二叉树,所以时间复杂度始终为O(n*logn)。与
数组的初试状态无关。
堆排序是不稳定排序。
void Swap(int &a, int &b){
int temp=a;
a=b;
b=temp;
}
void AdjustDown(int A[], int s,int n){//注意这里的n是数组的长度,不是数组最后一个元素的下标。
int min;
for(int i=s;(2*i+1)<n;){
if(2*i+2<n && A[2*i+1]>A[2*i+2])//沿较小的子节点向下调整
min=2*i+2;
else min=2*i+1;
if(A[i]<=A[min]) break;//调整结束
Swap(A[i],A[min]);
i=min;//沿较小的子节点向下走
}
}
void BuildHeap(int A[], int n)
{
for(int i=n/2-1;i>=0;i--)
AdjustDown(A,i,n);
}
void HeapSort(int A[], int n){
BuildHeap(A,n);
for(int i=n-1;i>0;i--){
Swap(A[0],A[i]);
AdjustDown(A,0,i);
}
}