1. 程式人生 > >堆Heap和優先佇列(Priority Queue)學習小結

堆Heap和優先佇列(Priority Queue)學習小結

Heap是一種資料結構,能保證取max/min是O(1)時間。通常如果查最小值/最大值,我們可以用Heap。如果是查是不是存在(Contain()),就用HashMap。如果又要查最小/大值,又要查是不是存在,就用Heap+HashMap.

Max-heap/Min-heap保證父節點都比子節點大/小,但兄弟節點之間沒有大小關係。

Heap雖然是一個(滿)二叉樹,但通常用陣列實現(因為容易實現)。一個節點i的左子節點編號是2*i,右子節點是2*i+1。Heap通常是完全二叉樹(因為效率高),但沒有要求一定是完全二叉樹。

Heap Sort就是把所有元素放到Max Heap或Min Heap裡面,再一個一個pop出來。時間複雜度最壞/最好/平均 都是O(nlogn),空間複雜度為O(1)。注意Heap Sort是不穩定排序,因為它不能保證重複元素排序後還保留前後一致的順序。注意Heap Sort實際應用不多,為什麼呢?因為Heap的維護很麻煩。實際場景中的資料是頻繁發生變動的,而對於待排序序列的每次更新(增,刪,改),我們都要重新做一遍堆的維護,以保證其特性(父節點大/小於其子節點),這在大多數情況下都是沒有必要的。當資料更新不很頻繁的時候,堆排序可能會比較實用。

Priority Queue也是一種資料結構。其中每個元素都有一個關鍵字key,元素之間的比較都是通過key來比較的。優先佇列包括最大優先佇列和最小優先佇列,優先佇列的應用比較廣泛,比如作業系統中的排程程式,當一個作業完成後,需要在所有等待排程的作業中選擇一個優先順序最高的作業來執行,並且也可以新增一個新的作業到作業的優先佇列中。一個典型的例子是實時輸出最近的一個小時內訪問頻率最高的10個IP 。注意這裡是實時輸出,也就是說Priority Queue/Heap可以用於線上演算法,不需要讀完全部的資料,可以real time輸出到當前為止的結果。

Priority Queue可以用Heap實現,也可以用其他方式,比如說直接對陣列插入實現,但是效率不高。注意Priority Queue不是Queue,那為什麼它的名字裡面有queue呢?可能是因為它能提供queue的介面,比如remove(),pop(), insert(), top()等等。

Priority Queue和Heap有什麼區別嗎?如果是JAVA自帶的Priority Queue的話,答案是有的(C++的Priority Queue可能也類似,具體要確認一下)。
具體的區別是在remove操作中,Priority Queue的時間複雜度是O(n),而Heap是O(logn)。因為Priority Queue需要找到這個資料,需要O(n)的時間,而Heap藉助了HashMap,所以只需要O(1)的時間就可以找到。那為什麼Priority Queue不用HashMap呢?這個就是JAVA提供的函式裡面沒有加上這一塊而已,如果自己程式設計就也可以是O(logn)。

C++的priority_queue的定義是:
priority_queue<Type, Container, Functional>


Type為資料型別, Container為儲存資料的容器,Functional為元素比較方式。

如果不寫後兩個引數,那麼容器預設用的是vector,比較方式預設用operator<,也就是優先佇列是大頂堆,隊頭元素最大。

另外,需要注意的是,C++ STL預設的priority_queue是將優先順序最大的放在佇列最前面,也即是最大堆。
假設有如下一個struct:

struct Node {
    int value;
    int idx;
    Node (int v, int i): value(v), idx(i) {}
    friend bool operator < (const struct Node &n1, const struct Node &n2) ; 
};

inline bool operator < (const struct Node &n1, const struct Node &n2) {
    return n1.value < n2.value;
}

priority_queue<Node> pq; // 此時pq為最大堆

如果需要最小堆,則需要如下實現:

struct Node {
    int value;
    int idx;
    Node (int v, int i): value(v), idx(i) {}
//  friend bool operator < (const struct Node &n1, const struct Node &n2) ;
    friend bool operator > (const struct Node &n1, const struct Node &n2) ;
}; 

inline bool operator > (const struct Node &n1, const struct Node &n2) {
    return n1.value > n2.value;
}

定義的時候宣告
priority_queue< Node, vector< Node >, greater< Node > > pq; // 此時greater會呼叫 > 方法來確認Node的順序,此時pq是最小堆。
可以參考下面定義最小堆的例子:

