您的位置:首页 > 理论基础 > 数据结构算法

数据结构-选择排序-堆排序

2017-04-11 16:41 369 查看
选择排序

①简单选择排序。

②树形选择排序。

③堆排序。←this

堆排序(Heap Sort)只需要一个记录大小的辅助空间,每个待排序的记录仅占有一个存储空间。
堆定义:满足ki<=k2;且ki<=k(2i+1) (i=1,2,...,n/2)完全二叉树且父比子大(或父比子小)
堆排序方法对记录较少的文件不值得提倡,对n较大的文件还是很有效的。因为其运行时间主要耗费在建初始堆,和调整新堆
最大优点是,堆排序的最坏情况下,时间复杂度也是O(nlogn)。
首先介绍堆运算:

Sift-Up

简单来说就是二叉树的结点(样例用叶子)突然变大,需要重新对堆进行排列。从下往上传递(交换)更大的值
            20                                                         20                                                      25
        /          \                                                 /         \                                                /         \
    17              9             5变成25                17           9               25向上  
           20            9
    /   \           /    \           ---------->              /   \          /    \           ---------->           /   \      
   /    \  
 10    11      4      5                                10      11    4     5                              10      17  
 4     5
 /  \    /                                                 /  \        /                                             /  \        /
3  7  5                                               3    7    25     
                                    3    7    11
输入:数组H[ 1...n ] 和位于1和n之间的索引 i
输出:上移H[ i ](如果需要),使他不大于父节点。

//            堆a,根i
void sift_up(int *a,int i){
bool done=false;
if(i==1) return ;               //总根
while(i!=1 && !done){
if( a[i] > a[i/2] )         //子比父大,换
swap(a[i],a[i/2]);
else done = true;
i=i/2;
}
}


②Sift-Down
简单来说就是二叉树的某根突然变小,需要重新对堆进行排列,从上往下传递(交换)更小的值

            20                                                         20                                                    20
        /          \                                                 /         \                                              /         \
    17              9             17变成3                 3            9                3往下
              11           9
    /   \           /    \           ---------->              /   \          /    \           ---------->           /   \          /    \  
 10    11      4      5                                10      11    4     5                              10       5     4       5
 /  \    /                                                 /  \        /                                             /  \       /
3  7  5                                               3    7    5                                            3    7    3

输入:数组H[ 1...n ] 和位于1和n之
4000
间的索引i
输出:下移H[ i ](如果需要),以使它不小于子节点。
//             堆a,子根i ,总n
void sift_down(int *a,int i,int n){
bool done=false;
if(2*i > n) return ;            //根i是叶子
while(2*i<=n && !done){
i=2*i;                      //根i叶子
if(i+1<=n && a[i+1]>a[i])   //根i两个叶子里选个大的
i=i+1;
if(a[i/2] < a[i])           // 父比子大,换
swap(a[i],a[i/2]);
else done = true;
}
}


插入数值
在树的最后添上一个数,然后执行Sift-Up,从下到上重新排列一下,保持堆的特性

            20                                                         20                                                       20
        /          \                                                 /         \                                               /          \
    17              9              插入18                 17            9              Sift-Up              18 
            9
    /   \           /    \           ---------->              /   \          /    \           ---------->           /     \           /    \  
 10    11      4      5                                10      11    4     5                              10       17      4       5
 /  \    /                                                 /  \        /  \                                           /  \      /    \
3  7  5                                               3    7    5    18                                    3    7
  5     11

输入:数组H[ 1...n ] 和元素x。
输出:新的堆H[ 1...n+1 ],x为其元素之一。
//             堆a,数x,总n
void insert(int *a,int x,int &n){
n=n+1;
a
=x;
sift_up(a,n);
}



删除数值

从堆里删除元素,先将最后一个叶子跟要删除的数值交换,
如果最后一个叶子比较大那么Sift-Up,从下到上重新排列一下

