俩必须掌握的排序
问最多的俩,不会手撕就和面试官干瞪眼吧OTZ。
快速排序
面字节、腾讯都被要求手写快排啦。
import java.util.*;
//快速排序
//不稳定,平均空间复杂度O(logn),平均时间复杂度是O(nlogn)
/**
再优化,之前代码中基准数已经在pivotKey中保存了,
所以不需要每次交换都设置一个temp变量,
在交换左右指针的时候只需要先后覆盖就可以了。
这样既能减少空间的使用还能降低赋值运算的次数。
**/
public class Main{
public static void main(String[] args){
int[] a = new int[]{9,5,2,6,7,3,1,3,2};
QuickSort.sort(a);
System.out.println(Arrays.toString(a));
}
}
class QuickSort{
//一次划分(找到一个基准数,划分后基准数左边都比他小,右边都比他大)
public static int partition(int[] arr,int left,int right){
//一般设置最左边的为基准数
int pivotKey = arr[left];
//基准数的位置
int pivotPointer = left; // 这个变量并没有用到,哇哈哈
//右指针找比基准数小的,左指针找比基准数大的,然后进行交换
while(left<right){
// 先移动右指针,原因http://www.cnblogs.com/wxisme/p/5243631.html
while(left<right && arr[right]>=pivotKey){
right--;
}
//↑右指针找到了第一个比基准数小的数,就停止循环,并把小的数移动到左边
arr[left] = arr[right];
while(left<right && arr[left]<=pivotKey){
left++;
}
//↑左指针找到了第一个比基准数大的数,就停止循环,并把大的数移动到右边
arr[right] = arr[left];
}
// 把基准值赋值给两个指针碰头的地方,也就是中间
// 退出上面循环的时候,left=right
arr[left] = pivotKey; //所以left换成right一样的
//把这个位置返回,以给下一次划分使用,划分左半边就-1,划分右半边就+1
return left;//这里换成right也ok
}
// 快排
public static void quickSort(int[] arr,int left,int right){
if(left < right) {
//得到第一次划分的基准数位置
int pivotPos = partition(arr,left,right);
//左半边继续快速排序
quickSort(arr,left,pivotPos-1);
//右半边继续快速排序
quickSort(arr,pivotPos+1,right);
}
}
// 排序
public static void sort(int[] arr){
if(arr==null||arr.length==0){
return;
}
// 快排
quickSort(arr,0,arr.length-1);
}
// 交换数字
public static void swap(int[] arr,int left,int right){
int temp = arr[left];
arr[left] = arr[right];
arr[right] = temp;
}
}
归并排序
import java.util.*;
// 归并排序
// 稳定,平均空间复杂度为O(n),平均时间复杂度为O(nlogn)。
/**
其基本思想是,先递归划分子问题,然后合并结果。
把待排序列看成由两个有序的子序列,然后合并两个子序列,
.....
**/
public class Main{
public static void main(String[] args){
int[] a = new int[]{9,5,2,6,7,3,1,3,2};
MergeSort.mSort(a,0,a.length-1);
System.out.println(Arrays.toString(a));
}
}
class MergeSort{
public static void mergeSort(int[] arr){
// 判空
if(arr==null||arr.length==0){
return;
}
// 归并排序
mSort(arr,0,arr.length-1);
}
// 归并排序,其实就是递归分治
public static void mSort(int[] arr,int left,int right){
// 和快排一样,直接left<right括起来也可以
if(left>=right)
return;
// 中点
int mid = (left+right)/2; // int mid = left + (right - left) / 2;
//递归排序左半边
mSort(arr,left,mid);
//递归排序右半边
mSort(arr,mid+1,right);
//合并
merge(arr,left,mid,right);
}
//合并两个有序数组[left,mid]和[mid+1,right]
public static void merge(int[] arr,int left,int mid,int right){
//中间数组
//归并排序需要用到一个临时数组
int[] temp = new int[right-left+1];
// 双指针,i指向第一个数组左边界,j指向第二个数组左边界
int i = left;
int j = mid+1;
// 下标
int k = 0;
// 填满temp
while(i<=mid&&j<=right){
//i和j分别从两个有序数组的最左边开始遍历,谁更小就放temp里去
if(arr[i]<=arr[j]){
// 别忘记自增
temp[k++] = arr[i++];
}else{
temp[k++] = arr[j++];
}
}
//下面是while,别脑抽写成if啦
// 而且一定是<=,试了一下 < 出错了,为什么呢?因为如果刚好指向了mid不能丢掉mid呀。要把mid加进去
//如果第二个数组的数全部遍历完了,第一个数组还有剩,接下来直接把第一个数组的数全部加进temp即可
while(i<=mid){
temp[k++] = arr[i++];
}
//如果第一个数组的数全部遍历完了,第二个数组还有剩,接下来直接把第二个数组的数全部加进temp即可
while(j<=right){
temp[k++] = arr[j++];
}
//此时temp已经是一个合并成功的有序数组,再放回arr就好啦
for(int p=0;p<temp.length;p++){
arr[left+p]=temp[p];
//注意一定是arr[left+p],不是arr[p],merge函数的参数就是从left开始的!注意!
}
}
}
最后更新于