1.归并排序
1.1归并排序的原理
两个数组归并的过程:首先借助一个临时空间来保存数组,在两个有序数组搞两个下标,比较两个下标的大小,谁小就把谁放在临时空间,顺便把下标往后移。
因为是原地排序,因此在最后把数组要拷贝到原数组。
归并排序的前提是两边必须都有序,那怎么办呢?为了保证数组有序,因此继续往下切分,切分到只剩下一个元素,就称为有序的数组了。然后按照上述相同的步骤,比较两个有序区间元素的大小,然后继续拷贝回去,之后,比较下一组的数据,以及类推。
1.2归并排序的实现(递归)
import java.util.Arrays;
public class Sort {
public static void mergeSort(int [] arr){
_mergeSort(arr,0,arr.length);
}
//辅助递归的方法
public static void _mergeSort(int [] arr,int left,int right) {
if(right-left<=1) {
//没有元素或者只有一个元素
//不需要做任何处理
return ;
}
int mid=(left+right)/2;
_mergeSort(arr,left,mid);
_mergeSort(arr,mid,right);
merge(arr,right,left,mid);
}
//归并排序中的关键操作,就是归并两个有序数组
//使用该merge方法完成数组归并的过程
//此处两个数组就通过left,mid ,right来描述
//[left,mid)左侧区间
//[mid,right)右侧区间
public static void merge(int [] arr,int right,int left,int mid){
//在代码中进行具体的归并排序
//需要创建一个临时的空间来保存归并的结果
//临时空间得保存的下两个数组
//right-left
int [] tmp=new int[right-left];
//这个下标表示当前元素放到临时空间的哪个位置上
int tmpIndex=0;
int cur1=left;
int cur2=mid;
while(cur1<mid&&cur2<right){
//目的是为了稳定性
if(arr[cur1]<=arr[cur2]){
//把cur1对应的元素放到临时空间中
tmp[tmpIndex]=arr[cur1];
tmpIndex++;
cur1++;
}else{
//把cur对应的元素插到临时空间中
tmp[tmpIndex]=arr[cur2];
tmpIndex++;
cur2++;
}
}
//循环换结束之后,需要把剩余的元素也拷贝到最终的结果
while(cur1<mid){
tmp[tmpIndex]=arr[cur1];
tmpIndex++;
cur1++;
}
while(cur2<right){
tmp[tmpIndex]=arr[cur2];
tmpIndex++;
cur2++;
}
//还需要把tmp的结果放到arr数组,原地排序
//把原始数组的[left,right]填满
for(int i=0;i<tmp.length;i++){
arr[left+i]=tmp[i];
}
}
public static void swap(int [] arr,int i,int j){
int tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
public static void main(String[] args) {
int [] arr={9,5,2,7,3,6,8};
mergeSort(arr);
System.out.println(Arrays.toString(arr));
}
}
归并排序的时间复杂度为O(NlogN),空间复杂复杂度为O(N),稳定性:稳定排序。
1.3归并排序的实现(非递归)
import java.util.Arrays;
public class Sort {
//非递归实现归并排序
//直接把[0] [1]视为带归并的数组
public static void mergeSortByLoop(int [] arr){
//gap用于限定分组
//gap指的含义每个待归并数组的长度
int gap=1;
for(;gap<arr.length;gap=gap*2){
//当前两个带归并的数组
for(int i=0;i<arr.length;i=i+2*gap){
//折个循环中控制两个相邻的数组进行归并
int left=i;
int mid=i+gap;
if(mid>=arr.length){
mid=arr.length;
}
int right=i+2*gap;
if(right>=arr.length){
right=arr.length;
}
merge(arr,right,left,mid);
}
}
}
public static void merge(int [] arr,int right,int left,int mid){
//在代码中进行具体的归并排序
//需要创建一个临时的空间来保存归并的结果
//临时空间得保存的下两个数组
//right-left
if(left>=right){
return;
}
int [] tmp=new int[right-left];
//这个下标表示当前元素放到临时空间的哪个位置上
int tmpIndex=0;
int cur1=left;
int cur2=mid;
while(cur1<mid&&cur2<right){
//目的是为了稳定性
if(arr[cur1]<=arr[cur2]){
//把cur1对应的元素放到临时空间中
tmp[tmpIndex]=arr[cur1];
tmpIndex++;
cur1++;
}else{
//把cur对应的元素插到临时空间中
tmp[tmpIndex]=arr[cur2];
tmpIndex++;
cur2++;
}
}
//循环换结束之后,需要把剩余的元素也拷贝到最终的结果
while(cur1<mid){
tmp[tmpIndex]=arr[cur1];
tmpIndex++;
cur1++;
}
while(cur2<right){
tmp[tmpIndex]=arr[cur2];
tmpIndex++;
cur2++;
}
//还需要把tmp的结果放到arr数组,原地排序
//把原始数组的[left,right]填满
for(int i=0;i<tmp.length;i++){
arr[left+i]=tmp[i];
}
}
public static void main(String[] args) {
int [] arr={9,5,2,7,3,6,8};
mergeSortByLoop(arr);
System.out.println(Arrays.toString(arr));
}
}