• JS 实现最大堆、最小堆(TS)


    Heap)是一种特殊的抽象数据结构(通常以二叉堆 Binary Heap 作为其基本实现),
    可分为“最大堆”和“最小堆”两类,一般会用动态数组作为底层数据结构来模拟完全二叉树。
    二叉堆是利用完全二叉树的特性来维护一组数据及相关操作,一次操纵的时间复杂度一般在O(1) ~ O(logn)之间

    最大堆为例,其会满足一下性质:
    1. 堆中某个节点的域值总是不小于其子节点的域值;
    2. 总是为一颗完全二叉树;

    任一节点(除去根结点)的父结点及其左右子结点(不包括叶子结点)的索引位置满足以下特点:
    假设当前结点索引为 i ,则:
    父结点:(i - 1) / 2(下取整)
    左子结点:i * 2 + 1
    右子结点:i * 2 + 2

    最大堆的代码实现(TS):
    /**
     * 最大堆
     * == 基础版(number,没有做泛型)
     */
    class MaxBinaryHeap {
      private data: number[];
    
      public constructor() {
        this.data = [];
      }
    
      /**
       * 获取元素个数
       * @return {number}
       */
      public getSize(): number {
        return this.data.length;
      }
    
      /**
       * 是否不包含任何元素
       * @return {boolean}
       */
      public isEmpty(): boolean {
        return this.data.length === 0;
      }
    
      /**
       * 获取当前结点的父结点索引
       * @param {number} i - 目标结点的索引
       * @return {number}
       */
      private getParentIndex(i: number): number {
        if (i === 0) {
          throw new Error('Root element(index-0) doesn\'t has parent!')
        }
        return Math.floor((i - 1) / 2);
      }
    
      /**
       * 获取其左子结点的索引
       * @param {number} i - 目标结点的索引 
       * @return {number} 
       */
      private getLeftChildIndex(i: number): number {
        return i * 2 + 1;
      }
    
      /**
       * 获取其右子结点的索引
       * @param {number} i - 目标结点的索引 
       * @return {number} 
       */
      private getRightChildIndex(i: number): number {
        return i * 2 + 2;
      }
    
      /**
       * 向堆中添加新元素
       * @param {number} el - 新添加的元素
       */
      public add(el: number): void {
        // 先将新元素添加到尾部
        this.data.push(el);
        // 再将该元素上浮到合适的位置
        this.siftUp(this.getSize() - 1);
      }
    
      /**
       * 查看当前堆中的最大元素
       * @return {number}
       */
      public findMax(): number {
        if (this.getSize() === 0) {
          throw new Error('Failed to execute, Heap is Empty!');
        }
        return this.data[0];
      }
    
      /**
       * 取出当前堆中的最大值
       * @return {number}
       */
      public extractMax(): number {
        const max = this.findMax();
        // 交换堆顶和堆尾元素
        this.swap(0, this.getSize() - 1);
        // 将当前最大元素移出堆
        this.data.pop();
        // 调整好堆
        this.siftDown(0);
    
        return max;
      }
    
      /**
       * 上浮当前元素到合适的位置
       * @param {number} i - 特定元素的位置
       */
      private siftUp(i: number): void {
        const { data, getParentIndex } = this;
        while (i > 0 && data[i] > data[getParentIndex(i)]) {
          const parentIdx = getParentIndex(i);
          this.swap(i, parentIdx)
          i = parentIdx;
        }
      }
    
      /**
       * 下沉当前元素到合适的位置
       * @param {number} i - 特定元素的位置
       */
      private siftDown(i: number): void {
        const { data, getLeftChildIndex, getRightChildIndex } = this;
        const size = this.getSize();
        // 如果当前结点不是叶子结点
        //(如果是叶子结点,其左子结点是不存在,另外其左子索引即使存在也将越界)
        while (getLeftChildIndex(i) < size) {
          let j = getLeftChildIndex(i);
          // 如果左子结点存在,且其右子结点比左子结点要大,则更新j
          if (j + 1 < size && data[j + 1] > data[j]) {
            j = getRightChildIndex(i);
          }
          // 如果当前结点不小于左右子结点中的更大者,则中断(已经满足堆性质)
          if (data[i] >= data[j]) break;
          this.swap(i, j);
          i = j;
        }
      }
    
      /**
       * 交换两个位置上的元素
       * @param {number} i - 待交换结点A
       * @param {number} j - 待交换结点B
       */
      private swap(i: number, j: number): void {
        const size = this.getSize();
        if (i < 0 || j < 0 || i >= size || j >= size) {
          throw new RangeError('Index is not valid!');
        }
        const data = this.data;
        const t = data[i];
        data[i] = data[j];
        data[j] = t;
      }
    
      /**
       * 将数据以字符串的形式输出展示
       * @return {string}
       */
      public toString(): string {
        return '[' + this.data.toString() + ']';
      }
    }
    
    const h = new MaxBinaryHeap();
    h.add(0);
    h.add(5);
    h.add(1);
    h.add(4);
    
    console.log(h.toString());
    console.log(h.extractMax());
    console.log(h.extractMax());
    console.log(h.extractMax());
    console.log(h.extractMax());
    console.log(h.toString());
    
    // 输出如下:
    // [5,4,1,0]
    // 5
    // 4
    // 1
    // 0
    // []

    最小堆的代码实现(TS):

    class MinBinaryHeap {
        private data: number[];
        constructor() {
            this.data = [];
        }
        public getSize(): number {
            return this.data.length;
        }
        public isEmpty(): boolean {
            return this.data.length === 0;
        }
        private getParentIndex(i: number): number {
            if (i <= 0) {
                throw new RangeError('Root element has no parent!')
            }
            return Math.floor((i - 1) / 2);
        }
        private getLeftChildIndex(i: number): number {
            return i * 2 + 1;
        }
        private getRightChildIndex(i: number): number {
            return i * 2 + 2;
        }
        public findMin(): number {
            if (this.getSize() === 0) {
                throw new Error('Empty heap!');
            }
            return this.data[0];
        }
        public add(el: number): void {
            this.data.push(el);
            this.siftUp(this.getSize() - 1);
        }
        private siftUp(k: number): void {
            const { data, getParentIndex } = this;
            while (k > 0 && data[getParentIndex(k)] > data[k]) {
                const parentIdx = getParentIndex(k);
                this.swap(k, parentIdx);
                k = parentIdx;
            }
        }
        public extractMin(): number {
            const min = this.findMin();
            this.swap(0, this.getSize() - 1);
            this.data.pop();
            this.siftDown(0);
            return min;
        }
        private siftDown(k: number): void {
            const { data, getLeftChildIndex, getRightChildIndex } = this;
            const size = this.getSize();
            while (getLeftChildIndex(k) < size) {
                let i = getLeftChildIndex(k);
                if (i + 1 < size && data[i] > data[i + 1]) {
                    i = getRightChildIndex(i);
                }
                if (data[k] <= data[i]) {
                    break;
                }
                this.swap(k, i);
                k = i;
            }
        }
        private swap(i: number, j: number): void {
            const { data } = this;
            const t = data[i];
            data[i] = data[j];
            data[j] = t;
        }
    }

    需要转为JS版,可以将代码粘贴在此处:

    https://www.typescriptlang.org/play?#code/Q

  • 相关阅读:
    用户访问集群架构的流程
    HTTP请求方法 HTTP的响应方法
    数据报文
    什么是HTTP? 什么是超文本? 什么是URL?
    HTTP协议原理
    C语言之数据类型③——字符与字符串
    C语言之数据类型②——浮点类型
    C语言之数据类型①——整数类型
    uniapp自定义小程序左上角的图标并且添加自定义事件
    在uniapp中使用iconfont
  • 原文地址:https://www.cnblogs.com/fanqshun/p/16477994.html
Copyright © 2020-2023  润新知