归并排序其实是利用了分治思想
分:将数组不断从中间分成两半,将分出来的子数组继续分成两半(递归),直到数组中的元素只有一个为止(递归结束条件)
治:当数组被分成最小(只有一个元素)时,我们将最后一次被分出来的两个数组按从小到大(或者从大到小)的顺序合并成为一个数组,如此递归上去,最后得到一个有序的数组
首先我们先来说合并的思路:
函数名:void merge(int a[],int l[],int r[],int left,int right)
这里a是合并后的数组,l和r是两个子数组,从他们中向a中传数据
left参数和right参数表示左右数组的长度
int i = 0,j=0,k=0;定义三个参数用作l,r,a数组的指针
当左边数组指针没有到头,右边也没有到头的时候,比较i和j所指的数字的大小,谁小,就把谁往a里面传,传完后传入数组的指针和a数组都向右移动(这里i++表示先做传值,后加)
while(i< left&&j<right){
if(l[i] < r[j] ) a[k++] = l[i++];
else a[k++] = r[j++];
}
如果有一边的指针已经到头了,那很显然,因为这两个数组都是有序的,所以我们只需要把没到头那个数组剩下的所有值直接传入a数组就好了
while(i<left){
a[k++] = l[i++];
}
while(j<right){
a[k++] = r[j++];
}
现在合并完成了,接下来我们要梳理一下分数组的思路
函数:void mergeSort(int a[],int n)
其中参数a表示要分割的数组,n为该数组的长度(这个n的作用其实是为了让我们找到中间元素的下标)
因为我们要使用递归的思想 首先写上结束条件 if(n < 2) return; 这个很明显吧,就只有1个元素了还分个毛线…
接下来把中间元素的下标求出来int mid = n/2;
然后开两个数组,l和r,分别为左边的数组和右边的数组:
int l[mid],r[n-mid];
现在这两个数组都是空空如也~所以我们要把a数组中mid左边元素放入l,右边(包括自身)放入r
for(int i=0;i<mid;i++){
l[i] = a[i];
}
for(int j=mid;j<n;j++){
r[j-mid] = a[j];
}
好嘞,这一次分组就完事了,然后对接下来分出来的左右数组再进行分组
mergeSort(l,mid);
mergeSort(r,n-mid);
分完之后还要合并奥~
merge(a,l,r,mid,n-mid);
整个代码就算完成啦
归并排序的时间复杂度是稳定的,一直都为O(logn)所以这个效率是要比快排高的(o(n^2)到o(logn)),所以我们可以使用归并提高一些效率