如果最后一个叶子比较小那么Sift-Down,从上到下重新排列一下

            20                                                         20                                                    20                                               20
        /          \                要删除10                  /          \                                              /         \                                     
 /         \
    17              9           10和5交换              17            9              删除10              17           9 
         Sift-Down       17           9
    /   \           /    \           ---------->              /   \          /    \           ---------->           /   \          /    \         ---------->       /   \  
       /    \    
 10    11      4      5                                5      11    4     5                                
5       11     4       5                       7       11    
4       5
 /  \    /                                                 /  \        /                                             /  \                                               /    \       
3  7  5                                               3    7    10            
                             3    7                                           3      5

输入:非空堆H[ 1...n ] 和位于1和n之间的索引i
输出:删除H[ i ]之后的新堆H[ 1...n-1]
//           堆a,下标i,总n
void delet(int *a,int i,int &n){
int x=a[i],y=a
;a
=0;
n=n-1;
if(i == n+1)                 //删的是最后一个点,那已经删掉了
return ;
a[i]=y;                      //用最后一个叶子覆盖这个结点
if(y>=x) sift_up(a,i);       //换上的比原来大,上浮
else sift_down(a,i,n);       //小,下沉
}


⑤创建堆

从叶子的上一个根到整个的根节点,每一次都将小的数沉下来
★sift-down是根向叶子的调整,从叶子开始毫无意义,所以直接跳过叶子部分
//         数组a,长度n
void build(int *a,int n){
for(int i=n/2;i>=1;i--){     //从一半开始到根节点
sift_down(a,i,n);
}
}


⑥堆排序
因为每次堆顶端都是最大值,那么,把最后的那个换成堆顶端,那么最大值就在最后面了。
依次把当前最大值放到后面,那么就可以排出从小到大的顺序了。
所要用到的步骤是:⑤创建堆+②Sift-Down

            20                                                        5                     
                              17
        /          \                                                 /         \                  无视20              /         \
   17             9             20交换5    
           17            9         从顶sift-down     
11           9        
 17交换7 
    /   \           /    \           ---------->              /   \          /    \           ---------->           /   \          /    \   
  ---------->  ...省略...
 10    11      4      5                                10      11    4     5                              10      5  
  4       5
 /  \    /                                                 /  \        /                                             /  \       /
3  7   5 
                                           3    7   20                                          3   7 
20

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N=30;
void print(int *tree){
int altm=0;
for(int j=1;j<5;j++){
int tm=1;
for(int i=1;i<j;i++)
tm*=2;
for(int i=1;i<=tm;++i)
{
printf("%d ",tree[altm+i]);
}
printf("\n");altm+=tm;
}
}
// 堆a,子根i ,总n
void sift_down(int *a,int
d18f
i,int n){
bool done=false;
if(2*i > n) return ; //根i是叶子
while(2*i<=n && !done){
i=2*i; //根i叶子
if(i+1<=n && a[i+1]>a[i]) //根i两个叶子里选个大的
i=i+1;
if(a[i/2] < a[i]) // 父比子大,换
swap(a[i],a[i/2]);
else done = true;
}
}
// 数组a,长度n void build(int *a,int n){ for(int i=n/2;i>=1;i--){ //从一半开始到根节点 sift_down(a,i,n); } }
void heapSort(int *a,int n){
build(a,n);
for(int j=n;j>=2;j--){
swap(a[1],a[j]);
sift_down(a,1,j-1);
}
}
int main()
{
int a
={0,3,7,5,10,5,4,9,20,17,11};
int len=10;
print(a);
//build(a,len);print(a);printf("---%d\n",len);
//insert(a,13,len);print(a);printf("---%d\n",len);
//delet(a,5,len);print(a); printf("---%d\n",len);
heapSort(a,len);
print(a);
for(int i=1;i<=len;i++){
printf("%d ",a[i]);
}printf("\n");
return 0;
}

那么问题来了?从大到小排序怎么弄呢?
把sift_down()里的第七行和第九行改一下方向就行了,每次堆顶都是最小值->最小堆

堆排序到此结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  堆排序
相关文章推荐