成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

js數(shù)據(jù)結(jié)構(gòu)-二叉樹(二叉堆)

ningwang / 563人閱讀

摘要:二叉樹二叉樹是一種樹形結(jié)構(gòu),它的特點是每個節(jié)點最多只有兩個分支節(jié)點,一棵二叉樹通常由根節(jié)點,分支節(jié)點,葉子節(jié)點組成。

二叉樹

二叉樹(Binary Tree)是一種樹形結(jié)構(gòu),它的特點是每個節(jié)點最多只有兩個分支節(jié)點,一棵二叉樹通常由根節(jié)點,分支節(jié)點,葉子節(jié)點組成。而每個分支節(jié)點也常常被稱作為一棵子樹。

根節(jié)點:二叉樹最頂層的節(jié)點

分支節(jié)點:除了根節(jié)點以外且擁有葉子節(jié)點

葉子節(jié)點:除了自身,沒有其他子節(jié)點

常用術(shù)語
在二叉樹中,我們常常還會用父節(jié)點和子節(jié)點來描述,比如圖中2為6和3的父節(jié)點,反之6和3是2子節(jié)點

二叉樹的三個性質(zhì)

在二叉樹的第i層上,至多有2^i-1個節(jié)點

i=1時,只有一個根節(jié)點,2^(i-1) = 2^0 = 1

深度為k的二叉樹至多有2^k-1個節(jié)點

i=2時,2^k-1 = 2^2 - 1 = 3個節(jié)點

對任何一棵二叉樹T,如果總結(jié)點數(shù)為n0,度為2(子樹數(shù)目為2)的節(jié)點數(shù)為n2,則n0=n2+1

樹和二叉樹的三個主要差別

樹的節(jié)點個數(shù)至少為1,而二叉樹的節(jié)點個數(shù)可以為0

樹中節(jié)點的最大度數(shù)(節(jié)點數(shù)量)沒有限制,而二叉樹的節(jié)點的最大度數(shù)為2

樹的節(jié)點沒有左右之分,而二叉樹的節(jié)點有左右之分

二叉樹分類

二叉樹分為完全二叉樹(complete binary tree)和滿二叉樹(full binary tree)

滿二叉樹:一棵深度為k且有2^k - 1個節(jié)點的二叉樹稱為滿二叉樹

完全二叉樹:完全二叉樹是指最后一層左邊是滿的,右邊可能滿也可能不滿,然后其余層都是滿的二叉樹稱為完全二叉樹(滿二叉樹也是一種完全二叉樹)

二叉樹的數(shù)組表示

用一個數(shù)組來表示二叉樹的結(jié)構(gòu),將一組數(shù)組從根節(jié)點開始從上到下,從左到右依次填入到一棵完全二叉樹中,如下圖所示

通過上圖我們可以分析得到數(shù)組表示的完全二叉樹擁有以下幾個性質(zhì):

left = index * 2 + 1,例如:根節(jié)點的下標(biāo)為0,則左節(jié)點的值為下標(biāo)array[0*2+1]=1

right = index * 2 + 2,例如:根節(jié)點的下標(biāo)為0,則右節(jié)點的值為下標(biāo)array[0*2+2]=2

序數(shù) >= floor(N/2)都是葉子節(jié)點,例如:floor(9/2) = 4,則從下標(biāo)4開始的值都為葉子節(jié)點

二叉堆

二叉堆由一棵完全二叉樹來表示其結(jié)構(gòu),用一個數(shù)組來表示,但一個二叉堆需要滿足如下性質(zhì):

二叉堆的父節(jié)點的鍵值總是大于或等于(小于或等于)任何一個子節(jié)點的鍵值

當(dāng)父節(jié)點的鍵值大于或等于(小于或等于)它的每一個子節(jié)點的鍵值時,稱為最大堆(最小堆)


從上圖可以看出:

左圖:父節(jié)點總是大于或等于其子節(jié)點,所以滿足了二叉堆的性質(zhì),

右圖:分支節(jié)點7作為2和12的父節(jié)點并沒有滿足其性質(zhì)(大于或等于子節(jié)點)。

二叉堆的主要操作

insert:插入節(jié)點

