heap的一些實現,二叉堆,左式堆,二項佇列

2021-07-27 19:04:56 字數 4178 閱讀 1261

//這裡的堆指的都是二叉堆,為了優先佇列產生(優先佇列,使一些特殊的結點在出隊的時候要優先出來。出隊入隊操作變成了insert和delete)

//堆是乙個完全二叉樹,除了最後一層,其餘層都是滿的。這樣的話儲存用乙個陣列就可以,任乙個元素位置在i,其左兒子位置是2*i,右兒子位置是2*i+1,父結點是i/2向下取整

//完全二叉樹的高度是logn的下界,所以涉及到完全二叉樹的操作,是跟這個高度相關的,就是o(logn),比如下面的上濾和下濾

//buildheap是o(n),它的界就是堆兩個結點之間的線的條數,這個可以通過計算堆中所有結點的高度和來得到,這個和是o(n)

//d堆,二叉堆是2堆,這樣就理解d堆是什麼了。書上說實踐中4堆可以勝過二叉堆......

//二叉堆和bst(二叉搜尋樹)的區別,二叉搜尋樹的左結點《父結點《右結點,堆是父結點小於孩子結點,孩子結點的順序沒有比。大於同理。

template class binaryheap

bool isempty() const;

const comparable & findmin() const;

void insert(const comparable & x) //insert相當於入隊操作

array[hole] = x;

} void deletemin() //deletemin相當於出隊操作,操作可以迅速執行依賴於堆序性質,最小的在根上,這個規律遞迴到子堆

void deletemin(comparable & minitem) //和上面的區別,就是把出隊的元素存到minitem裡了

void makeempty();

private:

int currentsize;

vectorarray;

void buildheap() //這個堆(現在還不是堆)先是亂排的,然後從下往上一直下濾,這個堆就是有序的了 }

void percolatedown(int hole) //下濾,上面呼叫的函式

if (array[child] < tmp)

else

break;

} array[hole] = tmp;

}};//左式堆:1.左兒子的零路徑長至少與右路徑的零路徑長一樣大;2.任一結點的零路徑長比它的諸兒子結點的零路徑長的最小值多1。merge用到了這兩個性質

//n個結點的左式樹有一條右路經最多含有log(n+1)的下界個結點。對左式堆操作的一般思路是,將所有的工作放到右路徑上進行,保證樹深短。

//從樹開始,這裡面的類定義都有乙個技巧,就是用公有的函式呼叫私有的函式

templateclass leftistheap

void deletemin()

void deletemin(comparable & minitem)

void makeempty();

void merge(leftistheap & rhs) //合併,左式堆的最主要的演算法。遞迴的將具有大的根值的堆和具有小的根值的堆的右子堆合併。所以執行合併的時間與右路徑的長的和成正比,合併兩個左式堆的時間界o(logn)

root = merge(root, rhs.root);

rhs.root = null;

} const leftistheap & operator=(const liftistheap & rhs);

private:

struct leftistnode

};leftistnode * root;

leftistnode * merge(leftistnode *h1, leftistnode *h2)

leftistnode * merge1(leftistnode *h1, leftistnode *h2);

else

h1->npl = h1->right->npl + 1;

} return h1;

} void swapchildren(leftistnode *t);

void reclainmemory(leftistnode *t);

leftistnode * clone(leftistnode *t) const;

};//二項佇列,是乙個二項樹的森林,n個結點,用幾棵二項樹組成

templateclass binomialqueue

int minindex = findminindex(); //找到最小的是哪棵樹的樹根,返回座標

minitem = thetrees[minindex]->element;

binomialnode *oldroot = thetrees[minindex];

binomialnode *deletedtree = oldroot->leftchild;

delete oldroot;

binomialqueue deletedqueue; //把找到的那棵樹的根刪除之後,剩下的變成乙個新的森林

deletedqueue.thetrees.resize(minindex + 1); //為什麼我覺得resize(minindex)也可以......

deletedqueue.currentsize = (1 << minindex) - 1; //就是2^minindex-1,就是那棵樹去掉根結點之後的結點數

for (int j = minindex - 1; j >= 0; j--) //這個就是造森林的過程,**沒問題.......二項佇列的特點,每一棵樹的子樹,都是層數從0往上排的.......只是整個森林不一定每棵樹都有

thetrees[minindex] = null;

currentsize -= deletedqueue.currentsize + 1;

merge(deletedqueue);

} void makeempty();

void merge(binomialqueue & rhs)

binomialnode * carry = null; //合併二項佇列的過程,有兩個森林,然後按照從小往大排每棵樹,把兩個森林對應的位置相加(combinetrees就是幹這個的)。

//這個carry相當於,乙個進製,比如高度為2的兩個樹合併之後,高度變成了3,carry就要存這個,之前的兩棵樹2的位置清空;

//然後carry,兩棵樹有8種情況,逐個分析。這裡combine,也只合併兩個高度相同的tree。

for (int i = 0, j = 1; j <= currentsize; i++; j *= 2) //這裡的i就是thetrees的標號,j是用來控制這個標號的。i對應這個位置的樹的高度,每棵樹有2^i個結點,所以如果總共有n個結點,樹最大的編號就是logn的下界

for (int k = 0; k < rhs.thetrees.size(); k++)

rhs.thetrees[k] = null;

rhs.currentsize = 0;

} }const binomialqueue & operator=(const binomialqueue & rhs);

private:

struct binomialnode

};enum(default_trees = 1);

//乙個二項佇列的森林,包括一共有多少個結點,還有森林每棵樹根結點的vector(vector的座標是這棵樹的高度,這棵樹的結點數是2^i,i是座標。比如只有乙個結點的就是0,如果沒有這棵樹,就是null)

int currentsize; //總的結點數

vectorthetrees; //an array of tree roots

int findminindex() const

}return minindex;

} int capcity() const;

binomialnode* combinetrees(binomialnode *t1, binomialnode *t2) //這個是把兩個大小一樣的tree合併 t1和t2是兩棵樹的root

void makeempty(binomialnode * & t);

binomialnode * clone(binomialnode *t) const;

};

左式二叉堆

定義 零路徑長npl x 定義為從x到乙個沒有兩個兒子的節點的最短路徑長。npl x min 1 定義npl null 1,那麼這個公式滿足只有乙個子節點的節點 左式堆的性質 1 結構性質 對於堆中的任一節點,左兒子的零路徑長至少與右兒子的零路徑長一樣大 2 堆序性質 和二叉樹的相同,節點的值大於等...

二叉堆 Heap 的學習

定義 在一棵完全二叉樹中,滿足a parent i a i 小根堆為例 性質1 parent i i 2 left i i 2 right i i 2 1 性質2 高度為 h的堆,元素對多 個,最少 個 性質3 含 n個元素的堆的高度為floor lg n 性質 4 當用陣列表示儲存了 n個元素的堆...

二叉堆的實現

include include define max heap size 101 class binaryminheap void insert intvalue void removemin intgetmin void displayheaparray private int heaparray...