// 二叉堆
// 父亲节点大于等于 子节点
// 是一颗完全二叉树
function swap(arr, x, y) {
var temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
class MaxHeap{
constructor(capacity) {
if(arguments.length > 1 ){
// 这是参数是 arr , n
this.capacity = arguments[1];
this.Item = [''].concat(arguments[0])
this.count = arguments[1];
for(var i=Math.floor(this.count/2); i>=1; i--) {
this.shiftDown(i);
}
} else {
this.Item = new Array(capacity+1);;
this.count = 0;
this.capacity = capacity;
}
}
// 返回 数据的大小
size() {
return this.count;
}
// 判断Item是否是 空的
isEmpty() {
return this.count == 0;
}
// 添加新元素
insert (item) {
// 这个可能会 数组越界
// 但是js自动可以添加
this.Item[this.count+1] = item;
this.count++;
this.shiftUp(this.count)
}
shiftUp(count) {
// 判断父节点 是否比自身节点 大
while( count > 1 && this.Item[Math.floor(count/2)] < this.Item[count]) {
swap(this.Item, Math.floor(count/2), count );
count = Math.floor(count/2);
}
}
// 出堆
extractMax() {
if( this.count > 0){
var x = this.Item[1];
this.Item[1] = this.Item.pop();
this.count--;
this.shiftDown(1)
return x;
}
return null;
}
shiftDown(k) {
while( 2*k <= this.count) {
var j = 2*k;
//如果右节点 存在且大于左节点 则j = j+1
if( j+1 <= this.count && this.Item[j+1] > this.Item[j]) {
j = j+1;
}
// 如果父节点 大于子节点最大值 则跳出循环
if( this.Item[k] > this.Item[j]) {
break;
}
swap(this.Item, k, j);
k=j;
}
}
}
// 堆排序算法
// 将n个元素逐个插入到一个空堆中,算法复杂度是O(nlongn)
function heapSort(arr, n) {
var max = new MaxHeap(n);
for(var i=0; i<n; i++) {
max.insert(arr[i]);
}
for(var i=n-1; i>=0; i--) {
arr[i] = max.extractMax();
}
}
// 而 heapify 的过程, 算法复杂度是O(n)
// 就是构造函数中有两个参数的例子
function heapSort2(arr, n) {
var max2 = new MaxHeap(arr, n);
for(var i=n-1; i>=0; i--) {
arr[i] = max2.extractMax();
}
}
// 原地堆 排序
function __shiftDown(arr, n, i) {
while( 2*i+1 < n) {
var j = 2*i+1;
//如果右节点 存在且大于左节点 则j = j+1
if( j+1 < n && arr[j+1] > arr[j]) {
j = j+1;
}
// 如果父节点 大于子节点最大值 则跳出循环
if( arr[i] > arr[j]) {
break;
}
swap(arr, i, j);
i=j;
}
}
function heapSort3(arr, n) {
for(var i= Math.floor((n-1)/2); i>=0; i--)
__shiftDown(arr, n, i)
for(var i= n-1; i>0; i--) {
swap(arr, i, 0)
__shiftDown(arr, i, 0)
}
return arr;
}
//生成一个数组
function generate(n, rangeL, rangR){
var arr = []
for(var i=0; i<n; i++){
var x = Math.floor(Math.random() * (rangR -rangeL+1)) + rangeL;
arr.push(x);
}
return arr;
}
// 生成一个近乎有序的数组
function getNearlyArray(arr, swaptimes) {
for(var i=0;i<swaptimes; i++) {
var x = Math.floor(Math.random() * arr.length);
var y = Math.floor(Math.random() * arr.length);
swap(arr, x, y);
}
}
// 随机的 100000个数组 排序的耗时
var n = 100000;
var arr = generate(n, 0, n)
var arr2 = [].concat(arr);
var arr3 = [].concat(arr);
var start = new Date();
heapSort(arr, n)
var end = new Date();
console.log("heapSort用时:" + (end - start) + "毫秒")
var start2 = new Date();
heapSort2(arr2, n)
var end2 = new Date();
console.log("heapSort2用时:" + (end2 - start2) + "毫秒")
var start3 = new Date();
heapSort3(arr3, n)
var end3 = new Date();
console.log("heapSort3用时:" + (end3 - start3) + "毫秒")
//稳定排序 对于相等的元素 在排序后,原来靠前的元素依然靠前