數據結構 - 堆

(1)什麼是二叉堆

二叉堆本質上是一種完全二叉樹,二叉堆的經典表示方法是使用一個數組表示,其中:

  • 根結點爲數組的第一個元素A[0]。
  • 其它結點中,第i個結點和數組索引元素對應的關係爲:
A[(i – 1) / 2] 返回第i個結點的父結點
A[(2 * i) + 1] 返回第i個結點的左兒子結點
A[(2 * i) + 2] 返回第i個結點的右兒子結點

它分爲兩個類型:

  • 最大堆

    最大堆任何一個父節點的值,都大於等於它左右孩子節點的值。

  • 最小堆

    最小堆任何一個父節點的值,都小於等於它左右孩子節點的值。

假設父節點的下標是parent,那麼它的左孩子下標就是 2*parent+1;它的右孩子下標就是 2*parent+2

(2)二叉堆的主要操作

  • 堆的構建

    大頂堆

    //從第一個非葉子節點開始依次對數組中的元素進行下沉操作
    //和孩子節點的最大值max比較
    //大於max — 不需要在下沉
    //小於max — 和max交換位置 - 繼續和下一層孩子節點比較,直到隊列末尾
    function ajustMaxHeap(array, index, length) {
          for (let i = 2 * index + 1; i < length; i = 2 * i + 1) {
            if (i + 1 < length && array[i + 1] > array[i]) {
              i++;
            }
            if (array[index] >= [array[i]]) {
              break;
            } else {
              [array[index], array[i]] = [array[i], array[index]];
              index = i;
            }
          }
        }
    
        function createMaxHeap(arr, length) {
          for (let i = Math.floor(length / 2) - 1; i >= 0; i--) {
            ajustMaxHeap(arr, i, length);
          }
          return arr;
        }
    

    小頂堆

    //從第一個非葉子節點開始依次對數組中的元素進行下沉操作
    //和孩子節點的最小值min比較
    //小於min — 不需要在下沉
    //大於min — 和min交換位置(下沉) - 繼續和下一層孩子節點比較,直到隊列末尾
    function ajustMinHeap(array, index, length) {
          for (let i = 2 * index + 1; i < length; i = 2 * i + 1) {
            if (i + 1 < length && array[i + 1] < array[i]) {
              i++;
            }
            if (array[index] < [array[i]]) {
              break;
            } else {
              [array[index], array[i]] = [array[i], array[index]];
              index = i;
            }
          }
        }
    
        function createMinHeap(arr, length) {
          for (let i = Math.floor(length / 2) - 1; i >= 0; i--) {
            ajustMinHeap(arr, i, length);
          }
          return arr;
        }
    
  • 堆的插入

    //由於堆屬於優先隊列,只能從末尾添加
    //添加後有可能破壞堆的結構,需要從下到上進行調整
    //如果元素小於父元素,上浮
    //以小頂堆爲例:
    function minHeapAdd(array = [], element) {
          array.push(element);
          if (array.length > 1) {
            let index = array.length - 1;
            let target = Math.floor((index - 1) / 2);
            while (target >= 0) { array[target]);
              if (array[index] < array[target]) {
                [array[index], array[target]] = [array[target], array[index]]
                index = target;
                target = Math.floor((index - 1) / 2);
              } else {
                break;
              }
            }
          }
          return array;
        }
    
  • 堆的移除

    //由於堆屬於優先隊列,只能從頭部移除
    //移除頭部後,使用末尾元素填充頭部,開始頭部下沉操作
    //以小頂堆爲例:
    function minHeapPop(array = []) {
          let result = null;
          if (array.length > 1) {
            result = array[0];
            array[0] = array.pop();
            ajustMinHeap(array, 0, array.length);
          } else if (array.length === 1) {
            return array.pop();
          }
          return result;
        }
    
  • 封裝

    class Heap {
      constructor(arr, type) {
        this.data = [...arr];
        this.type = type;
        this.size = this.data.length;
      }
        
      create() {
          for (let i = Math.floor((this.size / 2) - 1); i >= 0; i--) {
            this.ajust(i);
          } 
      }
        
      ajust (index) {
          const arr = this.data;
          for (let i = 2 * index + 1; i < this.size; i = 2 * i + 1) {
            if (i + 1 < this.size) {
              if ((this.type === 'max' && arr[i + 1] > arr[i]) ||
                (this.type === 'min' && arr[i + 1] < arr[i])) {
                i++;
              }
            }
            if ((this.type === 'max' && arr[index] < [arr[i]]) ||
              (this.type === 'min' && arr[index] > [arr[i]])) {
              [arr[index], arr[i]] = [arr[i], arr[index]];
              index = i;
            } else {
              break;
            }
          }
        }
        
        add (element) {
          const arr = this.data;
          arr.push(element);
          if (this.size > 1) {
            let index = this.size - 1;
            let target = Math.floor((index - 1) / 2);
            while (target >= 0) {
              if ((this.type === 'min' && arr[index] < arr[target]) ||
                (this.type === 'max' && arr[index] > arr[target])) {
                [arr[index], arr[target]] = [arr[target], arr[index]]
                index = target;
                target = Math.floor((index - 1) / 2);
              } else {
                break;
              }
            }
          }
        }
        
        pop () {
          const arr = this.data;
          let result = null;
          if (this.size > 1) {
            result = arr[0];
            arr[0] = arr.pop();
            this.ajust(0, this.size);
          } else if (this.size === 1) {
            return arr.pop();
          }
          return result;
        }
        
    }
    
        var heap = new Heap('max');
        heap.add(6)
        heap.add(10)
        console.log(heap.value);
        console.log(heap.pop());
        console.log(heap.value);
    

JavaScript中二叉樹(二叉堆)的介紹(代碼示例)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章