【堆】堆的定义以及堆排序

堆的定义:

堆是一类特殊的数据结构统称,堆通常可以被看做一颗树的数组对象。

堆满足下列性质:

1)堆中某个节点的值总是不大于或不小于其父节点的值

2)堆总是一颗完全二叉树。

完全二叉树:除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。

满二叉树:树中除了叶子节点,每个节点都有两个子节点

完美二叉树:满足完全二叉树性质,树的叶子节点均在最后一层(也就是形成了一个完美的三角形)

堆的定义:n个元素的序列{k1,k2,ki,...,kn}当且仅当满足以下关系时,称之为堆。

(ki<=k2i,ki<=k2i+1)或者(ki>=k2i,ki>=k2i+1),(i=1,2,3,..,n/2)

完全二叉树中所有非终端点的值均不大于(或不小于)其左右孩子节点的值。由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。

根据上述知道堆的性质,分为大顶堆,和小顶堆。即堆顶元素子序列n个元素中是最大值或最小值。堆排序就是根据这个性质进行排序

堆排序

步骤:

1. 将输入的序列{R1,R2,R3, ... , Rn}初始化为大顶堆

2. 将堆顶元素R[1]与最后一个元素R[n]交换,此时新的无序区(R1,R2,...,Rn-1)和有序区(Rn)

3. 将交换后的新的无序区,调整为新的大顶堆。再次将R1与最后一个元素交换,得到新的无序区(R1,R2,R3,...,Rn-2)和有序区(Rn-1,Rn)

4. 不断重复2, 3过程,直到有序区个元素个数为n-1,则排序完成。

注意:在第1步初始化大顶堆和第3步中再次调整大顶堆,对序列非叶结点遍历顺序不同。第1步从最后有叶子结点往上调整,第3步中调整最大堆,是从最上面结点开始往下调整。

代码:

#include <iostream>

using namespace std;

/****
*堆排序
*首先讲待排序列初始化为大顶堆(这里要理解堆的性质,堆顶元素是最大值)
*将堆顶元素R[1]与最后一个元素R[n]交换,此时新的无序区(R1,R2,...,Rn-1)和有序区(Rn)
*将交换后的新的无序区,调整为新的大顶堆。再次将R1与最后一个元素交换,得到新的无序区(R1,R2,R3,...,Rn-2)和有序区(Rn-1,Rn)
*不断重复此过程,直到有序区个元素个数为n-1,则排序完成
****/

void sift_down(int *a, int start, int last){

    while(1){   //对交换父节点和子节点,再次检查子节点是否是子树中最大值
        /*
        *当列表第一个是以下标0开始,结点下标为i,左孩子则为2*i+1,右孩子下标则为2*i+2;
        *若下标以1开始,左孩子则为2*i,右孩子则为2*i+1
        */
        int left = 2*start+1;  //左孩子的结点下标
        if(left>last){
            break;
        }
        if(left+1 <= last && a[left+1] > a[left]){  // # 当结点的右孩子存在,且大于结点的左孩子时
            left += 1;
        }
        if(a[left] > a[start]){
            int temp = a[left];
            a[left] = a[start];
            a[start] = temp;

            start = left;   // # 交换之后以交换子结点为根的堆可能不是大顶堆,需重新调整
        }else{   //# 若父结点大于左右孩子,则退出循环
            break;
        }
        cout<< ">>";
        for(int i=0;i<=last;i++){
            cout<<a[i]<<" ";
        }
        cout<<endl;
    }
}

int main()
{
    int n;
    cin>>n;
    int a[n];
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    for(int i=n/2 - 1;i>=0;i--){   //n/2-1 表示 最后一个有孩子的节点, # 从最后一个有孩子的节点开始往上调整
        cout<<a[i]<<endl;
        sift_down(a,i,n-1);//初始化大顶堆
    }  //上述代码初始化输入序列为大顶堆

    for(int head = n-1;head>0;head--){  //此for循环,开始逐个交换堆顶元素和堆顶元素,
//交换后调用sift_down函数,重新调整为大顶堆
        int team = a[head];
        a[head] = a[0];
        a[0] = team;

        sift_down(a,0,head-1);  //从堆顶的节点节点开始往下调整,一次调整得到大顶堆
    }

    for(int i=0;i<=n-1;i++){
            cout<<a[i]<<" ";
        }
        cout<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值