这一节介绍一下排序的一些高级算法。
(1) 归并排序(时间复杂度 O(N*logN))
归并排序算法的核心就是 将两个有序数组归并成一个。
如何得到两个有序数组呢?就用到了递归的概念。将整个数组分为两个数组,对两个数组分别进行排序,就得到了两个有序数组,怎么排序呢?再将其分成两个数组。。。。。。
缺点:他需要在存储器中有另一个大小等于被排序的数据项数目的数组,如果初始数组几乎占满了整个存储空间,则归并排序不能工作。
// 递归函数中尽量少定义数组和变量,这样会增加效率,这个程序没有考虑到,读者可自行修改。
class Solution {
public int[] InsertionSort(int[] nums, int startNode, int endNode) {
if (endNode - startNode == 0) {
return new int[]{nums[startNode]};
}
int mid = (startNode + endNode) / 2;
int[] left = InsertionSort(nums, startNode, mid);
int[] right = InsertionSort(nums, mid + 1, endNode);
return SortGui(left, right);
}
// 两个有序数组合并成一个有序数组
public int[] SortGui(int[] A, int[] B) {
int[] temp = new int[A.length + B.length];
int pointA = 0;
int pointB = 0;
int pointC = 0;
while (pointA < A.length && pointB < B.length) {
if (A[pointA] < B[pointB])
temp[pointC++] = A[pointA++];
else if (A[pointA] >= B[pointB])
temp[pointC++] = B[pointB++];
}
while (pointA < A.length)
temp[pointC++] = A[pointA++];
while (pointB < B.length)
temp[pointC++] = B[pointB++];
return temp;
}
public static void main (String[] args) {
int[] nums = {2,3,1,34,2,5,7,4};
Solution aSolution = new Solution();
int[] temp = aSolution.InsertionSort(nums, 0, nums.length - 1);
System.out.println(Arrays.toString(temp));
}
}
(2) 希尔排序
希尔排序是插入排序的一种又称“缩小增量排序”,是插入排序算法的一种更高效的改进版本。
什么叫缩小增量排序呢?比如给定一个数组,要对它进行4-增量排序,即对1,5,9,13。。。。;2,6,10,14。。。;3,7,11,15。。。等等对这几组分别进行排序;然后再对它进行1-增量排序,方法一样,这就是缩小增量排序。
进行n-增量排序,那其中的n是如何决定的呢?一般使用 h = h * 3 + 1。比如数组大小为100,则n的取值为40,13,4,1。就是将h=1代入迭代,然后找到小于100的迭代值。
时间复杂度:希尔排序不像快速排序和其他时间复杂度为O(N*logN)的排序算法那么快,因此对非常大的文件排序,他不是最优的选择。但是,希尔排序比选择排序和插入排序这种时间复杂度为O(N^2)的排序算法要快得多,并且他非常容易实现。
import java.util.Arrays;
class Solution {
public int[] ShellSort(int[] nums) {
int n = 1;
int temp;
int in;
while (nums.length > 3 * n + 1)
n = 3 * n + 1;
while (n > 0) {
for (int i = 0; i < n; i++) {
for (int j = n + i; j < nums.length; j = j + n) {
in = j;
temp = nums[in];
while (in > n - 1 && nums[in - n] >= temp) {
nums[in] = nums[in - n];
in = in - n;
}
nums[in] = temp;
}
}
n = (n - 1) / 3;
}
return nums;
}
public static void main (String[] args) {
int[] nums = {2,3,1,34,1,5,7,4};
Solution aSolution = new Solution();
int[] temp = aSolution.ShellSort(nums);
System.out.println(Arrays.toString(nums));
}
}
(3) 快速排序(最快的排序算法,时间复杂度O(N*logN) )
快速排序算法本质上通过把一个数组划分为两个子数组,然后递归地调用自身为每一个子数组进行快速排序来实现的。可能到这有小朋友就要问了,这不就是归并排序吗?相似但也不完全相似,归并是划分成两个有序数组,这个是通过一个枢纽(可以理解为阈值)来分开成两个数组。在以上基础上算法还需要对一些小数组(三个元素及以下)进行单独排序。
划分方法:取数组的首尾和中间,寻找这三个数的中位数,作为枢纽(阈值(当然还可以有其他方法做枢纽)。
import java.util.Arrays;
class Solution {
static int[] nums;
public void FastSort(int startNode, int endNode) {
int threshold;
int mid = (startNode + endNode) / 2;
int thIndex;
median(startNode, endNode);
if (endNode - startNode <= 2) {
return;
}
// 注意mid是在求阈值时的,两个数组的分界线可不是他,应该是thIndex
threshold = nums[mid];
thIndex=divide(startNode, endNode, threshold);
FastSort(startNode, thIndex - 1);
FastSort(thIndex + 1, endNode);
}
public int divide(int start, int end, int th) {
end--;
start++;
while (start < end) {
while (nums[start] < th) {
start++;
}
while (end > 0 && nums[end] > th) {
end--;
}
swap(start, end);
start++;
end--;
}
// 注意要返回分界线的坐标哦
return end;
}
public void median(int start, int end) {
int mid = (start + end) / 2;
if (nums[start] > nums[mid]) {
swap(mid, start);
}
if (nums[mid] > nums[end]) {
swap(mid, end);
}
if (nums[start] > nums[mid]) {
swap(mid, start);
}
}
public void swap(int a, int b) {
int temp;
temp = nums[a];
nums[a] = nums[b];
nums[b] = temp;
}
public void set(int[] n) {
this.nums = n;
}
public static void main (String[] args) {
int[] nums = {2,3,4,1,23,4,5,1,23};
Solution aSolution = new Solution();
aSolution.set(nums);
aSolution.FastSort(0,nums.length - 1);
System.out.println(Arrays.toString(aSolution.nums));
}
}