博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
堆排序
阅读量:5923 次
发布时间:2019-06-19

本文共 3204 字,大约阅读时间需要 10 分钟。

  科班出身的程序员就应该研究些算法和数据结构类的东西,不然,有什么优势?

  堆排序,结构是完全二叉树,选择排序的一种,其流程控制和冒泡排序类似,每次选出一个最大(或最小的元素)排出去,然后下一轮再选出一个最大(最小的),以此类推,直到剩下一个不能构成二叉树为止也排出去,排出来的就是有序的了。只不过每次选极值元素不是对所有元素都对比,而是心中模拟有一棵完全二叉树,每次选出最大顶(最小顶)。

  

原理解析:

  给定一个列表array=[16,7,3,20,17,8],对其进行堆排序。

  首先根据该数组元素构建一个完全二叉树,得到

  然后需要构造初始堆,则从最后一个非叶节点开始调整,调整过程如下:
  第一步: 初始化大顶堆(从最后一个有子节点开始往上调整最大堆)

  20和16交换后导致16不满足堆的性质,因此需重新调整

  这样就得到了初始堆。

  第二步: 堆顶元素R[1]与最后一个元素R[n]交换,交换后堆长度减一

  即每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)。有了初始堆之后就可以进行排序了。

  第三步: 重新调整堆。此时3位于堆顶不满堆的性质,则需调整继续调整(从顶点开始往下调整)

  重复上面的步骤:

  注意了,现在你应该了解堆排序的思想了,给你一串列表,你也能写出&说出堆排序的过程。

  在写算法的过程中,刚开始我是很懵比。后来终于看懂了。请特别特别注意: 初始化大顶堆时 是从最后一个有子节点开始往上调整最大堆。而堆顶元素(最大数)与堆最后一个数交换后,需再次调整成大顶堆,此时是从上往下调整的。

  不管是初始大顶堆的从下往上调整,还是堆顶堆尾元素交换,每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换,交换之后都可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整。我在算法中是用一个while循环来解决的

参考代码:

1 public class HeapSort { 2     public void HeapAdjust(int[] array, int parent, int length) { 3         int temp = array[parent]; // temp保存当前父节点 4         int child = 2 * parent + 1; // 先获得左孩子 5         while (child < length) { 6             // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点 7             if (child + 1 < length && array[child] < array[child + 1]) { 8                 child++; 9             }10             // 如果父结点的值已经大于孩子结点的值,则直接结束11             if (temp >= array[child])12                 break;13             // 把孩子结点的值赋给父结点14             array[parent] = array[child];15             // 选取孩子结点的左孩子结点,继续向下筛选16             parent = child;17             child = 2 * child + 1;18         }19         array[parent] = temp;20     }21     public void heapSort(int[] list) {22         // 循环建立初始堆23         for (int i = list.length / 2; i >= 0; i--) {24             HeapAdjust(list, i, list.length);25         }26         // 进行n-1次循环,完成排序27         for (int i = list.length - 1; i > 0; i--) {28             // 最后一个元素和第一元素进行交换29             int temp = list[i];30             list[i] = list[0];31             list[0] = temp;32             // 筛选 R[0] 结点,得到i-1个结点的堆33             HeapAdjust(list, 0, i);34             System.out.format("第 %d 趟: \t", list.length - i);35             printPart(list, 0, list.length - 1);36         }37     }38     // 打印序列39     public void printPart(int[] list, int begin, int end) {40         for (int i = 0; i < begin; i++) {41             System.out.print("\t");42         }43         for (int i = begin; i <= end; i++) {44             System.out.print(list[i] + "\t");45         }46         System.out.println();47     }48     public static void main(String[] args) {49         // 初始化一个序列50         int[] array = { 16, 7, 3, 20, 17, 8 };51         // 调用堆排序方法52         HeapSort heap = new HeapSort();53         System.out.print("排序前:\t");54         heap.printPart(array, 0, array.length - 1);55         heap.heapSort(array);56         System.out.print("排序后:\t");57         heap.printPart(array, 0, array.length - 1);58     }59 }

时间复杂度:

  堆的存储表示是顺序的。因为堆所对应的二叉树为完全二叉树,而完全二叉树通常采用顺序存储方式。

  当想得到一个序列中第k个最小的元素之前的部分排序序列,最好采用堆排序。

  因为堆排序的时间复杂度是O(n+klog2n),若k≤n/log2n,则可得到的时间复杂度为O(n)

 算法稳定性:

  堆排序是一种不稳定的排序方法。

  因为在堆的调整过程中,关键字进行比较和交换所走的是该结点到叶子结点的一条路径,

  因此对于相同的关键字就可能出现排在后面的关键字被交换到前面来的情况。 

最后看一下排序家族

 

 

转载地址:http://vfavx.baihongyu.com/

你可能感兴趣的文章
走进webpack(2)--第三方框架(类库)的引入及抽离
查看>>
[译] 所有你需要知道的关于完全理解 Node.js 事件循环及其度量
查看>>
脚本监控网络状态,输出日志并归档(V2)
查看>>
(六十九)复合语句
查看>>
Cisco ASA 5505配置详解
查看>>
我的友情链接
查看>>
java读取某个文件夹下的所有文件
查看>>
设计模式:装饰者
查看>>
CentOS minimal+VirtualBox 设置桥接DHCP网络连接
查看>>
OC高效率52之不要使用dispatch_get_current_queue
查看>>
Quartz2D
查看>>
栈与queue
查看>>
对于java我的看法
查看>>
Java Web中实现Servlet的方式
查看>>
74LVC245AD技术资料
查看>>
第三方库之 - SVProgressHUD
查看>>
struts-自定义标签
查看>>
ZHYcms开源内容管理系统源码阅读
查看>>
11个让你吃惊的 Linux 终端命令
查看>>
Caused by: java.lang.IllegalArgumentException: error at ::0 can't find referenced pointcut
查看>>