数据结构与算法之堆: 实现最大堆类的两种方式及实现堆排序 (Typescript版)

实现最大堆类的两种方式及两种不同的排序

1 )方案一:参考最小堆类实现最大堆类及实现排序

class MaxHeap {
    heap: number[] = [];
    // 交换节点位置
    swap(i, j) {
        [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
    }
    // 获得父节点
    getParentIndex(i) {
        return (i - 1) >> 1;
    }
    // 获取左子节点
    getLeftIndex(i) {
        return (i << 1) + 1; // 极客写法
    }
    // 获取右子节点
    getRightIndex(i) {
        return (i << 1) + 2;
    }
    // 向上移动
    shiftUp(index) {
        // 如果到了堆顶元素,index是0,则不要再上移了
        if(!index) return;
        const parentIndex = this.getParentIndex(index)
        if(this.heap[parentIndex] < this.heap[index]) {
            this.swap(parentIndex, index)
            this.shiftUp(parentIndex)
        }
    }
    // 下移
    shiftDown(index) {
        // 边界1:如果到了堆尾元素,则不要再下移了
        if(index >= this.heap.length - 1) return;
        const size = this.size();
        const leftIndex = this.getLeftIndex(index);
        const rightIndex = this.getRightIndex(index);
        if (leftIndex < size && this.heap[leftIndex] > this.heap[index]) {
            this.swap(leftIndex, index);
            this.shiftDown(leftIndex);
        }
        if (rightIndex < size && this.heap[rightIndex] > this.heap[index]) {
            this.swap(rightIndex, index);
            this.shiftDown(rightIndex);
        }
    }
    // 插入
    insert(value) {
        this.heap.push(value);
        this.shiftUp(this.heap.length - 1);
    }
    // 删除堆顶
    pop() {
        // pop()方法删除数组最后一个元素并返回,赋值给堆顶
        if (this.heap.length <= 1) return;
        this.heap[0] = this.heap.pop();
        // 对堆顶重新排序
        this.shiftDown(0);
    }
    // 获取堆顶
    peak() {
        return this.heap[0];
    }
    // 获取堆的大小
    size() {
        return this.heap.length;
    }
    sort(arr) {
    	// 1. 先构建一个最小堆
    	arr.forEach((n) => {
    		this.insert(n);
    	});
    	// 2. 开始依次排序,注意这个边界, pop不能是1,否则会造成死循环
    	const result = [];
    	while(this.heap.length > 1) {
    		result.push(this.peak());
    		this.pop();
    	}
    	// 3. 处理最后一个
    	result.push(this.peak());
    	this.heap = result;
    	return result;
    }
}

const mh = new MaxHeap();
mh.sort([1, 10, 9, 5, 3, 11, 3, 9, 11, 13, 1, -1, 98]);
console.log(mh.heap); // [98, 13, 11, 11, 10, 9, 9, 5, 3, 3, 1, 1, -1]
  • 实现最小堆类参考:https://blog.csdn.net/Tyro_java/article/details/133468244
  • 这里排序需要注意的是:先构建的最小堆,并不是排好序的,只是最小值在堆顶,O(logn)
  • 循环中每次把最小值取出来, 注意pop下移操作时的边界问题,O(nlogn)

2 )方案二:另一种方式实现最大堆类及实现排序

class MaxHeap {
  heap: number[] = []
  // 交换两个元素
  static swap (arr, i, j) {
    if (i === j) return;
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
  // 构建最大堆的过程,递归
  static maxHeapify (Arr, i, size) {
    // 左节点(索引)
    const l = (i << 1) + 1
    // 右节点
    const r = (i << 1) + 2
    let largest = i;
    // 父节点i和左节点l做比较取最大
    if (l <= size && Arr[l] < Arr[largest]) largest = l;
    // 右节点和最大值比较
    if (r <= size && Arr[r] < Arr[largest]) largest = r;
    if (largest !== i) {
      MaxHeap.swap(Arr, i, largest);
      MaxHeap.maxHeapify(Arr, largest, size);
    }
  }
  sort () {
    const iArr = this.heap;
    const n = iArr.length;
    if (n <= 1) return iArr;
    // 这里,从最后一个父节点开始构建最大堆, 因为最后一个父节点到第一个父节点就是一次完整的构建最大堆的过程,完成后堆顶就是最大值
    for (let i = Math.floor(n / 2); i >= 0; i--) {
      MaxHeap.maxHeapify(iArr, i, n); // 这里最后一个参数 n 是完整的堆
    }
    // 减而治之
    for (let j = 0; j < n; j++) {
      MaxHeap.swap(iArr, 0, n - 1 - j); // 堆顶与最后一项互换, 堆顶被破坏, 堆顶变成最后一项
      MaxHeap.maxHeapify(iArr, 0, n - 1 - j - 1); // 这里逐渐缩小堆的规模,注意后面的减1,因为最后一个元素是排好序的最大值
    }
    return iArr
  }
}

const mh = new MaxHeap();
mh.heap = [1, 10, 9, 5, 3, 11, 3, 9, 11, 13, 1, -1, 98]
mh.sort();
console.log(mh.heap); // [98, 13, 11, 11, 10, 9, 9, 5, 3, 3, 1, 1, -1]
  • 这个排序的精髓是:在这个排序里先构建一次最大堆,获取排好序的最大值后,逐渐减而治之

你可能感兴趣的:(Data,Structure,and,Algorithms,算法,leetcode)