排序方法可以分为两种:内部排序 和 外部排序
内部排序的方法很多,常用的大致可以分为:
选择排序
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);
排序常用最大堆来进行。
堆可以用完全二叉树来表示,又称二叉堆。任何一个数组都可以用堆来表示
使用堆排序,我们需要做三件事:
- 创建最大堆:对数组中所有数据进行重排,使其满足最大堆的定义;
- 最大堆调整:创建最大堆时需要随时对已排好的最大堆进行重排,
- 堆排序:移除位于第一个数据的根节点,将最后的元素补到第一位,然后再进行调整
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.