堆排序

堆的定义

堆(二叉堆)可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示(普通的一般的二叉树通常用链表作为基本容器表示),每一个结点对应数组中的一个元素。
如下图,是一个堆和数组的相互关系

Paste_Image.png

父子节点的运算关系

对于给定的某个结点的下标 i,可以很容易的计算出这个结点的父结点、孩子结点的下标:
Parent(i) = floor(i/2),i 的父节点下标
Left(i) = 2i,i 的左子节点下标
Right(i) = 2
i + 1,i 的右子节点下标

最大堆最小堆

二叉堆一般分为两种:最大堆和最小堆。

最大堆中的最大元素值出现在根结点(堆顶)
堆中每个父节点的元素值都大于等于其孩子结点(如果存在)

最大堆
最小堆中的最小元素值出现在根结点(堆顶)
堆中每个父节点的元素值都小于等于其孩子结点(如果存在)

堆排序

堆排序就是把最大堆堆顶的最大数与末尾的数交换,然后将末尾的数排除大根堆。然后将剩余的堆继续调整为最大堆,再次将堆顶的最大数与末尾的数交换,这个过程持续到剩余数只有一个时结束。在堆中定义以下几种操作:

基本操作

  • Bottom-up reheapify (swim).* If the heap order is violated because a node's key becomes larger than that node's parents key, then we can make progress toward fixing the violation by exchanging the node with its parent. After the exchange, the node is larger than both its children (one is the old parent, and the other is smaller than the old parent because it was a child of that node) but the node may still be larger than its parent. We can fix that violation in the same way, and so forth, moving up the heap until we reach a node with a larger key, or the root.
Paste_Image.png
private void swim(int k) {
   while (k > 1 && less(k/2, k)) {
      exch(k, k/2);
      k = k/2;
   }
}
  • Top-down heapify (sink).* If the heap order is violated because a node's key becomes smaller than one or both of that node's children's keys, then we can make progress toward fixing the violation by exchanging the node with the larger of its two children. This switch may cause a violation at the child; we fix that violation in the same way, and so forth, moving down the heap until we reach a node with both children smaller, or the bottom.
Paste_Image.png
private void sink(int k) {
   while (2*k <= N) {
      int j = 2*k;
      if (j < N && less(j, j+1)) j++;
      if (!less(k, j)) break;
      exch(k, j);
      k = j;
   }
}
  • Insert. We add the new item at the end of the array, increment the size of the heap, and then swim up through the heap with that item to restore the heap condition.
  • Remove the maximum. We take the largest item off the top, put the item from the end of the heap at the top, decrement the size of the heap, and then sink down through the heap with that item to restore the heap condition.
    Paste_Image.png

实现

分为两步,先建堆再进行下沉堆排序。
建堆过程是从末尾节点的孩子开始进行下沉(sink)操作,依次执行直到头结点,此时建成大根堆。
堆排序则是将头结点和尾节点交换,将尾节点从堆中删除,然后对头结点进行下沉操作。

Paste_Image.png
public void heapSort(int[] array, int n) {
    //建堆过程
    for(int i=(array.length-1)/2;i>=0;i--){
        sink(array,i,array.length-1);
    }   
    for(int i=n-1;i>0;i--){
        swap(array,0,i);
        sink(array,0,i-1);
    }   
}
    
private void sink(int[] array,int lo,int hi){
    while(lo*2+1<=hi){
        int left=lo*2+1;
        if(left<hi&&array[left]<array[left+1]) left++;
        if(array[lo]>=array[left])break;
        swap(array,left,lo);
        lo=left;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 关于最大堆 什么是最大堆和最小堆?最大(小)堆是指在树中,存在一个结点而且该结点有儿子结点,该结点的data域值都...
    凌云壮志几多愁阅读 88,377评论 33 71
  • 堆排序的时间复杂度与归并排序相同为O(nlg n),空间复杂度与插入排序相同为O(1)。堆这种数据结构还用于优先队...
    Nautilus1阅读 942评论 1 0
  • 堆排序与快速排序[https://www.jianshu.com/p/2cc8fc1c878f],归并排序[htt...
    爱情小傻蛋阅读 1,159评论 1 7
  • 今天,有机会和朋友一起去了古中山国遗址博物馆。 一路上朋友的车很快,一路狂飙的爽。一段高速,两边秋草枯黄,朔风野大...
    心如美玉阅读 216评论 2 1
  • 黑夜,只不过是上帝用黑色的纱遮住了天空的眼眸,忘了揭开罢了。 夜空一片漆黑,就像人的黑色的眼眸一般深沉...
    子巷阅读 362评论 0 2