数据结构——优先队列和堆

目录

1、优先队列

1.1、什么是优先队列

1.2、优先队列的实现

2、堆

2.1、什么是堆

2.2、堆的类型

2.3、二叉堆

2.3.1、堆的声明
2.3.2、创建堆
2.3.3、结点的双亲
2.3.4、结点的孩子
2.3.5、获取最大元素
2.3.6、堆化元素
2.3.7、删除元素
2.3.8、插入元素
2.3.9、清空堆
2.3.10、数组建堆
2.3.11、堆排序

正文

1、优先队列

1.1、什么是优先队列

  • 为了找到元素集合中的最小或最大元素,优先队列支持插入(Insert)删除最小值(DeleteMin)操作(返回并删除最小元素)或删除最大值(DeleteMax)操作(返回并删除最大元素)。如图1-1所示。


    图1-1 优先队列示例
  • 如果最小键值元素拥有最高的优先级,那么这种优先队列叫做升序优先队列(即总是删除最小的元素)。类似地,如果最大键值元素拥有最高的优先级,那么这种优先队列叫做降序优先队列(即总是删除最大的元素)。

1.2、优先队列的实现

  • 首先列出可其可能的实现方式。
    1)无序数组实现:将元素插入数组中,不考虑元素的顺序,则插入的时间复杂度为O(1),删除操作需要找到相应的键值,然后进行删除,则删除的时间复杂度为O(n)。
    2)无序链表实现:类似数组实现,插入的时间复杂度为O(1),删除的时间复杂度为O(n)。
    3)有序数组实现:元素按照键值排序的方式插入数组中,插入的时间复杂度为O(n),删除只在数组一端执行,则删除的时间复杂度为O(1)。
    4)有序链表实现:元素按照键值排序的方式插入链表中,与数组类似,插入的时间复杂度为O(n),删除只在数组一端执行,则删除的时间复杂度为O(1)。
    5)二叉搜索树实现:若随机插入元素,则插入和删除操作的平均时间复杂度为O(logn)。
    6)平衡二叉搜索树实现:插入和删除操作的平均时间复杂度为O(logn)。
    7)二叉堆实现:在第2节中会说明堆实现的细节。二叉堆实现搜索、插入和删除的时间复杂度均为O(logn),而找到最大或最小元素的时间复杂度均为O(1)。
  • 上述实现的比较如图1-2所示。


    图1-2 实现比较

2、堆

2.1、什么是堆

  • 堆是一棵具有特定性质的二叉树。基本要求是堆中所有结点的值必须大于等于(或者小于等于)其孩子结点的值。如图2-1所示。


    图2-1 堆

2.2、堆的类型

  • 最小堆:结点的值必须小于或等于其孩子结点的值。如图2-2所示。


    图2-2 最小堆
  • 最大堆:结点的值必须大于或等于其孩子结点的值。如图2-3所示。


    图2-3 最大堆

2.3、二叉堆

  • 在二叉堆中,每个结点最多有两个孩子结点,也就是在形式上是一棵完全二叉树,所以用数组来存储二叉堆不会浪费任何空间。假定所有元素都存储在数组中,从下标0开始。图2-3可以表示成如图2-4所示。


    图2-4 数组表示
2.3.1、堆的声明
/**
 * @Auther: lalala
 * @Date: 2018/7/15 15:08
 * @Description:二叉堆
 */
public class Heap {
    public int[] array;
    public int count;           //堆的元素个数
    public int capaticy;        //堆的大小
    public int heap_type;       //最小堆或最大堆
    public Heap(int capaticy,int heap_type){
        //具体实现如下
    }

    public int Parent(int i){
        //具体实现如下
        return -1;
    }

    public int LeftChild(int i){
        //具体实现如下
        return -1;
    }

    public int RightChild(int i){
        //具体实现如下
        return -1;
    }

    public int GetMaximum(){
        //具体实现如下
        return -1;
    }
}
2.3.2、创建堆
    public Heap(int capaticy,int heap_type){
        this.heap_type=heap_type;
        this.count=0;
        this.capaticy=capaticy;
        this.array=new int[capaticy];
    }
2.3.3、结点的双亲
  • 对于第i个位置上的结点,其双亲结点在(i-1)/2位置上。图2-3中,结点6在2号位置,其双亲结点在0号位置。
    public int Parent(int i){
        if(i<=0||i>this.count){
            return -1;
        }
        return (i-1)/2;
    }
