Insert
插入排序
public static void insertSortAnalyze(int[] arr) {
for(int i = 1; i < arr.length; i ++) {
if(arr[i] > arr[i - 1]) continue;
for(int j = i; j > 0; j ++) {
if(arr[j] > arr[j - 1]) break;
swap(arr, j, j - 1);
}
}
}
最坏时间复杂度
- 当arr数组是倒序时,出现最坏时间复杂度情况,即 i 每遍历一次,j 都要从 i 位置遍历到 0 位置,消耗的时间应为O(1) + O(2) + … + O(n),等差数列求和公式 (a1 + an) * n / 2得到时间复杂度为O(n2)
最好时间复杂度
- 当arr数组是正序时,出现最好时间复杂度情况,但也要通过第一层for循环进行一次遍历,得到时间复杂度为O(n)
平均时间复杂度
- (O(n) + O(n2)) / 2 = O(n2)
空间复杂度
- 排序时没有创建多余的空间,空间复杂度是O(1)
Select
选择排序
public static void selectSortAnalyze(int[] arr) {
for(int i = 0; i < arr.length; i ++) {
int minIndex = i;
for(int j = i + 1; j < arr.length; j ++) {
if(arr[j] < arr[minIndex]) {
minIndex = j;
}
}
swap(arr, minIndex, i);
}
}
时间复杂度
-
就上述代码来看,无论是最好还是最坏的情况,都得遍历1 + 2 + … + n次才可以成为最佳序列,时间复杂度都为O(n2)
-
空间复杂度的话,没有消耗多余空间,复杂度为O(1)
Bubble
public static void bubbleSortAnalyze(int[] arr) {
for(int i = arr.length - 1; i > 0; i --) {
boolean flag = false;
for(int j = 0; j < i; j ++) {
if(arr[j] < arr[j + 1]) continue;
flag = true;
swap(arr, j, j + 1);
}
if(!flag) break;
}
}
最坏时间复杂度
- 即当 arr 为倒序时,出现最坏时间复杂度,即要遍历1 + 2 + … + n次才能成为最佳序列,时间复杂度为O(n2)
最好时间复杂度
- 本来我以为冒泡的最好时间复杂度和最坏时间复杂度是一样的来着,后来看到网上说冒泡的最好时间复杂度为O(n),后来我在代码中加入了flag标志位,在 arr 正序时,时间复杂度正好为 O(n)
空间复杂度
- 没有多余消耗,复杂度为O(1)
Quick
public static void quickSortAnalyze(int[] arr, int start, int end) {
if(start >= end) return ;
boolean dir = true;
int left = start, right = end;
while(left != right) {
if(dir) {
if(arr[left ++] <= arr[right]) continue;
dir = false;
left --;
} else {
if(arr[left] <= arr[right --]) continue;
dir = true;
right ++;
}
swap(arr, left, right);
}
quickSort(arr, start, left - 1);
quickSort(arr, right + 1, end);
}
最坏时间复杂度
- 当 arr 为正序时,遍历的时间复杂度为 O(n) + O(n - 1) + … + 2 + 1,复杂度为O(n2)
最好时间复杂度
- 当每一次都正好将 arr 平分后递归,递归的时间复杂度为T(n) = O(n) + 2 * T(n/2),最终时间复杂度为 O(nlog2n)
空间复杂度
- nlog2n
Shell
public static void shellSortAnalyzer(int[] arr) {
int interval = arr.length / 2;
while(interval != 0) {
for(int i = 0; i < interval; i ++) {
//插排
for(int j = i + interval; j < arr.length ; j += interval) {
if(arr[j] > arr[j - interval]) continue;
for(int k = j; k >= i + interval; k -= interval) {
if(arr[j] >= arr[j - interval]) break;
swap(arr, j, j - interval);
}
}
}
interval /= 2;
}
}
最坏时间复杂度
- O(n2)
最好时间复杂度
- O(n)
平均时间复杂度
- O(n1.3),别问咋来的,问就是不知道
空间复杂度
- 跟快排一样为O(nlog2n)
Heap
public static void heapSort(int[] arr) {
for(int i = arr.length; i > 0; i --) {
createHeap(arr, i);
swap(arr, i - 1, 0);
}
}
//构建从0-len的大顶堆
public static void createHeap(int[] arr, int len) {
for(int i = len / 2; i >= 0; i --) {
heapify(arr, len, i);
}
}
//构建i为顶点的大顶堆
public static void heapify(int[] arr, int len, int i) {
int left = i * 2 + 1;
int right = i * 2 + 2;
int max = i;
if(left < len && arr[max] < arr[left]) {
max = left;
}
if(right < len && arr[max] < arr[right]) {
max = right;
}
if(max != i) {
swap(arr, i, max);
heapify(arr, len, max);
}
}
时间复杂度
- 最好和最坏都为O(nlog2n)
空间复杂度
- O(1)
Merge
//分
public static int[] mergeSort(int[] arr, int start, int end) {
if(end == start) return new int[]{ arr[start] };
int mid = (start + end) / 2;
int[] a = mergeSort(arr, start, mid);
int[] b = mergeSort(arr, mid + 1, end);
return separate(a, b);
}
//治
public static int[] separate(int[] a, int[] b) {
int[] res = new int[a.length + b.length];
int point = 0;
int i = 0;
int j = 0;
while(i + j < res.length) {
if(i == a.length) {
res[point ++] = b[j ++];
} else if(j == b.length) {
res[point ++] = a[i ++];
} else if(a[i] > b[j]) {
res[point ++] = b[j ++];
} else {
res[point ++] = a[i ++];
}
}
return res;
}
时间复杂度分析
- 最好和最坏都是O(nlog2n)
空间复杂度分析
- O(n)