归并排序
归并排序的思想:假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]向上取整个长度为2或者1的有序子序列,再两两归并。这种办法称为2路归并排序。
package com.weixuan.sort.merge;
public class MergeSort {
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + "\t");
}
public static void mergeSort(int[] data) {
mergeSortCore(data, 0, data.length - 1);
}
private static void mergeSortCore(int[] data, int low, int high) {
int midIndex = (low + high) >> 1;
if (low < high) {
// 左边
mergeSortCore(data, low, midIndex);
// 右边
mergeSortCore(data, midIndex + 1, high);
// 左右归并
merge(data, low, midIndex, high);
}
}
private static void merge(int[] data, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int i = low;// 左指针
int j = mid + 1;// 右指针
int k = 0;
// 把较小的数先移到新数组中
while (i <= mid && j <= high) {
if (data[i] < data[j]) {
temp[k++] = data[i++];
} else {
temp[k++] = data[j++];
}
}
// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = data[i++];
}
// 把右边边剩余的数移入数组
while (j <= high) {
temp[k++] = data[j++];
}
// 把新数组中的数覆盖nums数组
for (int k2 = 0; k2 < temp.length; k2++) {
data[k2 + low] = temp[k2];
}
}
}
性能
空间复杂度 O(N+logN) = O(N)
时间复杂度 O(NlogN)
比较过程是两两比较,不存在跳跃,是稳定的排序。
过程 初始序列{50,10,90,30,70,40,80,60,20}
堆排序
package com.weixuan.sort.heap;
public class HeapSort {
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + "\t");
}
/**
* @param data
* 原始数组序列
* @brief 构建堆
*/
private static void buildHeap(int[] data) {
/**
* 获取最后一个非叶子节点
*/
int begin = data.length / 2;
for (int i = begin; i >= 0; i--) {
adjustHeap(data, data.length, i);
}
}
/**
* @param data
* 要调整的数组
* @param heapSize
* 长度
* @param index
* 需要调整的节点的下标
* @brief 调整堆
*/
private static void adjustHeap(int[] data, int heapSize, int index) {
/**
* 节点index的左孩子下标
*/
int leftChildSubscript = 2 * index + 1;
/**
* 节点index的右孩子下标
*/
int rightChildSubscript = 2 * index + 2;
/**
* 最大元素的初始下标
*/
int largestSubscript = index;
/**
* 找到最大元素
*/
/**
* 如果当前根节点小于左孩子的值,那么最大元素的下标为左孩子的下标.
*/
if ((leftChildSubscript < heapSize) && (data[largestSubscript] < data[leftChildSubscript])) {
largestSubscript = leftChildSubscript;
}
/**
* 如果当前根节点小于右孩子的值,那么最大元素的下标为右孩子的下标.
*/
if ((rightChildSubscript < heapSize) && (data[largestSubscript] < data[rightChildSubscript])) {
largestSubscript = rightChildSubscript;
}
/**
* 将最大元素调整至根节点. 根节点不是最大的,那么就调整.
*/
if (index != largestSubscript) {
/**
* 将根节点的值与子节点中的最大值进行调整.
*/
swapElements(data, index, largestSubscript);
/**
*/
adjustHeap(data, heapSize, largestSubscript);
}
}
private static void swapElements(int[] data, int index1, int index2) {
int temp = data[index1];
data[index1] = data[index2];
data[index2] = temp;
}
/**
* @param data
* 原始数组
* @brief 堆排序
*/
public static void heapSort(int[] data) {
int length = data.length;
/**
* 构建堆
*/
buildHeap(data);
while (length >= 1) {
/**
* 将堆的最后一个元素与堆顶元素交换.
*/
swapElements(data, 0, length - 1);
length--;
/**
* 将剩余元素调整为堆
*/
adjustHeap(data, length, 0);
}
}
}
性能
堆排序的运行时间主要耗费在初始创建堆和重新构建堆上。
构建堆的时间复杂度是
O(N)
时间复杂度是 O(NlogN) ,由于记录的比较是跳跃进行的,所以不稳定。
插入排序
在待排序的元素序列基本有序的前提下,效率最高的排序方法是插入排序
思想:将一个记录插入到已经排序的有序表中,从而得到一个新的记录+1的有序表。
package com.weixuan.sort.insert;
public class InsertSort {
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + "\t");
}
public static void insertSort(int[] data) {
for (int i = 1; i < data.length; i++) {
if (data[i] < data[i - 1]) {
int temp = data[i]; // 需要插入的数字
int j = i - 1;
// 数组后移
while (j >= 0 && data[j] > temp) {
data[j + 1] = data[j];
j--;
}
data[j + 1] = temp;
}
}
}
}
性能分析
当数组基本有序时,性能最好,比较n-1次,没有数据移动。时间复杂度为 O(N)
最差情况,待排数据是逆序的,需要比较
∑i=2ni=2+3+4+...n=(n+2)(n−1)2
次,移动的次数是
∑i=2ni+1=(n+4)(n−1)2
次
此时的时间复杂度是 O(N2)
平均情况下,平均的比较次数和移动次数是 N24
shell排序
本质是分组插入排序
package com.weixuan.sort.shell;
public class ShellSort {
public static void printArray(int[] array) {
for (int i = 0; i < array.length; i++)
System.out.print(array[i] + "\t");
}
public static void shellSort(int[] data) {
int increment = data.length;
int j;
do {
increment = increment / 3 + 1;
for (int i = increment; i < data.length; i++) {
if (data[i] < data[i - increment]) {
int temp = data[i];
for (j = i - increment; j >= 0 && temp < data[j]; j -= increment) {
data[j + increment] = data[j];
}
data[j + increment] = temp;
}
}
} while (increment > 1);
}
}
性能
性能和步长(increment)有关,最佳时间复杂度是 O(N3/2)
比较是跳跃性的,是非稳定的排序算法
外部排序
外部排序还要考虑IO效率
tips
堆排序,冒泡排序 ,快速排序每趟结束时,都有一个元素被放置再在其最终位置上。