1 归并排序
归并排序是一种分治算法,把大的问题分成一个个小部分去处理,最终解决大问题。
假设有两个数组A,B,每个数组都是从小到大排好序的,如果要将这两个数组合并成一个数组,并且从小到大排好序
那么只需要从头比较,依次将A,B中较小的值放入新的数组。
对于一个无序的数组,也可以通过这种方法排序,将数组从中间分成2部分,但是大概率是两个无序数组,然后接着对
分开的两部分再分,直到每部分都是只有一个数,显然每部分都是有序的了。如下图:
一.先分
二.比较排序再和
然后对最下一层两两比较,例如80 30 得到新的序列 [30,80] 60 20 得到新的序列[20,60]
然后在对新序列[30,80]和[20,60]进行合并, 从头比较得到新的序列[20,30,60,80] 同理可得 右边的序列[10,50,60,70]
最后对[20,30,60,80] 和 [10,50,60,70] 进行合并,先创建一个新数组,长度等于两个数组长度的和,然后开始比较:
10比20小,先将10存入; 将20和50比较,20小,将20存入;将50和30比较,30小,将30存入;50和60比较,50小,将50存入;60和60比较,将前面一个数组的60存入;然后接着60和80比较,60小,60存入新数组;80和70比较,70小,存入70,后面一个数组到头了的话,就把另一个数组的所有数据插入到新数组的末端;所有数据就被排好序了。
如图:
代码实现
//第一步通过递归分
public static void mergeSort(int []arr,int low,int high){
int middle=(high+low)/2;
if(low<high){
//前半部分
mergeSort(arr,low,middle);
//后半部分
mergeSort(arr,middle+1,high);
//对排好序的进行归并
merge(arr,low,middle,high);
}
}
//第二步合并
public static void merge(int []arr,int low,int middle,int high){
int []temp=new int[high-low+1];
int i=low;
int j=middle+1;
int index=0;
//遍历前半部分和后半部分,一次将小的值存入新的数组
while(i<=middle&&j<=high){
if(arr[i]<=arr[j]){
temp[index]=arr[i];
i++;
index++;
}else {
temp[index]=arr[j];
j++;
index++;
}
}
//前半部分有剩余,就把剩下的全部存进去
while(i<=middle){
temp[index]=arr[i];
index++;
i++;
}
//后半部分有剩余,就把剩下的全部存进去
while(j<=high){
temp[index]=arr[j];
index++;
j++;
}
//将临时数组的数据存入原来数组
for(int k=0;k<temp.length;k++){
arr[low+k]=temp[k];
}
}
2 希尔排序
希尔排序是一种插入排序的优化,插入排序将一个数插入到前面合适的位置,如果很小的数一开始在队列的末端,那么比较的次数将会大大增加,希尔排序则解决了这个问题,能够快速的将队尾较小的值换到队前面,将大的值快速放到最后,减少比较次数
交换的步长从length/2开始,依次减半。开始时步长较大,比较次数较少,大元素基本放到最后,大致有序。最后步长短,比较次数多,不过队列基本有序状态,比较次数大大减少。
public static void shellSort(int []arr){
//步长从arr.length/2 --> 1 时间复杂度log2n
for(int d=arr.length/2;d>0;d=d/2){
//时间复杂度O(n)
for(int i=d;i<arr.length;i++){
int j=0;
//时间复杂度O(1)
//以d为步长比较i前面的值然后插入a[i]的值
for(j=i;j>=d;j=j-d){
if(arr[j]<arr[j-d]){
int temp=arr[j];
arr[j]=arr[j-d];
arr[j-d]=temp;
}
else break;
}
}
}
}