本文主要讲解堆排序的原理以及代码实现,要理解堆排序,首先需要了解二叉树,下面我们逐步展开
树
- 树是一种数据结构
- 树是由有限个节点(可以理解位一个数据)组成的集合
- 树的每个节点最多只能有一个父节点,可以有多个子节点
依据这三条,可以将树画成图,如图所示
因其图像就像一棵倒挂的树,所以称为树
二叉树
任意节点最多只有2个子节点的树就是二叉树,上面的图形就不是一棵二叉树
以下就是一棵二叉树
完全二叉树
来自百度百科的定义如下:
一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。
如何理解?
从以数组表示二叉树的结构上看,数组内部没有空洞
从图形上看子节点填充按照从左往右的方式排布
堆
堆严格意义上讲叫二叉堆,因为它可以看做一棵近似的完全二叉树
最大堆
一棵完全二叉树,任何节点,都满足左子节点和右子节点的值都小于该节点的值
如上图所示,取任意一个节点,例如15这个节点,既大于左子节点10,又大于右子节点3,而且其他节点也是满足这种关系的,所以这就可以称为最大堆。
堆排序实现原理
堆排序是基于最大堆实现的,首先先建一个最大堆,那么第一个元素就是所有元素里最大的值,将这个值,与最后一个值交换,这时候最大堆性质没法保持,那么为了保持最大堆,第一个值逐级下沉,即分别与左右子节点进行比较,直至再次保持为最大堆。接着将第一个值与倒数第二个值交换,继续保持最大堆,以此类推。
代码实现如下
export function maxHeapSort<T>(maxHeap: T[], compFunc: (A: T, B: T) => number) {
let heapSize: number = 0;
function buildHeap() {
heapSize = maxHeap.length;
for(let num = (heapSize - 1) >> 1, i = num; i >= 0; i--) {
maxHeapify(i);
}
}
function left(idx: number) {
return (idx << 1) + 1;
}
function right(idx: number) {
return (idx << 1) + 2;
}
function parent(idx: number) {
return (idx - 1) >> 1;
}
function swap(idx1: number, idx2: number) {
let tmp = maxHeap[idx1];
maxHeap[idx1] = maxHeap[idx2];
maxHeap[idx2] = tmp;
}
function maxHeapify(idx: number) {
let leftIdx = left(idx);
let largestIdx: number;
if(leftIdx < heapSize && compFunc(maxHeap[idx], maxHeap[leftIdx]) < 0)
largestIdx = leftIdx;
else
largestIdx = idx;
let rightIdx = right(idx);
if(rightIdx < heapSize && compFunc(maxHeap[largestIdx], maxHeap[rightIdx]) < 0)
largestIdx = rightIdx;
if(largestIdx != idx) {
swap(largestIdx, idx);
maxHeapify(largestIdx);
}
}
buildHeap();
for(let i = maxHeap.length - 1; i > 0; i--) {
swap(0, i);
heapSize--;
maxHeapify(0);
}
return maxHeap;
}