• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-159  評論-223  文章-30  trackbacks-0
            前言
               眾所周知,stl中的優先級隊列是基于最大堆實現的,能夠在對數時間內插入元素和獲取優先級最高的元素,但如果要求在對數時間內還能獲取優先級最低的元素,而不只是獲取優先級最高的元素,該怎么實現呢?可以用最大堆-最小堆或雙端堆數據結構來實現,最大堆-最小堆和雙端堆都是支持雙端優先隊列的插入、刪除最小元素和最大元素等操作的堆,在這些操作上,時間復雜度都是對數時間,但是雙端堆的操作比最大堆-最小堆的相應操作還要快一個常數因子,而且算法更加簡單,因此本文講述選擇使用雙端堆實現優先級隊列的原理。

            定義與性質
               雙端堆是一顆完全二叉樹,該完全二叉樹要么為空,要么滿足以下性質:
             (1)根結點不包含元素
             (2)左子樹是一個最小堆
             (3)右子樹是一個最大堆
             (4)如果右子樹不為空,則令i是左子樹中任一結點,j是i在右子樹中的對應結點,如果i在右子樹中的對應結點不存在,則j為i的父結點在右子樹中的對應結點,  對于結點i和j,i的關鍵字值小于等于j的關鍵字值。
               從以上性質可以推出:對于左子結中的任一結點i,設j為i的對稱結點,則由最小元素到i,i到j,j到最大元素的路徑上元素是非遞減有序的。在雙端堆上的操作算法核心就是為了保證這樣的單向路徑上元素必須是非遞減有序的。
               例如下圖所示,就是一個雙端堆
            操作算法
             (1)插入元素:設所插結點為i,其對稱結點為j,要分2種情況 a)當i處于最小堆時,則j處于最大堆中,如果KeyValue(i)>KeyValue(j),則設置value= KeyValue(i),KeyValue(i)=KeyValue(j),并執行在最大堆中位置j處插入值value的操作;否則執行在最小堆中位置i處插入值KeyValue(i)的操作。b)當i處于最大堆時,則j處于最小堆中,如果KeyValue(i)<KeyValue(j),則設置value=KeyValue(i),KeyValue(i)=KeyVaue(j),并執行在最小堆中位置i處插入值value的操作;否則執行在最大堆中位置j處插入值KeyValue(i)的操作。

             (2)刪除最小元素:首先在最小堆中執行一個向下回溯過程,這個過程執行的堆大小要比原來的堆小1,從最小元素結點開始,每次選取關鍵字值較小的孩子結點,并用其值更新父結點值,直到底部沒有孩子結點,執行的結果是在底部某葉子結點i處形成一個空洞(形象說法,這個空洞需要后續操作來調整填補,下同),i的對稱結點j在最大堆中,設最末元素結點為e,如果KeyValue(e)>KeyValue(j),則設置KeyValue(i)=KeyValue(j),并執行在最大堆中位置j處插入值KeyValue(e)的操作;否則執行在最小堆中位置i處插入值KeyValue(e)的操作。

             (3)刪除最大元素:這個操作比刪除最小元素要麻煩一點,和刪除最小元素類似,先執行一個向下回溯過程得到空洞結點i,其對稱結點為j,為了保證能滿足雙端堆的性質,需要考慮以下幾種情況:a)j只存在一個孩子,如下圖第1個所示。 b)j存在兩個孩子,如下圖第2個所示。 c)j不存在孩子,但存在左兄弟(可能存在孩子),如下圖第3個所示。 d)j不存在孩子,但存在右兄弟,如下圖最后一個所示。
            令min為具有較小值的結點,max為具有較大值的結點,最末元素結點為e,value=KeyValue(e),如果j存在孩子結點,則 min為孩子中關鍵字值較小的結點,max為關鍵字值較大的結點;否則min為j和其兄弟結點中關鍵字值較小的結點,max為關鍵字值較大的結點。如果KeyValue(min)<value而且value<KeyValue(max),在這種情況下,只需調整i或其父結點、max的值即可,操作如下:如果KeyValue(i)<KeyValue(max),則設置KeyValue(parent(i))=KeyValue(max),否則設置KeyValue(i)=KeyValue(max),最后設置KeyValue(max)=value;如果KeyValue(max)<=value,在這種情況下,則執行在最大堆中位置i處插入值value的操作;如果value<=KeyVlaue(min),在這種情況下,先調整i或其父結點、max的值(見上),再執行在最小堆中位置min處插入值value的操作。

             (4)構造堆:給定一個元素大小為N的序列S,在這個序列空間上構造堆,一般有兩種實現方法,一是循環N-1次,每次插入元素S[i],也就是自頂向下構建堆,時間復雜度為O(NlgN)。另一種是從最后一個內部結點N/2左右開始,執行調整堆操作,直到第一個元素,也就是自底向上構建堆,時間復雜度為O(N)。

             (5)最大堆(最小堆)插入:這是一個向上回溯過程,和單個最大堆(最小堆)操作是一樣的,從底部某處開始,比較待插元素和結點關鍵字值大小,直到前者不大于(不小于)后者時或碰到最大堆(最小堆)頂部為止,這時就找到了插入位置,將待插元素放到這個位置即可。

               設雙端堆元素個數為N,由于完全二叉樹高度為O(lgN),則插入元素操作時間復雜度為O(lgN),刪除最小元素和最大元素為不超過2O(lgN),實現這些操作最重要的一點就是要保證性質4,只有當性質4滿足時,雙端堆才有意義,才能保證獲取最大元素和最小元素操作的對數時間,具體的實現詳見下文。
            posted on 2011-10-03 17:53 春秋十二月 閱讀(3795) 評論(3)  編輯 收藏 引用 所屬分類: Algorithm

            評論:
            # re: 基于雙端堆實現的優先級隊列(2): 內幕 2014-01-05 10:06 | Cppowboy
            你好,我學習了您講解的算法之后自己按照算法寫了C++的實現,包括插入,刪除最小,刪除最大的操作,但是在線評測總是出問題,個人檢查認為是對的,您能幫忙指點一下,看看哪里錯了嗎?
            #include <stdio.h>
            #include <cstring>
            #include <cmath>
            #include <stdlib.h>
            #include <iostream>
            using namespace std;
            #define SIZE 110100

            struct heap
            {
            public:
            void init();
            void insert(long long);
            long long del_min();
            long long del_max();

            void fool_min_insert(long long,long long);
            void fool_max_insert(long long,long long);
            void min_push_down(long long&);
            void max_push_down(long long&);

            long long parent(long long n){return n/2;}
            long long lchild(long long n){if(n*2<=size)return 2*n;else return 0;}
            long long rchild(long long n){if(n*2+1<=size)return n*2+1;else return 0;}
            bool is_min_heap(long long n)
            {
            while(n>3)n/=2;
            if(n==2)return true;
            else return false;
            }
            long long partner(long long n)
            {
            long long j=log((double)n)/log((double)2);
            long long k=(long long)pow((double)2,(double)j)-1;
            long long e=k^n;
            if(e<=size)return e;
            else return partner(parent(n));
            }
            void swap(long long &a,long long &b){long long temp=a;a=b;b=temp;}
            private:
            long long key[SIZE];
            long long size;
            };
            void heap::init()
            {
            size=1;
            memset(key,-1,sizeof(key));
            }
            void heap::fool_min_insert(long long k,long long pos)
            {
            key[pos]=k;
            long long cur=pos;
            while(parent(cur)>1)
            {
            if(key[parent(cur)]>key[cur])
            {
            swap(key[parent(cur)],key[cur]);
            cur=parent(cur);
            }
            else break;
            }
            }
            void heap::fool_max_insert(long long k,long long pos)
            {
            key[pos]=k;
            long long cur=pos;
            while(parent(cur)>1)
            {
            if(key[parent(cur)]<key[cur])
            {
            swap(key[parent(cur)],key[cur]);
            cur=parent(cur);
            }
            else break;
            }
            }
            void heap::insert(long long k)
            {
            size++;
            if(size==2)
            {
            key[size]=k;
            return;
            }
            if(is_min_heap(size))
            {
            long long j=partner(size);
            if(k<=key[j])
            fool_min_insert(k,size);
            else
            {
            fool_min_insert(key[j],size);
            fool_max_insert(k,j);
            }
            }
            else
            {
            long long j=partner(size);
            if(k>=key[j])
            fool_max_insert(k,size);
            else
            {
            fool_max_insert(key[j],size);
            fool_min_insert(k,j);
            }
            }
            }
            void heap::min_push_down(long long& cur)
            {
            long long c;
            while(lchild(cur)!=0)
            {
            c=cur;
            if(lchild(cur)!=0)c=lchild(cur);
            if(rchild(cur)!=0&&key[rchild(cur)]<=key[lchild(cur)])
            c=rchild(cur);
            key[cur]=key[c];
            cur=c;
            }
            }
            void heap::max_push_down(long long& cur)
            {
            long long c;
            while(lchild(cur)!=0)
            {
            c=cur;
            if(lchild(cur)!=0)c=lchild(cur);
            if(rchild(cur)!=0&&key[rchild(cur)]>=key[lchild(cur)])
            c=rchild(cur);
            key[cur]=key[c];
            cur=c;
            }
            }
            long long heap::del_min()
            {
            if(size==1)return 0;
            if(size==2)
            {
            size--;
            return key[size+1];
            }
            long long e=key[2];
            long long cur=2;
            min_push_down(cur);
            long long j=partner(cur);
            long long value=key[size];
            if(value<=key[j])
            fool_min_insert(value,cur);
            else
            {
            fool_min_insert(key[j],cur);
            fool_max_insert(value,j);
            }
            size--;
            return e;
            }
            long long heap::del_max()
            {
            if(size==1)return 0;
            if(size==2)return key[size--];
            long long e=key[3];
            long long cur=3;
            max_push_down(cur);
            long long j=partner(cur);
            long long value=key[size];
            if(lchild(j)==size)j=lchild(j);
            else if(rchild(j)<=size)
            j=(key[lchild(j)]>key[rchild(j)])?lchild(j):rchild(j);
            if(value>=key[j])
            fool_max_insert(value,cur);
            else
            {
            fool_max_insert(key[j],cur);
            fool_min_insert(value,j);
            }
            size--;
            return e;
            }
            int main()
            {
            heap h;
            h.init();
            long long n,pts;
            char ch;
            scanf("%lld",&n);
            while(n-->0)
            {
            getchar();
            scanf("%c",&ch);
            if(ch=='I')
            {
            scanf("%lld",&pts);
            h.insert(pts);
            }
            else if(ch=='H')
            printf("%lld\n",h.del_max());
            else if(ch=='L')
            printf("%lld\n",h.del_min());
            }
            return 0;
            }  回復  更多評論
              
            # re: 基于雙端堆實現的優先級隊列(2): 內幕[未登錄] 2014-01-05 16:02 | 春秋十二月
            @Cppowboy
            是不是操作后,序列違反了雙端堆的性質?我的代碼是可以用的,當時做過很久的隨機測試,都沒有違反性質。  回復  更多評論
              
            # re: 基于雙端堆實現的優先級隊列(2): 內幕[未登錄] 2014-01-05 16:11 | 春秋十二月
            @Cppowboy
            實現細節可以不同,我的實現與stl中的優先級隊列類似,關鍵是各種操作后,保證序列不違反性質就行了。  回復  更多評論
              
            伊人久久综在合线亚洲2019| 亚洲国产成人久久综合一| 亚洲国产成人久久精品影视| 亚洲国产精品无码久久一线| 久久久久久久综合狠狠综合| 亚洲国产精品无码久久青草| 91精品国产综合久久香蕉| 久久精品男人影院| 久久最近最新中文字幕大全| 99久久成人国产精品免费| MM131亚洲国产美女久久| 国产亚洲欧美精品久久久| 伊人情人综合成人久久网小说| 国产精品美女久久久久久2018| 国内精品久久久久久久久| 88久久精品无码一区二区毛片 | 久久久WWW免费人成精品| 中文字幕亚洲综合久久2| 国产精品成人99久久久久| 久久久久久国产精品无码超碰| 久久精品成人免费国产片小草| 免费一级欧美大片久久网| 久久精品国产99国产精偷| 久久免费看黄a级毛片| 麻豆一区二区99久久久久| 性做久久久久久免费观看| 久久99精品国产自在现线小黄鸭| 成人精品一区二区久久久| 久久人人爽人人爽AV片| 中文字幕久久精品 | 色欲久久久天天天综合网| 国内精品免费久久影院| 久久综合色之久久综合| 久久久精品无码专区不卡| 久久九九有精品国产23百花影院| 一级做a爰片久久毛片毛片| 国产精品成人无码久久久久久| 国产精品狼人久久久久影院| 久久久久亚洲Av无码专| 精品欧美一区二区三区久久久| 久久国产综合精品五月天|