delete:刪除節(jié)點

max-hepify:調(diào)整分支節(jié)點堆性質(zhì)

rebuildHeap:重新構(gòu)建整個二叉堆

sort:排序

初始化一個二叉堆

從上面簡單的介紹,我們可以知道,一個二叉堆的初始化非常的簡單,它就是一個數(shù)組

初始化一個數(shù)組結(jié)構(gòu)

保存數(shù)組長度

    class Heap{
        constructor(arr){
            this.data = [...arr];
            this.size = this.data.length;
        }
    }
max-heapify最大堆操作

max-heapify是把每一個不滿足最大堆性質(zhì)的分支節(jié)點進(jìn)行調(diào)整的一個操作。

如上圖:

調(diào)整分支節(jié)點2(分支節(jié)點2不滿足最大堆的性質(zhì))

默認(rèn)該分支節(jié)點為最大值

將2與左右分支比較,從2,12,5中找出最大值,然后和2交換位置

根據(jù)上面所將的二叉堆性質(zhì),分別得到分支節(jié)點2的左節(jié)點和右節(jié)點

比較三個節(jié)點,得到最大值的下標(biāo)max

如果該節(jié)點本身就是最大值,則停止操作

將max節(jié)點與父節(jié)點進(jìn)行交換

重復(fù)step2的操作,從2,4,7中找出最大值與2做交換

遞歸

    maxHeapify(i) {
        let max = i;
        
        if(i >= this.size){
            return;
        }
        // 當(dāng)前序號的左節(jié)點
        const l = i * 2 + 1;
        // 當(dāng)前需要的右節(jié)點
        const r = i * 2 + 2;
        
        // 求當(dāng)前節(jié)點與其左右節(jié)點三者中的最大值
        if(l < this.size && this.data[l] > this.data[max]){
            max = l;
        }
        if(r < this.size && this.data[r] > this.data[max]){
            max = r;
        }
        
        // 最終max節(jié)點是其本身,則已經(jīng)滿足最大堆性質(zhì),停止操作
        if(max === i) {
            return;
        }
        
        // 父節(jié)點與最大值節(jié)點做交換
        const t = this.data[i];
        this.data[i] = this.data[max];
        this.data[max] = t;
        
        // 遞歸向下繼續(xù)執(zhí)行
        return this.maxHeapify(max);
    }
重構(gòu)堆

我們可以看到,剛初始化的堆由數(shù)組表示,這個時候它可能并不滿足一個最大堆或最小堆的性質(zhì),這個時候我們可能需要去將整個堆構(gòu)建成我們想要的。
上面我們做了max-heapify操作,而max-heapify只是將某一個分支節(jié)點進(jìn)行調(diào)整,而要將整個堆構(gòu)建成最大堆,則需要將所有的分支節(jié)點都進(jìn)行一次max-heapify操作,如下圖,我們需要依次對12,3,2,15這4個分支節(jié)點進(jìn)行max-hepify操作

具體步驟:

找到所有分支節(jié)點:上面堆的性質(zhì)提到過葉子節(jié)點的序號>=Math.floor(n/2),因此小于Math.floor(n/2)序號的都是我們需要調(diào)整的節(jié)點。

例如途中所示數(shù)組為[15,2,3,12,5,2,8,4,7] => Math.floor(9/2)=4 => index小于4的分別是15,2,3,12(需要調(diào)整的節(jié)點),而5,2,8,4,7為葉子節(jié)點。

將找到的節(jié)點都進(jìn)行maxHeapify操作

    rebuildHeap(){
        // 葉子節(jié)點
        const L = Math.floor(this.size / 2);
        for(let i = L - 1; i>=0; i--){
            this,maxHeapify(i);
        }
    }
最大堆排序

最大堆的排序,如上圖所示:

交換首尾位置

將最后個元素從堆中拿出,相當(dāng)于堆的size-1

然后在堆根節(jié)點進(jìn)行一次max-heapify操作

重復(fù)以上三個步驟,知道size=0 (這個邊界條件我們在max-heapify函數(shù)里已經(jīng)做了)

    sort() {
        for(let i = this.size - 1; i > 0; i--){
            swap(this.data, 0, i);
            this.size--;
            this.maxHeapify(0);
        }
    }
