zoukankan      html  css  js  c++  java
  • 由堆到堆排序

    堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:

    1. 堆总是一棵完全二叉树。
    2. 堆中某个节点的值总是不大于或不小于其父节点的值;

    将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆 等。
    这里主要介绍二叉堆

    二叉堆的储存

    储存图
    上面是逻辑上结构, 下面则是实际的储存结构,我将数组和树的都从1开始编号。
    以下标pos为5,即值为4的点为例
    pos的父亲节点为 pos>>1 = 2, 值为3, 左儿子节点为 pos<<1 = 10 值为 10, 右儿子节点为 pos<<1|1 = 11 值为 9
    这图就是一个小根堆,大根堆也就容易理解了

    栗子

    题目链接

    题目很简单,用优先队列就能过。优先队列本质还是用堆实现的。
    那我们还是用堆来实现。
    根据题目要求,我们使用小根堆显然比较合适些;

    //操作1 x,就将x放入数组的末尾
    /*
    dui[]数组用于储存堆元素
    x 是要插入的元素
    因为是向上更新的,所以函数名为 up
    */
    void up(int x){
    	dui[++len] = x;
    	int pos = len;
        while(pos > 1){	//位置是1的话, 即根节点,就没必要更新了
            int fr = pos>>1;	//父亲节点的位置
            if(dui[pos] < dui[fr])		//要是小于父亲节点,就交换
                swap(dui[pos], dui[fr]);
            else
                break;
            pos>>=1;//交换后位置更新
        }
    }
    /*
    这样,就能保证每次插入新的元素之后,dui[1] 是堆中的最小值
    */
    
    操作 2
    就直接输出 dui[1] 就好了
    
    //操作3 移除最小的元素
    /*
    如果只是单纯的移除掉根节点,肯定是不行的
    所以,我们就把 dui[] 数组的最后一个元素覆盖掉 根节点 ,同时还要对堆进行维护, 使之依然是 小根堆
    */
    void down(){
        int pos = 1;
        dui[1] = dui[len--]; //覆盖
        while(pos<<1  <=  len){	//保证有左子节点
            int l = pos<<1, r = pos<<1|1, minn = l;	//minn 用于记录左右子节点中最小的
            if(r<=len && dui[r] < dui[l])	//在右子节点存在的情况下, 求出左右子节点中最小的
                minn++;
            if(dui[pos] > dui[minn]){	//更新维护
                swap(dui[pos], dui[minn]);
                pos = minn;
            }
            else
                break;
        }
    }
    
    
    本来想上图的,不过自己画的真的丑,自己随便模拟下就可以了
    

    这样这个题目就能很轻松了水过去了

    堆排序

    堆排序就简单了啊,就是上面两个函数的结合啊。
    上面的 操作1 就相当于 输入元素。
    操作3 就不是覆盖了,是将 根节点 和 最后一个节 点交换了。

    #include <bits/stdc++.h>
    const int maxn = 100005;
    using namespace std;
    typedef long long ll;
    int dui[maxn], len;	// len 堆的实际长度
    
    
    void up(int pos){
        while(pos > 1){
            int fr = pos>>1;
            if(dui[pos] > dui[fr])
                swap(dui[pos], dui[fr]);
            else
                break;
            pos>>=1;
        }
    }
    
    void down(int pos){
        while(pos<<1 <= len){
            int l = pos<<1, r = pos<<1|1, minn = l;
            if(r<=len && dui[r]>dui[l])
                minn++;
            if(dui[pos] < dui[minn]){
                swap(dui[pos], dui[minn]);
                pos = minn;
            }
            else
                break;
        }
    }
    
    int main()
    {
        int n;		//要排序的数组的长度
        scanf("%d", &n);
        len = n;
        for(int i=1; i<=n; i++){
            scanf("%d", &dui[i]);
            up(i);	//每次输入即插入, 要更新
        }
        for(int i=1; i<n; i++){
            swap(dui[1], dui[len]);		// 根节点 和 最后一个节 点交换
            len--;
            down(1);		//更新
        }
        for(int i=1; i<=n; i++)
            printf("%d ", dui[i]);
        return 0;
    }
    

    上面是利用小根堆, 将数组按降序排列
    升序怎么办??
    数组倒着输出就好了啊

    在这里插入图片描述

    还有大根堆啊

  • 相关阅读:
    【文本编辑】Raspberry B+上手教程--自己梳理,亲测有效
    【超级用户】Raspberry B+上手教程--自己梳理,亲测有效
    【连接网络】Raspberry B+上手教程--自己梳理,亲测有效
    【连接PC(路由)】Raspberry B+上手教程--自己梳理,亲测有效
    JavaScript的继承
    javascript的面向对象程序设计(二)
    javascript的面向对象程序设计(一)
    变量
    抽象工厂模式
    在jsp中应用MVC模型
  • 原文地址:https://www.cnblogs.com/jizhihong/p/13337369.html
Copyright © 2011-2022 走看看