struct node{
  int idx;
  int key;
  node(int a=0, int b=0):idx(a), key(b){}
};

struct cmp{
  bool operator()(node a, node b){
    return a.key > b.key;
  }
}; 
priority_queue<node, vector<node>, cmp> q;

當然也可以將其乘以一個負數,比如要構造一個int型別的最小堆:

priority_queue pq; //
pq.push( -1 * v1) ; //
pq.push( -1 * v2) ; //
pq.push( -1 * v3) ; // 分別是插入v1, v2, v3變數的相反數,那麼pq其實也就變相成為了最小堆

另外,也可以通過定義struct cmp來實現,cmp裡面只需給出operator()函式。給出operator()後則不需再定義operator < 或operator >。
用這種方法必須通過以下方式定義priority_queue,自動生成最小堆,也就是根據operator()函式得到的最小元素會在top。如果想生成實際上的最小堆則修改一下operator()函式把>改為<即可。

    priority_queue<Node,vector<Node>,cmp>q;
#include <iostream>
#include <queue>
using namespace std;
struct Node{
	int x, y;
}node;
struct cmp{
    bool operator()(Node a,Node b){
        if(a.x==b.x) return a.y>b.y;
        return a.x>b.x;}
};

 int main(){
    priority_queue<Node,vector<Node>,cmp>q;
    for(int i=0;i<10;i++){
    	node.x=i;
    	node.y=10-i/2;
		q.push(node);
    }
    while(!q.empty()){
        cout<<q.top().x<<' '<<q.top().y<<endl;
        q.pop();
    }
    return 0;
}

該程式碼輸出如下:
0 10
1 10
2 9
3 9
4 8
5 8
6 7
7 7
8 6
9 6

如果將operator()裡面的>改為<,則輸出結果為:
9 6
8 6
7 7
6 7
5 8
4 8
3 9
2 9
1 10
0 10

相關推薦

Heap優先佇列(Priority Queue)學習小結

Heap是一種資料結構,能保證取max/min是O(1)時間。通常如果查最小值/最大值,我們可以用Heap。如果是查是不是存在(Contain()),就用HashMap。如果又要查最小/大值,又要查是不是存在,就用Heap+HashMap. Max-heap/M

優先佇列Priority QueueHeap

對COMP20003中的Priority queue部分進行總結。圖片來自於COMP20003 queue佇列,顧名思義特點先進先出 priority queue優先佇列,出來的順序按照優先順序priority大小,越大(小)的先pop。 普通的方法:   Unsorted array:    

排序優先佇列的那些事

1. 什麼是堆? 堆是一種資料結構,它是一顆完全二叉樹。 堆分為最大堆和最小堆: 最大堆:任意節點的值不大於其父親節點的值。 最小堆:任意節點的值不小於其父親節點的值。 如下圖所示,就是個最大堆: 注:本文中的程式碼實現是最大堆,最小堆的實現相似,不再冗贅。 2. 堆有什麼用途? 堆最常用於優先

優先佇列(priority queue)常用的用法

優先佇列(priority queue)   優先順序佇列 是不同於先進先出佇列的另一種佇列。每次從佇列中取出的是具有最高優先權的元素。        首先它是一個佇列,但是它強調了“優先”二字,所以,已經不能算是一般意義上的隊列了,它的“優先”意指取隊首元素時,有一定的

STL之heap與優先順序佇列Priority Queue詳解

一、heap              heap並不屬於STL容器元件,它分為 max heap 和min heap,在預設情況下,max-heap是優先佇列(priority queue)的底層實現機制。而這個實現機制中的max-heap實際上 是以一個vector表現

