我们知道在一些互联网等公司的面试中会经常被问到介绍几种常见的排序算法,甚至会让我们手撕出来。在此,我在备战秋招的过程中整理了一些常见的排序算法,写在这里供大家分享。几种排序算法不分先后顺序。
**快速排序**(时间复杂度为nlogn,空间复杂度为logn,不稳定排序)
public void quickSort(int[] nums, int l, int r) {
if(l>=r)
return;
int p=partition(nums,l,r);
quickSort(nums, l, p-1);
quickSort(nums, p+1, r);
}
private int partition(int[] nums, int l, int r)
int i=l+1, j=r;
int num=nums[l];
while(true) {
while(i<=r && nums[i]<num)
i++;
while(j>=l+1 && nums[j]>num)
j--;
if(i>=j)
break;
int tmp=nums[i];
nums[i]=nums[j];
nums[j]=tmp;
}
int tmp=nums[j];
nums[j]=nums[l];
nums[l]=tmp;
return j;
}
性能分析:
最好的情况下是每次都将数组对半分,这样递归调用次数最少,复杂度为O(nlogn)。最坏的情况下,第一次从最小的元素切分,第二次从第二小的元素切分…这种情况需要比较n²/2,为了防止数组最开始就是有序的,在进行快排时需要随机打乱数组。
快速排序在小数组中也会递归调用自己,对于小数组,插入排序比快排的性能更好;对于有大量重复元素的数组,使用三向切分快排可以在线性时间内完成排序。
**选择排序**(时间复杂度为n²,空间复杂度为1,不稳定排序)
public void selectSort(int[] nums) {
for(int i=0; i<nums.length-1; i++) {
int min=i;
for(int j=i+1; j<nums.length; j++) {
if(nums[j]<nums[min]) {
min=j;
}
}
int tmp=nums[i];
nums[i]=nums[min];
nums[min]=tmp;
}
}
**冒泡排序**(时间复杂度为n²,空间复杂度为1,稳定排序)
public void bubbleSort(int[] nums) {
for(int i=0; i<nums.length-1; i++) {
for(int j=0; j<nums.length-1-i; j++) {
if(nums[j]>nums[j+1]) {
int tmp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=tmp;
}
}
}
}
**插入排序**(时间复杂度为n到n²,空间复杂度为1,稳定排序)
public void insertSort(int[] nums) {
for(int i=1; i<nums.length; i++) {
for(int j=i; j>0&&nums[j]<nums[j-1]; j--) {
int tmp=nums[j];
nums[j]=nums[j-1];
nums[j-1]=tmp;
}
}
}
插入排序的时间复杂度取决于数组的初始顺序,如果数组已经部分有序,那么逆序较少,需要交换的次数也就较少,时间复杂度较低。最坏的情况(数组倒序)下需要n²/2比较和n²/2次交换;最好的情况(数组有序)下需要n-1次比较和0次交换。
**归并排序**(时间复杂度为O(nlogn),空间复杂度为O(n),稳定排序)
public void mergeSort(int[] nums, int left, int right) {
if(left>=right)
return;
int mid=left+(right-left)/2;
mergeSort(nums, left, mid);
mergeSort(nums, mid+1, right);
mergeSortCore(nums,left,mid,right);
}
private void mergeSortCore(int[] nums, int left, int mid, int right) {
int[] tmp=new int[right-left+1];
int i=left, j=mid+1;
int k=0;
while(i<=mid&&j<=right) {
if(nums[i]<=nums[j]) {
tmp[k++]=nums[i++];
}else {
tmp[k++]=nums[j++];
}
}
while(i<=mid) {
tmp[k++]=nums[i++];
}
while(j<=right) {
tmp[k++]=nums[j++];
}
for(int m=0; m<k; m++) {
nums[left+m]=tmp[m];
}
}
**堆排序**(时间复杂度为O(nlogn),空间复杂度为O(1),不稳定排序)
一个堆的高度为logN,因此在堆中插入元素和删除最大元素的复杂度都为logN。对于堆排序,由于要对N个节点进行下沉操作,因此复杂度为NlogN。