归并排序思路及代码解释

310 阅读3分钟

前言

上一篇文章写了快速排序,最近由于刷题经常会遇到使用归并排序解题的,而且想想,如何对一个单链表进行排序,此时你不能用快排对不对?快排他需要两个指针的移动,显然对于单链表你很难做到指针的回溯移动,这样我们就要用到归并排序了

基本思想

归并排序其实要分为两个步骤,第一:分治,将一个大的问题分成所有小的子问题去解决,这就是分治,第二:归一,将所有小的子问题的解决答案合并,最终成为大问题的答案。 我的理解是,归并排序其实是一种自底向上的一种排序,你想一想,假如你要排一个长度只有2的数组,是不是很简单?只用比较这两个数大小,交换位置即可,那假如你要合并两个已经排好序的数组呢?也很简单对不对?用一个额外的数组空间将两个有序数组的元素一个一个比较后加进去就可以得到一个合并后的有序数组,也很简单对不对?所以,归并排序就是将一个复杂的问题分化成了这些很简单很简单的小问题去解决,它能保证你每一次进行归并(即数组合并)的两个数组都是有序的,因为它会从最底层开始,数组长度为1或者2,这里我给出一个递归的代码

代码解释

function mergeSort(arr,L,R){	//归并排序
  if(L == R)return;		//L = R说明数组长度为1直接返回
  let mid = (L + R)>>1;		//找出中点
  mergeSort(arr,L,mid);		//对中点左侧进行归并排序
  mergeSort(arr,mid + 1,R);	//对中点右侧进行归并排序
  merge(arr,L,mid,R);		//合并已经排序好的数组
}

function merge(arr,L,mid,R){	//对两个有序数组进行归并
  let temp = [];		//使用辅助数组保存归并结果
  let l1 = L;			//左数组起点
  let l2 = mid + 1;		//右数组起点
  while(l1 <= mid && l2 <= R)	//合并过程 小的加入
    temp.push(arr[l1] < arr[l2] ? arr[l1++] : arr[l2++]);
  while(l1 <= mid)		//将左数组剩余部分元素加入辅助数组
    temp.push(arr[l1++]);
  while(l2 <= R)		//将右数组剩余部分元素加入辅助数组
    temp.push(arr[l2++]);
  for(let i = 0;i<temp.length;i++){ //将temp数组的所有值复制到arr数组,从L位置开始
    arr[L + i] = temp[i];
  }
}

这里递归的过程和结束条件应该不难理解,我稍微解释一下merge(arr,L,mid,R)这条语句,不是说要归并两个有序数组吗?怎么这里只有一个,其实这两个数组隐式的藏在了arr里面,你看mergeSort(arr,L,mid)是不是把L ~ mid部分弄成有序的了? 再看mergeSort(arr,mid + 1,R) 是不是把mid + 1 ~ R部分弄成有序的了? 所以merge(arr,L,mid,R)它的两个数组分别是arr的 L到midmid+1到R

归并两个有序数组的过程应该不难,用两个指针分别指向两个数组的元素,小的加入辅助数组然后指针后移即可。

最后,如果对我解释的归并排序还有什么不懂的或者觉得我解释有不妥的地方的欢迎各位留言指教