归并排序
将一个一维数组从小到大排序。归并排序采用了一种化繁为简、分而治之的思想。总的来说,是把一个数组先不断拆分,直到拆分到一个一个单独的元素,再把他们逐渐合并起来。这里边用到了递归回溯。
合并思路
先说合并的思路:合并时,将两个有序数组合并为一个,需要一个临时数组。先比较两数组的第一数字大小,把小的放入临时数组,然后比较小的数的后一位数与之前数的大小,同样把小的放在临时数组里,就这样一直比较,小的入临时数组,大的保留,索引++,然后再比较。直到两数组有一个到底或都到底。结束后,如果两数组有一个还没到底,就把它剩余的数直接加入临时数组。最后把有序的临时数组赋给原数组。
这个方法需要的参数有,要排序的原数组arr,左边有序序列的初始索引left(但不一定是原数组的最左索引),中间索引mid也就是第二个序列的初始索引之前的一位,右边索引right也就是第二个序列的最右索引(但不一定是原数组的最右索引),还有中间数组temp。
- 先把左右两边(有序)的数据按照规则填充到 temp 数组,直到左右两边的有序序列,有一边处理完毕为止。如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素,即将左边的当前元素,填充到 temp 数组,反之,将右边有序序列的当前元素,填充到 temp 。
- 把有剩余数据的一边的数据依次全部填充到 temp,左边的有序序列还有剩余的元素,就全部填充到 temp,右边的有序序列还有剩余的元素,就全部填充到 temp。
- 将 temp 数组的元素拷贝到 arr。
代码
/**
* 合并方法
* @param arr 原数组
* @param left 第一个有序数组开头
* @param mid 第二个有序数组开头
* @param right 原数组结尾
* @param temp 临时数组
*/
private static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i=left;
int j=mid+1;
int t=0;
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t]=arr[i];
t++;
i++;
}else {
temp[t]=arr[j];
t++;
j++;
}
}
while (i<=mid){
temp[t]=arr[i];
t++;
i++;
}
while (j<=right){
temp[t]=arr[j];
t++;
j++;
}
t=0;
for (int k = left; k <=right; k++) {
arr[k]=temp[t];
t++;
}
}
分离+合并思路
将数组不断拆分,也就是每次把数组一分为二,mid每次等于首尾相加除2,因为索引是从0到长度-1,所以这个mid实际上指向第二数组头的前一位。采用递归,退出递归的条件是分到每组只剩下2个数,也就是保证每次left<right,left和right每次变化,分别表示小数组的最左和最右索引,当他们相等时就代表数组分到了只剩下一个元素。
先拆分左,再拆分右,然后合并。这里有点像栈的结构,假如有8个数,顺序是先拆到03,01,合并01,拆23,合并23,合并03,拆47,45,合并45,拆67,合并67,合并47,合并07。
代码
/**
* 分+和的方法
* @param arr
* @param left
* @param right
* @param temp
*/
private static void mergeSort(int[]arr,int left,int right,int[] temp){
if(left<right) {
int mid = (left + right) / 2;
mergeSort(arr, left, mid, temp);
mergeSort(arr, mid + 1, right, temp);
merge(arr,left,mid,right,temp);
}
}