Java排序算法总结之(三)——选择排序(简单选择排序、堆排序)

来源:互联网 发布:证书制作软件 编辑:程序博客网 时间:2024/05/29 10:03

排序方法可以分为两种:内部排序 和 外部排序

内部排序的方法很多,常用的大致可以分为:

  1. 插入排序(直接插入排序、折半插入排序、希尔排序)
  2. 交换排序(冒泡排序、快速排序)
  3. 选择排序(简单选择排序、堆排序)
  4. 归并排序
  5. 基数排序

选择排序

1.简单选择排序

基本思想:对数列的 n 个元素进行比较,选出最小(或者最大)的元素,与起始位置的值交换,再从余下的数据中找到最小(最大)值,放在已排序部分的末尾,直到排序完毕。

示例:选择排序的示例动画。红色表示当前最小值,黄色表示已排序序列,蓝色表示当前位置。

            

Java代码:(注意与冒泡排序的区别)

public class SelectSort {public static int[] sort(int[] s) {for (int i = 0; i < s.length - 1; i++) {for (int j = i + 1; j < s.length; j++) {if (s[i] > s[j]) {int temp = s[i];s[i] = s[j];s[j] = temp;}}}return s;}}
效率分析:

交换操作介于0和(n-1)次之间

选择排序的比较操作为n(n-1)/2次之间

选择排序的赋值操作介于0和3(n-1)次之间。总的时间复杂度为O(n^2)
稳定性:true,相等的值的相对位置不会发生改变。

2.堆排序

堆排序(Heapsort)是指利用 堆 这种数据结构所设计的一种排序算法。
堆 分为最大堆 和 最小堆,定义为子结点的键值或索引总是小于(或者大于)它的父节点
通常堆是通过一维数组来实现的。在Java中,数组起始位置为0,访问结点的计算方法为:
  • 父节点i的左子节点在位置(2*i+1);
  • 父节点i的右子节点在位置(2*i+2);
  • 子节点i的父节点在位置floor((i-1)/2);
排序常用最大堆来进行。
堆可以用完全二叉树来表示,又称二叉堆。任何一个数组都可以用堆来表示
         
使用堆排序,我们需要做三件事:
                    
图片来源:白话经典算法系列之七 堆与堆排序

  1. 创建最大堆:对数组中所有数据进行重排,使其满足最大堆的定义;
  2. 最大堆调整:创建最大堆时需要随时对已排好的最大堆进行重排,
  3. 堆排序:移除位于第一个数据的根节点,将最后的元素补到第一位,然后再进行调整
public class HeapSort {public static void buildHeap(int[] s) {// 叶子结点可以看成一个堆,故建堆从最后一个父节点开始int startIndex = getParentIndex(s.length - 1);// 循环添加结点,每添加一个,就要对当前堆进行调整,使其满足最大堆for (int index = startIndex; index >= 0; index--) {maxHeapAdjust(s, s.length, index);}}/** * 最大堆调整函数 *  * @param s  * @param heapSize  * @param parentIndex  */private static void maxHeapAdjust(int[] s, int heapSize, int parentIndex) {// 计算传入结点索引的左右子结点int leftChildIndex = getLeftChildIndex(parentIndex);int rightChildIndex = getRightChildIndex(parentIndex);// 当前结点与左右结点比较,得到对应最大值的索引int max = parentIndex;if (leftChildIndex < heapSize && s[max] < s[leftChildIndex]) {max = leftChildIndex;}if (rightChildIndex < heapSize && s[max] < s[rightChildIndex]) {// 注意:此处不能写成s[parentIndex]max = rightChildIndex;}// 如果最大值不是当前结点,那么需要交换。交换后,其子结点可能不是最大堆,需要重新调整,递归if (max != parentIndex) {int temp = s[parentIndex];s[parentIndex] = s[max];s[max] = temp;maxHeapAdjust(s, heapSize, max);}}public static void heapSort(int[] s) {// 最大堆的根结点为最大值,每次与数组最后一位交换,即可获得最大值,该值不再参与堆调整// 由于交换操作破坏了最大堆结构,需要再次调整,长度需要减一// 由于之前已经是最大堆,目前只有第一个值可能不满足,调整时传入的parentIndex = 0即可for (int i = s.length - 1; i > 0; i--) {int temp = s[0];s[0] = s[i];s[i] = temp;maxHeapAdjust(s, i, 0);}}private static int getLeftChildIndex(int index) {return (index << 1) + 1;}private static int getRightChildIndex(int index) {return (index << 1) + 2;}private static int getParentIndex(int index) {return (index - 1) >> 1;}}
效率分析:
堆排序的平均时间复杂度为O(nlogn),空间复杂度为O(1)。
稳定性:false.

参考文献:

选择排序-Wikipedia


0 0
原创粉丝点击