2.3.4、结点的孩子
  • 第i个位置结点的孩子处在2i+1和2i+2位置上。如图2-3中,结点6在2号位置,它的孩子结点2和5,分别处在5和6号位置上。
    public int LeftChild(int i){
        int left=2*i+1;
        if(left>this.count){
            return -1;
        }else {
            return left;
        }
    }

    public int RightChild(int i){
        int right=2*i+2;
        if(right>this.count){
            return -1;
        }else {
            return right;
        }
    }
2.3.5、获取最大元素
  • 该例子为最大堆,所以最大元素位于根结点,也就是数组的0号位置。
    public int GetMaximum(){
        if(this.count==0){
            return -1;
        }else {
            return this.array[0];
        }
    }
2.3.6、堆化元素
  • 当插入一个元素到堆中时,它可能不满足堆的性质,这种情况下需要调整堆中的位置使之重新变为堆,这个过程叫做堆化。如图2-5所示,需要进行堆化。


    图2-5 示例
  • 为了堆化结点1,先找到它孩子结点中的最大值,然后交换它们的位置。如图2-6所示。


    图2-6 示例(2)
  • 持续这个过程,现在交换1和8。如图2-7所示。


    图2-7 示例(3)
  • 代码实现如下:
    public void PercolateDown(int i){
        int l,r,max,temp;
        l=LeftChild(i);
        r=RightChild(i);
        if(l!=-1&&this.array[l]>this.array[i]){
            max=l;
        }else {
            max=i;
        }
        if(r!=-1&&this.array[r]>this.array[max]){
            max=r;
        }
        if(max!=i){
            temp=this.array[i];
            this.array[i]=this.array[max];
            this.array[max]=temp;
        }
        PercolateDown(max);
    }
2.3.7、删除元素
  • 为了从堆中删除元素,只需从根结点删除元素。当删除根结点后,将堆中最后一个元素复制到这个位置,然后删除最后的元素。可能会导致不满足堆的性质,为了使其再次成为堆,需要调用上述函数PercolateDown。
    1)、将第一个元素复制到其他变量。
    2)、将最后一个元素复制到第一个元素位置。
    3)、PercolateDown(第一个元素)。
    public int DeleteMax(){
        if(this.count==0){
            return -1;
        }
        int data=this.array[0];
        this.array[0]=this.array[count-1];
        this.count--;
        PercolateDown(0);
        return data;
    }
2.3.8、插入元素
  • 类似堆化和删除过程
    1)、堆的大小加1。
    2)、将新元素放在堆的尾部。
    3)、从下置上堆化这个元素。
    public int Insert(int data){
        int i;
        if(this.count==this.capaticy){
            ResizeHeap();
        }
        this.count++;
        i=this.count-1;
        while (i>=0&&data>this.array[(i-1)/2]){
            this.array[i]=this.array[(i-1)/2];
            i=(i-1)/2;
        }
        this.array[i]=data;
        return i;
    }

    public void ResizeHeap(){
        int[] array_old=new int[this.capaticy];
        System.arraycopy(this.array,0,array_old,0,this.count-1);
        this.array=new int[this.capaticy*2];
        if(this.array==null){
            return;
        }
        for(int i=0;i<this.capaticy;i++){
            this.array[i]=array_old[i];
        }
        this.capaticy*=2;
        array_old=null;
    }
2.3.9、清空堆
    public void DestroyHeap(){
        this.count=0;
        this.array=null;
    }
2.3.10、数组建堆
    public void BuildHeap(Heap h,int[] A,int n ){
        if(h==null){
            return;
        }
        while (n>h.capaticy){
            h.ResizeHeap();
        }
        for(int i=0;i<n;i++){
            h.array[i]=A[i];
        }
        h.count=n;
        for(int i=(n-1)/2;i>=0;i--){
            h.PercolateDown(i);
        }
    }
2.3.11、堆排序
    public void Heapsort(int[] A,int n){
        Heap h=new Heap(n,0);
        int old_size,i,temp;
        BuildHeap(h,A,n);
        old_size=h.count;
        for(i=n-1;i>0;i--){
            //h.array[0]存储最大元素
            temp=h.array[0];
            h.array[0]=h.array[h.count-1];
            h.count--;
            h.PercolateDown(i);
        }
        h.count=old_size;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容