插入和刪除

這個的插入和刪除就相對比較簡單了,就是對一個數(shù)組進(jìn)行插入和刪除的操作

往末尾插入

堆長度+1

判斷插入后是否還是一個最大堆

不是則進(jìn)行重構(gòu)堆

  insert(key) {
    this.data[this.size] = key;
    this.size++
    if (this.isHeap()) {
      return;
    }
    this.rebuildHeap();
  }

刪除數(shù)組中的某個元素

堆長度-1

判斷是否是一個堆

不是則重構(gòu)堆

  delete(index) {
    if (index >= this.size) {
      return;
    }
    this.data.splice(index, 1);
    this.size--;
    if (this.isHeap()) {
      return;
    }
    this.rebuildHeap();
  }
完整代碼
/**
 * 最大堆
 */

function left(i) {
  return i * 2 + 1;
}

function right(i) {
  return i * 2 + 2;
}

function swap(A, i, j) {
  const t = A[i];
  A[i] = A[j];
  A[j] = t;
}

class Heap {
  constructor(arr) {
    this.data = [...arr];
    this.size = this.data.length;
  }

  /**
   * 重構(gòu)堆
   */
  rebuildHeap() {
    const L = Math.floor(this.size / 2);
    for (let i = L - 1; i >= 0; i--) {
      this.maxHeapify(i);
    }
  }

  isHeap() {
    const L = Math.floor(this.size / 2);
    for (let i = L - 1; i >= 0; i++) {
      const l = this.data[left(i)] || Number.MIN_SAFE_INTEGER;
      const r = this.data[right(i)] || Number.MIN_SAFE_INTEGER;

      const max = Math.max(this.data[i], l, r);

      if (max !== this.data[i]) {
        return false;
      }
      return true;
    }
  }

  sort() {
    for (let i = this.size - 1; i > 0; i--) {
      swap(this.data, 0, i);
      this.size--;
      this.maxHeapify(0);
    }
  }

  insert(key) {
    this.data[this.size++] = key;
    if (this.isHeap()) {
      return;
    }
    this.rebuildHeap();
  }

  delete(index) {
    if (index >= this.size) {
      return;
    }
    this.data.splice(index, 1);
    this.size--;
    if (this.isHeap()) {
      return;
    }
    this.rebuildHeap();
  }

  /**
   * 堆的其他地方都滿足性質(zhì)
   * 唯獨跟節(jié)點,重構(gòu)堆性質(zhì)
   * @param {*} i
   */
  maxHeapify(i) {
    let max = i;

    if (i >= this.size) {
      return;
    }

    // 求左右節(jié)點中較大的序號
    const l = left(i);
    const r = right(i);
    if (l < this.size && this.data[l] > this.data[max]) {
      max = l;
    }

    if (r < this.size && this.data[r] > this.data[max]) {
      max = r;
    }

    // 如果當(dāng)前節(jié)點最大,已經(jīng)是最大堆
    if (max === i) {
      return;
    }

    swap(this.data, i, max);

    // 遞歸向下繼續(xù)執(zhí)行
    return this.maxHeapify(max);
  }
}

module.exports = Heap;
總結(jié)

堆講到這里就結(jié)束了,堆在二叉樹里相對會比較簡單,常常被用來做排序和優(yōu)先級隊列等。堆中比較核心的還是max-heapify這個操作,以及堆的三個性質(zhì)。

后續(xù)

下一篇應(yīng)該會介紹二叉搜索樹。歡迎大家指出文章的錯誤,如果有什么寫作建議也可以提出。我會持續(xù)的去寫關(guān)于前端的一些技術(shù)文章,如果大家喜歡的話可以關(guān)注一和點個贊,你的贊是我寫作的動力。
順便再提一下,我在等第一個粉絲哈哈

以下個人公眾號,歡迎大家關(guān)注,用戶量達(dá)到一定的量,我會推出一些前端教學(xué)視頻

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/100683.html

