简单选择排序和堆排序。他们都属于选择类排序。
- 简单选择排序
- 每趟排序:从无序序列中选择最小(或最大)值。更具体的说,每次从无序序列中选择最小值的位置,然后和无序序列的第一个位置(有序序列临界)进行交换,这样使得有序序列的长度加1,无序序列的长度减1.
- N个元素的简单选择排序需要N-1趟;不是N的原因是长度为1时自然有序,则省去了一趟排序。
- 从n个元素中得到最小值,需要恒定比较n-1次;第一趟排序因为无序序列长度为N,所以比较次数是N-1, 第二趟排序无序序列长度是N-1, 比较次数是N-2, 依次类推,直到无序序列的长度为1为止。
- 堆排序
- 堆是一种数据结构 - 满足一定规则的完全二叉树,因此可以用数组来存储,从而父子之间可以通过下标的转换进行定位。具体的规则以大顶堆为例,1) 父节点大于等于左右子节点,2) 定义是递归的,即左右子树也分别是堆;
- 堆排序主要的基本思想是针对简单排序中每趟排序对n个元素的比较都需要n-1次,即每趟排序都是独立的,没有利用上一趟排序中比较的结果。堆排序主要是为了减少比较次数,基于大小比较之间的传递关系,即如果 A> B, B >C, 则 A > C, 这样则不需要A与C之间的比较,而是直接能够推论出;
- 堆排序主要包括 1) 初始时堆的建立,2) 以及每次输出堆顶找到最大值后,将末尾元素放入堆顶后,对其进行调整使其继续为堆。其实第一步堆的建立也是多次堆调整的过程,从第一个有子节点的节点开始从后向前直到第一个元素为止进行调整;
- 每趟排序是取出堆顶元素作为无序序列的最大值,和无序序列的末尾元素进行交换,然后调整堆顶元素,使其继续为堆;
- 一个巧妙之处是每次将堆顶元素和无序序列末尾元素进行交换,好处 1) 和有序序列临界,从而使有序序列长度加1,无序序列长度减1; 2) 无序序列的末尾元素一定不存在无序序列中的子节点,因此可以直接挪到堆顶,而不需要考虑子节点如何处置;
- 堆的调整操作是堆排序的核心,如前所述,无论是堆的初始化建立,还是后期的每趟排序都需要此调整操作;调整操作中,每次和左右子节点进行比较,如果发生交换,继续迭代考虑发生交换的子节点是否需要和其子节点继续进行交换,直到没有子节点或者满足大顶堆为止。因此可以看到每趟排序中比较次数最多等于无序序列的深度,而不是n-1了,数目大大减少。
import java.util.Arrays;
public class SelectSortUtil {
public static void simpleSelectSort (int[] array) {
System.out.println("The simple select sort algorithm");
//长度为N的序列需要N-1趟排序,因为当无序序列减少到1时,则自然有序,不需要对其进行一趟排序
for (int i = 0; i < array.length - 1; i++) {
//返回无序序列最小值的位置,因为不包含end,所以可以参数可以是array.length
int minIndex = getMinIndex(array, i, array.length);
//和无序序列中的首元素进行交换,从而有序序列长度加1,无序序列长度减1
int tmp = array[i];
array[i] = array[minIndex];
array[minIndex] = tmp;
}
}
//从array的start到end的子序列中,选出最小值,返回其位置。其中子序列包含start (inclusive),但不包含end (exclusive).
private static int getMinIndex(int[] array, int start, int end) {
int minIndex = start;
for (int i = start + 1; i < end; i++) {
if (array[i] < array[minIndex]) {
minIndex = i;
}
}
return minIndex;
}
public static void heapSort(int[] array) {
System.out.println("The heap select sort algorithm");
//从第一个有子节点的元素开始从后向前调整,直到第一个元素。从而序列被调整为堆结构。
for (int i = array.length/2 - 1; i >= 0; i-- ) {
heapAdjust(array, i, array.length - 1);
}
//依次输出堆顶元素得到无序序列的最大值,1) 将其与无序序列的最后一个元素进行交换,2) 调整堆订,使其继续为堆
for (int i = array.length - 1; i >= 0; i--) {
int tmp = array[i];
array[i] = array[0];
array[0] = tmp;
heapAdjust(array, 0, i - 1);
}
}
//调整array[s..m]的子序列,子序列中包含s,但不包含m, 其中除了array[s]外,其余已经满足堆的特性
private static void heapAdjust(int[] array, int s, int m) {
for (int j = 2*s + 1; j <= m; j++) {
if (j < m && array[j] < array[j+1]) j++;
if (array[s] > array[j]) break;
int tmp = array[s];
array[s] = array[j];
array[j] = tmp;
s = j;
}
}
public static void main(String[] args) {
int[] array = new int[]{1, 30, 5, 3, 8, 50, 2, 9, 7, 14};
SortUtil.simpleSelectSort(array);
System.out.println(Arrays.toString(array));
array = new int[]{1, 30, 5, 3, 8, 50, 2, 9, 7, 14, 69, 21, 80, 77};
SortUtil.heapSort(array);
System.out.println(Arrays.toString(array));
}
}