• <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>
            隨筆-161  評(píng)論-223  文章-30  trackbacks-0
            前言
               眾所周知,stl中的優(yōu)先級(jí)隊(duì)列是基于最大堆實(shí)現(xiàn)的,能夠在對(duì)數(shù)時(shí)間內(nèi)插入元素和獲取優(yōu)先級(jí)最高的元素,但如果要求在對(duì)數(shù)時(shí)間內(nèi)還能獲取優(yōu)先級(jí)最低的元素,而不只是獲取優(yōu)先級(jí)最高的元素,該怎么實(shí)現(xiàn)呢?可以用最大堆-最小堆或雙端堆數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn),最大堆-最小堆和雙端堆都是支持雙端優(yōu)先隊(duì)列的插入、刪除最小元素和最大元素等操作的堆,在這些操作上,時(shí)間復(fù)雜度都是對(duì)數(shù)時(shí)間,但是雙端堆的操作比最大堆-最小堆的相應(yīng)操作還要快一個(gè)常數(shù)因子,而且算法更加簡(jiǎn)單,因此本文講述選擇使用雙端堆實(shí)現(xiàn)優(yōu)先級(jí)隊(duì)列的原理。

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

             (2)刪除最小元素:首先在最小堆中執(zhí)行一個(gè)向下回溯過(guò)程,這個(gè)過(guò)程執(zhí)行的堆大小要比原來(lái)的堆小1,從最小元素結(jié)點(diǎn)開(kāi)始,每次選取關(guān)鍵字值較小的孩子結(jié)點(diǎn),并用其值更新父結(jié)點(diǎn)值,直到底部沒(méi)有孩子結(jié)點(diǎn),執(zhí)行的結(jié)果是在底部某葉子結(jié)點(diǎn)i處形成一個(gè)空洞(形象說(shuō)法,這個(gè)空洞需要后續(xù)操作來(lái)調(diào)整填補(bǔ),下同),i的對(duì)稱結(jié)點(diǎn)j在最大堆中,設(shè)最末元素結(jié)點(diǎn)為e,如果KeyValue(e)>KeyValue(j),則設(shè)置KeyValue(i)=KeyValue(j),并執(zhí)行在最大堆中位置j處插入值KeyValue(e)的操作;否則執(zhí)行在最小堆中位置i處插入值KeyValue(e)的操作。

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

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

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

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

            評(píng)論:
            # re: 基于雙端堆實(shí)現(xiàn)的優(yōu)先級(jí)隊(duì)列(2): 內(nèi)幕 2014-01-05 10:06 | Cppowboy
            你好,我學(xué)習(xí)了您講解的算法之后自己按照算法寫(xiě)了C++的實(shí)現(xiàn),包括插入,刪除最小,刪除最大的操作,但是在線評(píng)測(cè)總是出問(wèn)題,個(gè)人檢查認(rèn)為是對(duì)的,您能幫忙指點(diǎn)一下,看看哪里錯(cuò)了嗎?
            #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;
            }  回復(fù)  更多評(píng)論
              
            # re: 基于雙端堆實(shí)現(xiàn)的優(yōu)先級(jí)隊(duì)列(2): 內(nèi)幕[未登錄](méi) 2014-01-05 16:02 | 春秋十二月
            @Cppowboy
            是不是操作后,序列違反了雙端堆的性質(zhì)?我的代碼是可以用的,當(dāng)時(shí)做過(guò)很久的隨機(jī)測(cè)試,都沒(méi)有違反性質(zhì)。  回復(fù)  更多評(píng)論
              
            # re: 基于雙端堆實(shí)現(xiàn)的優(yōu)先級(jí)隊(duì)列(2): 內(nèi)幕[未登錄](méi) 2014-01-05 16:11 | 春秋十二月
            @Cppowboy
            實(shí)現(xiàn)細(xì)節(jié)可以不同,我的實(shí)現(xiàn)與stl中的優(yōu)先級(jí)隊(duì)列類似,關(guān)鍵是各種操作后,保證序列不違反性質(zhì)就行了。  回復(fù)  更多評(píng)論
              
            7777久久亚洲中文字幕| 久久久久久夜精品精品免费啦| 嫩草影院久久国产精品| 精品久久香蕉国产线看观看亚洲| 国产精品一区二区久久精品| 国产高潮国产高潮久久久91 | 国内精品久久久久久久涩爱| 久久影院午夜理论片无码 | 色8久久人人97超碰香蕉987| 一本大道加勒比久久综合| 久久99精品国产麻豆蜜芽| 久久精品国产99国产精品亚洲 | 久久久久亚洲精品天堂| 国产福利电影一区二区三区久久久久成人精品综合 | 久久午夜伦鲁片免费无码| 91久久精品国产91性色也| 国产毛片欧美毛片久久久| 99久久精品九九亚洲精品| 久久久久久久久无码精品亚洲日韩 | 国产亚洲精品久久久久秋霞| 99久久伊人精品综合观看| 亚洲精品无码久久久久去q| 久久综合九色欧美综合狠狠| 91精品国产综合久久久久久 | 精品免费tv久久久久久久| 国产精品久久婷婷六月丁香| 国产农村妇女毛片精品久久 | 亚洲国产精品成人AV无码久久综合影院 | 超级碰久久免费公开视频| 丰满少妇人妻久久久久久| 久久精品国产99国产精品导航 | 久久99精品久久久久久水蜜桃| 国产精品国色综合久久| 久久亚洲中文字幕精品有坂深雪 | 精品乱码久久久久久夜夜嗨| 国产精品视频久久久| 97久久香蕉国产线看观看| 浪潮AV色综合久久天堂| 2019久久久高清456| 久久久无码精品亚洲日韩京东传媒| 伊人 久久 精品|