排序算法汇总
排序算法是一个最基本的算法,经常会被问到,有些公司的面试也可能会考到,这里特意将所有的排序算法做一个总结
1. 冒泡排序
//冒泡排序,两个循环都从0开始冒泡 public int[] MaoPao(int [] array){ int len = array.length; for(int i=0;i<len-1;i++){ for(int j=0;j<len-i-1;j++){ if(array[j]>array[j+1]){ int temp = array[j]; array[j] = array[j+1]; array[j+1] = temp; } } } return array; }
最基本的一个排序算法,总是将最大的往右进行冒泡,总是比较两个相邻的元素。
平均时间复杂度O(n^2),最好时间O(n),最坏时间O(n^2),空间复杂度O(1),不需要另外开辟空间
稳定的算法,因为冒泡排序总是比较相邻的两个元素,所以是稳定的。
2. 直接插入排序
//直接插入排序 public int[] ChaRu(int [] array){ int len = array.length; for(int i=1;i<len;i++){ int temp = array[i]; int j=i-1; while(j>=0 && temp<array[j]){ //所有元素后移 array[j+1]=array[j]; j--; } array[j+1] = temp; //出入元素 } return array; }
第一个元素认为排序好的,之后的每一个元素往排好顺序的数组里插入,其他所有的元素向后移动一位
平均时间复杂度O(n^2),最好时间O(n),最坏时间O(n^2),空间复杂度O(1),不需要另外开辟空间
稳定的算法,因为出入排序每次也是相邻的比较并且后移,因此也是稳定的
3. 选择排序
//选择排序,每次找到最小的排过去就好 public int[] selectSort(int [] array){ int len = array.length; for(int i=0;i<len;i++){ int index = i; for(int j=len-1;j>i;j--){ if(array[j]<array[index]){ index=j; } } int temp = array[i]; array[i] = array[index]; array[index] = temp; } return array; }
每次循环,找到最小的一个元素,放在每一次循环的位置
平均时间复杂度O(n^2),最好时间O(n^2),无论如何都需要循环完才能确定最小元素,最坏时间O(n^2),空间复杂度O(1),不需要另外开辟空间
不稳定的排序算法,因为每次都需要选择来进行,而且是选择位置插入的。
4. 快速排序
//快速排序思想,每次第一个为base,然后小的放左边,大的放右边,递归两边 public int[] quickSort(int[] array, int left, int right){ if(left<right){ int base = array[left]; int temp; int i=left, j=right; do{ while(array[i]<base && i<right){ i++; } while(array[j]>base && j>left){ j--; } if(i<=j){ temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } }while(i<=j); if(left<j){ quickSort(array,left,j); } if(right>i){ quickSort(array,i,right); } } return array; }
每次,将首个left元素作为基础base,然后从left和right两边开始,小的放在左半部分,大的放在右半部分。然后递归排序左边和右边,直到left==right停止
平均时间复杂度O(nlog(n)),最好时间O(nlog(n)),最坏时间O(n^2),空间复杂度一般是O(log(n)),但是也可以到达O(1),如上述代码,是没有额外开辟空间的。
快速排序也是一个不稳定的排序算法
5. 归并排序
//归并排序 public int[] mergeSort(int[] array, int left, int right){ int mid = (left+right)/2; if(left<right){ mergeSort(array,left,mid); mergeSort(array,mid+1,right); merge(array,left,mid,right); } return array; } public void merge(int[] array, int left, int mid, int right){ int[] temp = new int[right-left+1]; int k = 0; int i=left; //左 int j=mid+1; //右 //合并 while(i<=mid && j<=right){ if(array[i]<array[j]){ temp[k]=array[i]; k++; i++; }else{ temp[k]=array[j]; i++; j++; } } //左边剩余 while(i<=mid){ temp[k]=array[i]; k++; i++; } //右边剩余 while(j<=right){ temp[k]=array[j]; k++; i++; } //复制到原数组中 for(int ii=0;ii<temp.length;ii++){ array[left+ii]=temp[ii]; } }
采用了分治法的思想,首先把一个数组不断地拆分,一直拆分到每个都只有一个元素为止,然后调用merge算法,不断地合并两个已经排好序的数组,知道最后排好顺序
平均时间复杂度O(nlog(n)),最好时间O(nlog(n)),最坏时间O(nlog(n)),空间复杂度一般是O(log(n)),归并排序是需要额外的申请空间的,每次是需要生成新的数组的。
归并排序是稳定的,因为是总是先分治到最小的数组,然后进行合并的。
6. 希尔排序
本质是分治思想和直接插入排序算法的结合
平均时间复杂度O(n^(1.3)),最好时间O(n),最坏时间O(n^(2)),空间复杂度一般是O(1)
不稳定的排序算法
7. 堆排序
本质是使用最小堆或者最大堆进行排序(二叉树),首先需要建立堆,之后每次拿出一个最小元素或者最大元素,需要重新地调整堆
平均时间复杂度O(nlog(n)),最好时间O(nlog(n)),最坏时间O(nlog(n)),空间复杂度一般是O(l)
不稳定的排序算法
8. 基数排序,基于桶排序的算法
(1) 首先根据个位数进行桶排序,然后按照0-9得到桶中的元素
(2) 根据十位数进行桶排序,然后按照0-9得到桶中的元素
(3) .......
(4) 一直达到最高位位置,得到的结果即为最终的排序结果
9. 桶排序
用空间换取时间的做法,给定一个很大范围的桶,每个元素放在一个桶中,只用一遍循环便可
时间复杂度O(n)
总结
冒泡,插入,归并和基数排序都是稳定的排序算法
选择排序,快速排序,希尔排序和堆排序都是不稳定的排序算法