Skip to content
New issue

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

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

Already on GitHub? # 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 方法也是可以用于排序算法之中的,这就是对应的下沉排序,感觉有点难理解。

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

No branches or pull requests

1 participant