相關(guān)文章

  • Python數(shù)據(jù)結(jié)構(gòu)——二叉堆的實現(xiàn)

    摘要:二叉堆的有趣之處在于,其邏輯結(jié)構(gòu)上像二叉樹,卻是用非嵌套的列表來實現(xiàn)。二叉堆結(jié)構(gòu)性質(zhì)為了更好地實現(xiàn)堆,我們采用二叉樹。圖完全二叉樹有意思的是我們用單個列表就能實現(xiàn)完全樹。下列所示的代碼是完全二叉堆的實現(xiàn)。 優(yōu)先隊列的二叉堆實現(xiàn) 在前面的章節(jié)里我們學(xué)習(xí)了先進(jìn)先出(FIFO)的數(shù)據(jù)結(jié)構(gòu):隊列(Queue)。隊列有一種變體叫做優(yōu)先隊列(Priority Queue)。優(yōu)先隊列的出隊(Dequ...

    stackfing 評論0 收藏0
  • 【閱讀筆記】——什么是二叉

    摘要:構(gòu)建二叉樹構(gòu)建二叉樹,就是把一個無序的完全二叉樹調(diào)整為二叉堆,本質(zhì)上就是讓所有非葉子節(jié)點一次下沉上浮構(gòu)建最大堆節(jié)點大的上浮,小的下沉構(gòu)建最小堆節(jié)點小的上浮,大的下沉文章什么是二叉堆 什么是二叉堆 二叉堆的本質(zhì)是一種完全二叉樹,它分為兩種類型:最大堆和最小堆 最大堆任何一個父節(jié)點的值,都大于等于它左右孩子的值,最小堆正好與之相反 showImg(https://segmentfault....

    big_cat 評論0 收藏0
  • 數(shù)據(jù)結(jié)構(gòu)與算法學(xué)習(xí)筆記 - 優(yōu)先隊列、二叉堆、左式堆

    摘要:模型優(yōu)先隊列是允許至少下列兩種操作的數(shù)據(jù)結(jié)構(gòu)以及找出返回并刪除優(yōu)先隊列中最小的元素。左式堆也是二叉樹,左式堆和二叉堆的唯一區(qū)別是左式堆不是理想平衡,而實際上趨向于非常不平衡。事實上,沿左式堆的右路徑是該堆中的最短路徑。 6.1 模型 優(yōu)先隊列是允許至少下列兩種操作的數(shù)據(jù)結(jié)構(gòu):insert 以及 deleteMin(找出、返回并刪除優(yōu)先隊列中最小的元素)。 insert 操作等價于 en...

    SunZhaopeng 評論0 收藏0
  • 算法筆記-二叉

    摘要:二叉堆的概念二叉堆是一種特殊的二叉樹。二叉堆分為兩種最大堆和最小堆,最大堆的父節(jié)點一定大于其子節(jié)點根節(jié)點最大,最小堆的父節(jié)點小于其子節(jié)點根節(jié)點最小。這是我對二叉堆的理解,如有不對,歡迎指正,我會立即修改,謝謝。 二叉堆的概念 二叉堆是一種特殊的二叉樹。二叉樹是每個節(jié)點只有兩個子節(jié)點的樹(應(yīng)該都懂吧)。二叉堆分為 兩 種:最大堆和最小堆,最大堆的父節(jié)點一定大于其子...

    MrZONT 評論0 收藏0
  • JavaScript數(shù)據(jù)結(jié)構(gòu)與算法(十一)二叉

    摘要:二叉堆數(shù)據(jù)結(jié)構(gòu)是一種特殊的二叉樹,他能高效快速的找出最大值和最小值,常應(yīng)用于優(yōu)先隊列和著名的堆排序算法中。 二叉堆數(shù)據(jù)結(jié)構(gòu)是一種特殊的二叉樹,他能高效、快速的找出最大值和最小值,常應(yīng)用于優(yōu)先隊列和著名的堆排序算法中。 二叉堆 二叉堆有以下兩個特性: 是一顆完全二叉樹,表示數(shù)的每一層都有左側(cè)和右側(cè)子節(jié)點(除最后一層的葉節(jié)點),并且最后一層的葉節(jié)點盡可能是左側(cè)子節(jié)點 二叉堆不是最小堆就是...

    MartinHan 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<