堆排序,优先队列,排序基础

堆排序虽然在性能效率上不及快速排序,但由于优先队列的使用十分广泛,所以堆排序依然是基础中比较重要的部分。

堆的实质其实是一个数组,不过其逻辑结构则是以一颗近似完全二叉树的形式存在的,如图

堆(最大堆)的性质:

1.父结点一定大于子节点(最小堆相反)

2.以每个子结点为根结点的树也是堆

由于近似完全二叉树的性质,还可得出如下性质(数组下标以1开头):

1.叶子结点的数组下标为((n/2+1)到n)

2.一个结点的左子结点点的下标为2i,右子结点的下标为2i+1

一.  堆性质维护(即堆在改变的情况,性质不变):

一般人往往会陷入误区,因为人思维的影响往往会一直思考如何建立一个堆,而不是首先思考如何维护一个堆。但是细想会明白建堆的过程就是在不断地改变堆,所以必要先知道如何去维护堆的性质。

思考:根据上述堆的性质,每个结点必须要大于其子结点,如果不大于怎么办?当然是让小的下去,大的上来,现在用伪代码模拟以i结点为子节点的堆维护过程。

伪代码:

PROTECT(A,i)

1.L=i的左子节点下标,R=i右子节点下标,max = i;

2.if(L<=size&&A[L]>A[i])

  max = L

3.if(R<=size&&A[R]>A[max])

  max = R

4.if(max!=i)

  exchange(max,i)

  PROTECT(A,max)

想必大家都知道1-3步的原因,而第4步则是源自堆的父节点一定大于子节点的性质,试想如果max=i那么i已经比它的两个子节点要大,自然就比其子节点的子节点要大,所以该堆已经满足性质,无需再下顺。

而若max!=i则i有可能小于其原子节点的子节点(i并非最大),需继续下顺,由于i与max结点值互换,所以此时max代表的才是i,max 需要继续下顺

 

代码:

//维护堆的性质
// a 表示堆,i表示当前节点下标 , n表示堆数组的实际长度 
void protect(int *a,int i,int n){
    int max = i;
    int l = i*2;
    int r = i*2+1;
    if(l<=n&&a[l]>a[i]){
        max=l;
    }
    if(r<=n&&a[r]>a[max]){
        max=r;
    }
    if(max!=i){
        int temp;
        temp=a[max];a[max]=a[i];a[i]=temp;
        protect(a,max,n);
    }
}

二.  建堆:

既然维护堆性质的方案已经得出,那么建堆其实就是从以最后一个非叶子节点(n/2)为根节点开始维护堆的性质,一直维护到根节点

 

代码:

//建堆
void build(int *a,int n){
    for(int i=n/2;i>=1;i--){
        protect(a,i,n);
    }
}

 三.  堆排序:

思考:经过对堆性质的了解,堆排序无非就是将堆的根节点(最大值)取出放在末端,然后维护堆性质,不断循环直到堆中无结点

整体流程:

1.将顶部结点与数组末端进行互换,并取出(为了将大值放到数组后面)

2.维护堆的性质

3.循环至无结点

代码:

//堆排序 
void sort(int *a,int n){
    for(int i=N;i>=2;i--){
        int temp=a[i];
        a[i]=a[1];
        a[1]=temp;
        n--;
        protect(a,1,n);
    }
}

整体示例代码:

/*
堆的建立,维护,插入,取出 
*/
#include<iostream>
using namespace std;
#define N 10

//维护堆的性质
// a 表示堆,i表示当前节点下标 , n表示堆数组的实际长度 
void protect(int *a,int i,int n){
    int max = i;
    int l = i*2;
    int r = i*2+1;
    if(l<=n&&a[l]>a[i]){
        max=l;
    }
    if(r<=n&&a[r]>a[max]){
        max=r;
    }
    if(max!=i){
        int temp;
        temp=a[max];a[max]=a[i];a[i]=temp;
        protect(a,max,n);
    }
}

//建堆
void build(int *a,int n){
    for(int i=n/2;i>=1;i--){
        protect(a,i,n);
    }
}

//取出堆顶
int getTop(int *a,int &n){
    int result = a[1];
    a[1]=a[n];a[n]=0;n--;
    protect(a,1,n);
    return result; 
}

//堆排序 
void sort(int *a,int n){
    build(a,n);
    for(int i=N;i>=2;i--){
        int temp=a[i];
        a[i]=a[1];
        a[1]=temp;
        n--;
        protect(a,1,n);
    }
}

int main(){
    int a[N+1];
    for(int i=1;i<=N;i++){
        cin>>a[i];
    }
    int n=10;
    sort(a,n);
    for(int i=1;i<=N;i++){
        cout<<a[i]<<"    ";
    }
    return 0;
} 

 优先队列代码:

 

/*
优先队列 
*/
#include<iostream>
using namespace std;

//维护优先队列性质 
void protect(int *a,int i,int n){
    int left = 2*i;
    int right = 2*i+1;
    int max = i;
    if(a[left]>a[i]&&left<=n){
        max = left;
    }
    if(a[right]>a[max]&&right<=n){
        max = right;
    }
    if(i!=max){
        int temp = a[max];
        a[max] = a[i];
        a[i] = temp;
        protect(a,max,n); 
    }
}

//建立优先队列
void build(int *a,int n){
    for(int i=n/2;i>=1;i--){
        protect(a,i,n);
    }
}

//取出优先队列头结点
int getTop(int *a,int &n){
    int top = a[1];
    a[1]=a[n];
    n--;
    protect(a,1,n);
    return top;
}

//替换优先列表中的结点 
void replace(int *a,int i,int k){
    if(a[i]>k){
        throw "replace k < node";
    }
    a[i]=k;
    while(i>1&&a[i/2]<a[i]){
        int temp = a[i];
        a[i] = a[i/2];
        a[i/2] = temp;
        i=i/2;
    }
}

void insert(int *a,int k,int &n){
    n++;
    a[n] = 0;
    replace(a,n,k);
}

void sort(int *a,int n){
    build(a,n);
    for(int i=n;i>=1;i--){
        int temp = a[i];
        a[i] = a[1];
        a[1] = temp;
        n--;
        protect(a,1,n);
    }
}

int main(){
    int a[100];
    int n = 10;
    cout<<"输入该数组的10个值:";
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    sort(a,n);
    cout<<"堆排序后:";
    for(int i=1;i<=n;i++){
        cout<<a[i]<<" ";
    }
    cout<<"是否要进行插入:1.是,2.否";
    int select;
    cin>>select;
    if(select == 1){
        int num;
        cout<<"输入要插入的值:";
        cin>>num;
        insert(a,num,n);
        sort(a,n);
        for(int i=1;i<=n;i++){
            cout<<a[i]<<" ";
        }
    }
    return 0;
}

 

    

posted on 2017-08-18 16:00  T~Z  阅读(476)  评论(0编辑  收藏  举报