日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【堆内存】动态图+代码五分钟轻松理解学会

發(fā)布時(shí)間:2025/3/15 编程问答 12 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【堆内存】动态图+代码五分钟轻松理解学会 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

?

前言背景

  • 堆(heap)又被為優(yōu)先隊(duì)列(priority queue)。盡管名為優(yōu)先隊(duì)列,但堆并不是隊(duì)列。
  • 因?yàn)殛?duì)列中允許的操作是先進(jìn)先出(FIFO),在隊(duì)尾插入元素,在隊(duì)頭取出元素。
  • 而堆雖然在堆底插入元素,在堆頂取出元素,但是堆中元素的排列不是按照到來(lái)的先后順序,而是按照一定的優(yōu)先順序排列的。
  • 堆的實(shí)現(xiàn)

    ????堆的一個(gè)經(jīng)典的實(shí)現(xiàn)是完全二叉樹(shù)(complete binary tree),這樣實(shí)現(xiàn)的堆稱為二叉堆(binary heap)。

    ????這里來(lái)說(shuō)明一下滿二叉樹(shù)的概念與完全二叉樹(shù)的概念以及完滿二叉樹(shù)概念。

    • 滿二叉樹(shù)(又稱完美二叉樹(shù))一個(gè)深度為k(>=-1)且有2^(k+1) - 1個(gè)結(jié)點(diǎn)的二叉樹(shù)。直接上圖理解。
    • 完全二叉樹(shù)從根結(jié)點(diǎn)到倒數(shù)第二層滿足完美二叉樹(shù),最后一層可以不完全填充,其葉子結(jié)點(diǎn)都靠左對(duì)齊。
    • 完滿二叉樹(shù)所有非葉子結(jié)點(diǎn)的度都是2。(只要你有孩子,你就必然是有兩個(gè)孩子。)?

    ????堆的特性:

    • 必須是完全二叉樹(shù)

    • 任一結(jié)點(diǎn)的值是其子樹(shù)所有結(jié)點(diǎn)的最大值或最小值

    • 最大值時(shí),稱為“最大堆”,也稱大頂堆;

    • 最小值時(shí),稱為“最小堆”,也稱小頂堆。

    ????

    堆的基礎(chǔ)實(shí)現(xiàn)

    ????只要謹(jǐn)記堆的定義特性,實(shí)現(xiàn)起來(lái)其實(shí)是很容易的。

    • 特性1. ?維持完全二叉樹(shù) ?

    • 特性2. ?子類(lèi)數(shù)字總是大于父類(lèi)數(shù)字 ?

    1public class MinHeap <E extends Comparable<E>> {2 private Array<E> data;34 public MinHeap(int capacity){5 data = new Array<>(capacity);6 }78 public MinHeap(){9 data = new Array<>(); 10 } 11 12 // 返回堆中的元素個(gè)數(shù) 13 public int size(){ 14 return data.getSize(); 15 } 16 17 // 返回一個(gè)布爾值, 表示堆中是否為空 18 public boolean isEmpty(){ 19 return data.isEmpty(); 20 } 21 22 // 返回完全二叉樹(shù)的數(shù)組表示中,一個(gè)索引所表示的元素的父親節(jié)點(diǎn)的索引 23 private int parent(int index){ 24 return (index - 1) / 2; 25 } 26 27 // 返回完全二叉樹(shù)的數(shù)組表示中,一個(gè)索引所表示的元素的左孩子節(jié)點(diǎn)的索引 28 private int leftChild(int index){ 29 return index * 2 + 1; 30 } 31 32 // 返回完全二叉樹(shù)的數(shù)組表示中,一個(gè)索引所表示的元素的右孩子節(jié)點(diǎn)的索引 33 private int rightChild(int index){ 34 return index * 2 + 2; 35 } 36}

    ????????最小堆的插入(ADD)

    ????????

    ????????假設(shè)現(xiàn)有元素 5 需要插入,為了維持完全二叉樹(shù)的特性,新插入的元素一定是放在結(jié)點(diǎn) 6 的右子樹(shù);同時(shí)為了滿足任一結(jié)點(diǎn)的值要小于左右子樹(shù)的值這?一特性,新插入的元素要和其父結(jié)點(diǎn)作比較,如果比父結(jié)點(diǎn)小,就要把父結(jié)點(diǎn)拉下來(lái)頂替當(dāng)前結(jié)點(diǎn)的位置,自己則依次不斷向上尋找,找到比自己大的父結(jié)點(diǎn)就拉下來(lái),直到?jīng)]有符合條件的值為止。

    ????動(dòng)畫(huà)講解

  • 在這里先將元素 5 插入到末尾,即放在結(jié)點(diǎn) 6 的右子樹(shù)。

  • 然后與父類(lèi)比較, 6 > 5 ,父類(lèi)數(shù)字大于子類(lèi)數(shù)字,子類(lèi)與父類(lèi)交換。

  • 重復(fù)此操作,直到不發(fā)生替換。

  • ????Show me the code:

    ????添加一個(gè)輔助函數(shù),用來(lái)交換傳入的索引兩個(gè)位置的元素值

    1/**2 * 交換傳入的索引兩個(gè)位置的元素值3 *4 * @param i5 * @param j6 */7 public void swap(int i, int j) {8 if (i < 0 || i >= size || j < 0 || j >= size)9 throw new IllegalArgumentException("Index is illegal."); 10 11 E temp = data[i]; 12 data[i] = data[j]; 13 data[j] = temp; 14 }

    ????數(shù)組中添加交換兩元素位置的方法,注意下面代碼中注釋的描述特性位置。?

    1 /**2 * 堆中添加元素方法。3 *4 * @param e5 */6 public void add(E e) {7 //特性1:新插入的元素首先放在數(shù)組最后,保持完全二叉樹(shù)的特性8 data.addLast(e);9 siftUp(data.getSize() - 1); 10 } 11 12 /** 13 * index 為i位置元素上浮。 14 * 15 * @param i 16 */ 17 private void siftUp(int i) { 18 //特性2:比較插入值和其父結(jié)點(diǎn)的大小關(guān)系,小于父結(jié)點(diǎn)則用父結(jié)點(diǎn)替換當(dāng)前值,index位置上升為父結(jié)點(diǎn) 19 // 當(dāng)上浮元素大于父親,繼續(xù)上浮。并且不能上浮到0之上 20 // 直到i 等于 0 或 比 父親節(jié)點(diǎn)小了。 21 while (i > 0 && data.get(i).compareTo(data.get(parent(i))) > 0) { 22 // 數(shù)組Array中添加方法swap 23 data.swap(i, parent(i)); 24 i = parent(i); // 這句話讓i來(lái)到新的位置,使得循環(huán)可以查看新的位置是否還要大。 25 } 26 }

    ????最小堆的刪除(DELETE)

    ????

    ????

    ????核心點(diǎn):將最后一個(gè)元素填充到堆頂,然后不斷的下沉這個(gè)元素。

    ?????假設(shè)要從節(jié)點(diǎn) 1 ,也可以稱為取出節(jié)點(diǎn) 1 ,為了維持完全二叉樹(shù)的特性 ,我們將最后一個(gè)元素 6 去替代這個(gè) 1 ;然后比較 1 和其子樹(shù)的大小關(guān)系,如果比左右子樹(shù)大(如果存在的話),就要從左右子樹(shù)中找一個(gè)較小的值替換它,而它能自己就要跑到對(duì)應(yīng)子樹(shù)的位置,再次循環(huán)這種操作,直到?jīng)]有子樹(shù)比它小。

    ????通過(guò)這樣的操作,堆依然是堆,總結(jié)一下:

    • 找到要?jiǎng)h除的節(jié)點(diǎn)(取出的節(jié)點(diǎn))在數(shù)組中的位置

    • 用數(shù)組中最后一個(gè)元素替代這個(gè)位置的元素

    • 當(dāng)前位置和其左右子樹(shù)比較,保證符合最小堆的節(jié)點(diǎn)間規(guī)則

    • 刪除最后一個(gè)元素

    ????Show me the code:

    1 public E findMin() {2 return data.get(0);3 }45 public E extractMin() {67 E ret = findMin();89 data.swap(0, data.getSize() - 1); // 0位置元素和最后一個(gè)元素互換。 10 data.removeLast(); // 刪除此時(shí)的最后一個(gè)元素(最小值) 11 siftDown(0); // 對(duì)于0處進(jìn)行siftDown操作 12 13 return ret; 14 } 15 16 /** 17 * k位置元素下移 18 * 19 * @param k 20 */ 21 private void siftDown(int k) { 22 23 while(leftChild(k) < data.getSize()){ 24 int j = leftChild(k); // 在此輪循環(huán)中,data[k]和data[j]交換位置 25 if( j + 1 < data.getSize() && 26 data.get(j + 1).compareTo(data.get(j)) < 0 ) 27 j ++; 28 // data[j] 是 leftChild 和 rightChild 中的最小值 29 30 if(data.get(k).compareTo(data.get(j)) >= 0 ) 31 break; 32 33 data.swap(k, j); 34 k = j; 35 } 36 }

    時(shí)間復(fù)雜度?

    對(duì)于有 n 個(gè)節(jié)點(diǎn)的堆來(lái)說(shuō),其高度?d = log2n + 1。 根為第 0 層,則第 i 層結(jié)點(diǎn)個(gè)數(shù)為 2i,
    考慮一個(gè)元素在堆中向下移動(dòng)的距離。

    • 大約一半的結(jié)點(diǎn)深度為 d-1 ,不移動(dòng)(葉)。

    • 四分之一的結(jié)點(diǎn)深度為 d-2 ,而它們至多能向下移動(dòng)一層。

    • 樹(shù)中每向上一層,結(jié)點(diǎn)的數(shù)目為前一層的一半,而子樹(shù)高度加一

    堆有l(wèi)ogn層深,所以插入刪除的平均時(shí)間和最差時(shí)間都是O(logN)

    優(yōu)先隊(duì)列(priority_queue)

    普通隊(duì)列是一種先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),先放進(jìn)隊(duì)列的元素取值時(shí)優(yōu)先被取出來(lái)。而優(yōu)先隊(duì)列是一種具有最高優(yōu)先級(jí)元素先出的數(shù)據(jù)結(jié)構(gòu),比如每次取值都取最大的元素。

    優(yōu)先隊(duì)列支持下面的操作:

    • a. 找出優(yōu)先級(jí)最高的元素(最大或最小元素);

    • b. 刪除一個(gè)具有最高優(yōu)先級(jí)的元素;

    • c. 添加一個(gè)元素到集合中。

    代碼實(shí)現(xiàn)

    1public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {23 private MaxHeap<E> maxHeap;45 public PriorityQueue(){6 maxHeap = new MaxHeap<>();7 }89 @Override 10 public int getSize(){ 11 return maxHeap.size(); 12 } 13 14 @Override 15 public boolean isEmpty(){ 16 return maxHeap.isEmpty(); 17 } 18 19 @Override 20 public E getFront(){ 21 return maxHeap.findMax(); 22 } 23 24 @Override 25 public void enqueue(E e){ 26 maxHeap.add(e); 27 } 28 29 @Override 30 public E dequeue(){ 31 return maxHeap.extractMax(); 32 } 33}

    堆排序?

    理解了優(yōu)先隊(duì)列,堆排序的邏輯十分簡(jiǎn)單。

    • 第一步:讓數(shù)組形成堆有序狀態(tài);
    • 第二步:把堆頂?shù)脑胤诺綌?shù)組最末尾,末尾的放到堆頂,在剩下的元素中下沉到正確位置,重復(fù)操作即可。

    ?

    轉(zhuǎn)載于:https://my.oschina.net/u/3492343/blog/2998772

    總結(jié)

    以上是生活随笔為你收集整理的【堆内存】动态图+代码五分钟轻松理解学会的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。