冒泡排序
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
public class BubbleSort {
public int[] sort(int[] arr){
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
}
插入排序
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
public class InsertionSort {
public int[] sort(int[] arr){
int len = arr.length;
for (int i = 1; i < len; i++) {
int preIndex = i - 1;
int current = arr[i];
while (preIndex >= 0 && arr[preIndex] > current){
arr[preIndex + 1] = arr[preIndex];
preIndex--;
}
arr[preIndex + 1] = current;
}
return arr;
}
}
选择排序
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
public class SelectionSort {
public int[] sort(int[] arr){
int len = arr.length;
for (int i = 0; i < len - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]){
minIndex = j;
}
}
int temp = arr[i];
arr[i] = arr[minIndex];
arr[minIndex] = temp;
}
return arr;
}
}
希尔排序
- 时间复杂度:O(n²) (这个不是很准)
- 空间复杂度:O(1)
public class ShellSort {
public int[] sort(int[] arr){
int len = arr.length;
for (int gap = len / 2; gap > 0; gap /= 2) {
//这是分组,增量啥啥的
for (int i = gap; i < len; i++) {
int j = i;
int current = arr[i];
//对每个组进行插入排序
while (j - gap >= 0 && current < arr[j - gap]) {
arr[j] = arr[j - gap];
j -= gap;
}
}
}
return arr;
}
}
归并排序
- 时间复杂度:O(nlog2N)
- 空间复杂度:O(n + log2N) (递归) | O(n) (迭代)
递归版
- 这个是每次递归都要新建一次temp数组
public class MergeSort {
int[] temp;
public void sort(int[] arr, int lo, int hi) {
temp = new int[arr.length];
if (hi <= lo) return;
int mid = (hi - lo >> 1) + lo;// ‘-’的优先级大于‘>>’
sort(arr, lo, mid);
sort(arr, mid + 1, hi);
merge(arr, lo, hi, temp);
}
public void merge(int[] arr, int lo, int hi,int[] temp) {
//注意,定位中点坐标最好都这么写,这样不会超int范围
int mid = (hi - lo >> 1) + lo;
int i = 0;
int j = lo,k = mid + 1;
while(j <= mid && k <= hi){
if (arr[j] < arr[k]) {
temp[i++] = arr[j++];
} else {
temp[i++] = arr[k++];
}
}
while(j <= mid){//将左边剩余元素填充进temp中
temp[i++] = arr[j++];
}
while(k <= hi){//将右序列剩余元素填充进temp中
temp[i++] = arr[k++];
}
//将temp中的元素全部拷贝到原数组中
for (int k2 = 0; k2 < i; k2++) {
arr[k2 + lo] = temp[k2];
}
}
}
- 这种写法不用
public class MergeSort {
public void sort(int arr[]){
int[] temp = new int[arr.length];
sort(arr,0,arr.length - 1,temp);
}
public void sort(int[] arr, int lo, int hi,int[] temp) {
temp = new int[arr.length];
if (hi <= lo) return;
int mid = (hi - lo >> 1) + lo;// ‘-’的优先级大于‘>>’
sort(arr, lo, mid,temp);
sort(arr, mid + 1, hi,temp);
merge(arr, lo, hi, temp);
}
public void merge(int[] arr, int lo, int hi,int[] temp) {
//注意,定位中点坐标最好都这么写,这样不会超int范围
int mid = (hi - lo >> 1) + lo;
int i = 0;
int j = lo,k = mid + 1;
while(j <= mid && k <= hi){
if (arr[j] < arr[k]) {
temp[i++] = arr[j++];
} else {
temp[i++] = arr[k++];
}
}
while(j <= mid){//将左边剩余元素填充进temp中
temp[i++] = arr[j++];
}
while(k <= hi){//将右序列剩余元素填充进temp中
temp[i++] = arr[k++];
}
//将temp中的元素全部拷贝到原数组中
for (int k2 = 0; k2 < i; k2++) {
arr[k2 + lo] = temp[k2];
}
}
}
但是不知道为什么,第二种写法用时是第一种写法的好多好多倍,甚至在牛客会爆time
快速排序
基本版(挖坑 + 分治)
public class QuickSort {
public int[] sort(int[] arr){
sort(arr,0,arr.length - 1);
return arr;
}
public void sort(int[] arr,int lo,int hi){
if(lo >= hi)return;
int i = lo,j = hi;
int temp = arr[lo];
int node;
while (i < j){
while (j > i && arr[j] >= temp){
j--;
}
while (i < j && arr[i] <= temp){
i++;
}
if (i < j){
node = arr[i];
arr[i] = arr[j];
arr[j] = node;
}
}
arr[lo] = arr[i];
arr[i] = temp;
sort(arr,lo,i - 1);
sort(arr,i + 1,hi);
}
}
手撸代码写法(模板)
public class QuickSort {
public int[] sort(int[] arr){
sort(arr,0,arr.length - 1);
return arr;
}
public void sort(int[] arr,int lo,int hi){
if(lo >= hi)return;
int i = lo,j = hi;
int temp = arr[lo];
while (i < j){
while (i < j && arr[j] >= temp)j--;
if (i < j)arr[i++] = arr[j];
while (i < j && arr[i] <= temp) i++;
if (i < j)arr[j--] = arr[i];
}
arr[i] = temp;
sort(arr,lo,i - 1);
sort(arr,i + 1,hi);
}
}
- 为什么归并排序和快速排序平均算法时间复杂度一样,甚至归并排序的空间复杂度小于快排的空间复杂度,我们主流还是用的快速排序呢?
快排能突破nlogn的下界
Arrays.sort里面的快排是做了优化的
大多数的排序情况下,快排的时间复杂度是不到nlogn的,更符合生产情况