Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Priority Queues, Heap Sort #12

Open
barretlee opened this issue May 27, 2016 · 0 comments
Open

Priority Queues, Heap Sort #12

barretlee opened this issue May 27, 2016 · 0 comments

Comments

@barretlee
Copy link
Owner

排序这块我们已经讨论很多了( #6 #8 #9 #10 ),从最开始基本的冒泡、插入、选择和希尔排序,到分治思想的延伸——归并排序(自顶向下和自底向上),再到归并排序的互补算法——快排,然后学习了新的数据结构——二叉堆,于是有了堆排序。

二叉堆是一种数据结构,他的每一个二叉树点元素数值都会比下面两个节点元素的数值要大,因为这种数据接口包含的信息量很大,而得到这种数据结构的成本是很低的,构建一个二叉堆的算法并不复杂:

/chapters/chapter-2-sorting/2.4-priority-queues/priorityQueueAdd.js

function priorityQueueAdd(input) {
  var output = [];

  output[1] = input[0];
  for(var i = 1, len = input.length; i < len; i++) {
    output = swim(output, input[i]);
  }

  return output;

  function swim(arr, val) {
    arr.push(val);
    var k = arr.length - 1;
    while(k > 1 && arr[k >> 1] < arr[k]) {
      var p = k >> 1;
      arr[p] = [arr[k], arr[k] = arr[p]][0];
      k = p;
    }
    return arr;
  }
}

通过上浮的方式,不断插入新元素,既可形成一个二叉堆。这种优先队列最大的特点是,能够拿到很快拿到最大元素(顶部),当这个最大元素被删除(优先级最高的事务被处理完成)时,还能快速高效地将剩下的元素重整为一个二叉堆:

/chapters/chapter-2-sorting/2.4-priority-queues/priorityQueueDelete.js

function priorityQueueDelete(input) {
  var output = [];

  input.splice(1, 1);
  output = sink(input);

  return output;

  function sink(arr) {
    arr.splice(1, 0, arr.pop());
    var k = 1, N = arr.length - 1;
    while(2 * k <= N) {
      var j = 2 * k;
      if(j < N && arr[j] < arr[j + 1]) j++;
      if(arr[k] >= arr[j]) break;
      arr[k] = [arr[j], arr[j] = arr[k]][0];
      k = j;
    }
    return arr;
  }
}

一个二叉堆能够快速拿到最大元素,并且能够立即重新调整为二叉堆,基于这个特性,就有了堆排序

/chapters/chapter-2-sorting/2.4-priority-queues/heapSort.js

function heapSort(input) {
  return sort(input);

  function sort (arr){
    var N = arr.length - 1;
    for(var k = N >> 2; k >= 1; k--) {
      arr = sink(arr, k, N);
    }
    while(N > 1) {
      arr[1] = [arr[N], arr[N] = arr[1]][0];
      N--;
      arr = sink(arr, 1, N);
    }
    return arr;
  }
  function sink(arr, k, N) {
    while(2 * k <= N) {
      var j = 2 * k;
      if(j < N && arr[j] < arr[j + 1]) j++;
      if(arr[k] >= arr[j]) break;
      arr[k] = [arr[j], arr[j] = arr[k]][0];
      k = j;
    }
    return arr;
  }
}

光看代码还是挺难理解的,脑海中必须有一个数组储存的堆模型。for 循环构造了堆(从 N/2 开始,跳过了所有大小为 1 的堆),注意,这里构造的并不是二叉堆,然后 while 循环将最大的元素 a[1] 和 a[n] 交换位置并修复堆,如此循环直到堆为空。

上面的排序用到的是 sink 方法,而 swim 方法也是可以用于排序算法之中的,这就是对应的下沉排序,感觉有点难理解。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant