您的位置:首页 > 编程语言 > C语言/C++

简单易懂的讲堆排序

2017-09-09 09:24 176 查看
堆排序利用二叉堆来进行排序,而二叉堆用完全二叉树代替(因为易于储存和索引),一个数组就可以存储完全二叉树。(๑╹◡╹)ノ"""

但堆并不一定是完全二叉树,堆还有更复杂的,就不一一列举;

怎么用数组存储呢?对于每一个节点i,其父节点为i/2,其左右子节点分别为i*2,i*2+1;如图:



怎么进行排序呢,我们分为两个大步骤:

1,构建一个最大堆(每一个父节点都大于其子节点)

对于每一个非叶子节点(有叶子的节点),如果其小于子节点,那么就与子节点中较大的进行交换;

对于一个节点数为n的二叉堆来说,最后一个非叶子节点为n/2;

#include<bits/stdc++.h>
using namespace std;
void sift(int *r,int low,int high){
int i=low,j=2*low;
int tmp=r[low];
while(j<=high){//如果存在左子节点 j代表左节点
if(j<high&&r[j]<r[j+1])j+=1;//如果存在右节点且右节点大于左节点  j+=1 j代表右节点
if(tmp<r[j]){        //如果较大的节点大于父节点就交换
r[i]=r[j];      //交换
i=j;
j*=2;   //继续判断子节点的子节点
}
else break;     //小于就终止
}
r[i]=tmp;
}
int main(){
int a[7]={0,5,2,4,6,1,3};//从1开始为有效,即树根编号为1;
for(int i=6/2;i>=1;i--){
sift(a,i,6);
}
for(int i=1;i<=6;i++)cout<<a[i];
return 0;
}
输出为654213即如图:



所有的父节点都大于其子节点,最大堆构建完成;

2,从最后一个元素开始到第一个元素进行如下操作:

(1)交换当前元素与第一个元素(根据最大堆性质,第一个元素最大);

(2)然后忽视被交换到后面的元素并重构最大堆

(1)最后一个元素编号为6,值为3与第一个元素进行交换:



(2)交换一次后可以看到6已经到达了位置,即6已经排好了序;

排除6重新构建最大堆:



(1)交换倒数第二个元素和第一个元素:

第二个元素到达位置,即5,6已经排好序

(2)忽略5,6并重构最大堆。。。。。。。。

这样一直到排好序,即每一次都有一个元素排好序,每次找堆顶元素(除去已排好序的元素中最大的数)并将其放到其应该待的位置;

完整代码:

#include<bits/stdc++.h>
using namespace std;
void sift(int *r,int low,int high){
int i=low,j=2*low;
int tmp=r[low];
while(j<=high){//如果存在左子节点 j代表左节点
if(j<high&&r[j]<r[j+1])j+=1;//如果存在右节点且右节点大于左节点  j+=1 j代表右节点
if(tmp<r[j]){        //如果较大的节点大于父节点就交换
r[i]=r[j];      //交换
i=j;
j*=2;   //继续判断子节点的子节点
}
else break;     //小于就终止
}
r[i]=tmp;
}
void HeapSort(int *r,int n){
int i;
for(i=n/2;i>=1;i--){       //构建初始最大堆
sift(r,i,n);
}

for(i=n;i>=2;i--){         //进行排序的过程
swap(r[1],r[i]);//交换未排好序中最大的数到达其排好序后应在的位置;
sift(r,1,i-1);//  思考一下为什么是i-1,文章最后给出解答
}
}
int main(){
int a[7]={0,5,2,4,6,1,3};
HeapSort(a,6);
for(int i=1;i<=6;i++)cout<<a[i];
return 0;
}
彩蛋:i-1即排除已经排好序的那些在末尾的大元素们,在未排序的序列中进行最大堆的重构( ̄▽ ̄)~*
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息