二叉樹(優先佇列

堆是一種特殊的二叉樹。 最小值堆:最小值堆的特性。 對於堆的任意非葉節點K,K的值總是小於或者等於左右子節點。 K <= 左節點;K <= 又節點; 堆例項: 堆實際上是一個完全二叉樹(若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1)

(Heap)棧(Stack)

數據結構 item ext 調用 .com 結束 baidu 決定 text 堆,隊列優先,先進先出(FIFO—first in first out) 棧,先進後出(FILO—First-In/Last-Out) 棧(操作系統):由操作系統自動分配釋放 ,存放函數的參

解釋內存中的棧(stack)、(heap)靜態區(static area)的用法。

ner 創建 static padding IT body 新版 變量 類型 通常我們定義一個基本數據類型的變量,一個對象的引用,還有就是函數調用的現場保存都使用內存中的棧空間;而通過new關鍵字和構造器創建的對象放在堆空間;程序中的字面量(literal)如直接書寫的10

FIFO佇列優先佇列

FIFO佇列 定義:先進先出的儲存結構(刪除時先刪最後一個元素) queue<型別> q; 增: q.push(元素值); //在隊尾加入一個元素 void 刪: q.pop(); //刪除

迴圈佇列佇列queue

迴圈佇列和鏈佇列(queue)   佇列的定義:佇列是一種特殊的線性表,是一種先進先出(FIFO)的資料結構。它只允許在表的前端(front)進行刪除操作,而在表的後端(rear)進行插入操作。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。佇列中沒有元素時,稱為空佇列。

解釋記憶體中的棧(stack)、(heap)靜態儲存區的用法。

String str = new String(“hello”); 答:通常我們定義一個基本資料型別的變數,一個物件的引用,還有就是函式呼叫的現場儲存都使用記憶體中的棧空間;而通過new關鍵字和構造器建立的物件放在堆空間;程式中的字面量(literal)如直接書寫的100、“hello”和常

合併果子(佇列優先佇列

有兩種方法 一種是佇列 一種是優先佇列(priority_queue) 這兩種方法的區別是佇列定義時沒有自動排序 所以只能在輸入的時候按順序才能輸出正解(所以佇列的方法不被認為是正解) 下面是程式碼 比較簡便: #include<bits/stdc++.h> using na

JAVA 線性表、棧、佇列優先佇列

線性表、棧、佇列和優先佇列 資料結構是以某種形式將資料組織在一起的合集。資料結構不僅儲存資料,還支援訪問和處理資料的操作。 JAVA的合集框架如下圖所示 合集 JAVA合集框架支援兩種型別的容器: 一種是儲存一個元素合集,簡稱合集。 另一種是為了儲存鍵、值對,稱

使用並查集UnionFind優先佇列PriorityQueue實現Kruskal演算法

拿到題目,先看看UnionFind 和 PriorityQueue 怎麼實現吧,誰讓上課沒好好聽呢。 Kruskal演算法是通過按照權值遞增的順序依次選擇圖中的邊,當邊不處於同一連通分量時加入生成樹,

最小的N個(優先佇列)

題目描述 有兩個長度為N的序列A和B,在A和B中各任取一個數相加可以得到N2個和,求這N2個和中最小的N個。 輸入 第一行輸入一個正整數N(1<=N<=100000); 第二行N個整數Ai且Ai<=109;第三行N個整數Bi且Bi<

解釋記憶體中的棧(stack)、(heap)靜態區(static area)的用法

堆區:專門用來儲存物件的例項(new 建立的物件和陣列),實際上也只是儲存物件例項的屬性值,屬性的型別和物件本身的型別標記等,並不儲存物件的方法(方法是指令,儲存在Stack中)1.儲存的全部是物件,每個物件都包含一個與之對應的class的資訊。(class的目的是得到操作指

(heap)棧(stack)、記憶體洩漏(memory leak)記憶體溢位

簡單的可以理解為: heap:是由malloc之類函式分配的空間所在地。地址是由低向高增長的。 stack:是自動分配變數,以及函式呼叫的時候所使用的一些空間。地址是由高向低減少的。 一、預備知識—程式的記憶體分配 一個由c/C++編譯的程式佔用的記憶體分為以下幾個部分

c語言最小的實現-優先佇列

一、背景 libevent 中有定時事件的管理,使用者可以把超時的定時事件插入到 管理器中,當時間到了之後觸發使用者的回撥函式處理; 查看了原始碼發現,定時器的資料結構其實是由最小堆來實現的。 二、相

java中(heap)棧(stack)的區別

   在學習java的過程中,經常會見到椎和棧的介紹,但是一直都是瞭解了個大概,而且看了之後又經常會忘掉,所以這次在網上查找了一下資料,把對堆和棧的介紹記錄下來,以供複習使用。     在java中記憶體的佔用主要分為四塊:靜態區、程式碼區、堆、棧。其中,堆和棧是使用最多的

優先佇列Priority Queues使用手冊

優先佇列容器預設使用向量容器實現,使用者也可以使用雙端佇列容器。優先佇列總是把優先順序最高的元素放在佇列的最前方,來保持佇列的有序性。 插入操作push()使用一個雙變數的布林函式,將佇列中的元素重新排序,以滿足這個要求。 該函式可以由使用者提供,否則可以使用<操作