数据结构与算法系列十四(归并排序)

98 阅读3分钟

前面几篇内容,我们看了最基础的排序算法:冒泡排序,插入排序、选择排序。之所以说它们是基础的排序算法,是因为这几个排序算法的时间复杂度有点高,是O(n^2)适合于小规模的数据排序

这一篇我们来看一个高效一点的算法,也就是今天的主角:归并排序,时间复杂度是O(nlogn)

#考考你:
1.你知道归并排序的核心思想吗?
2.你能用java实现归并排序吗?

案例

归并排序核心思想

假设有一个待排序序列:[4, 5, 6, 3, 2, 1]。我们需要按照升序进行排序,排序后的序列是这 样的:[1, 2, 3, 4, 5, 6]。

如何通过归并排序实现呢?

归并排序核心思想:

归并排序算法,利用分而治之思想,包含两个过程:分解、合并

分解: 将一个大的待排序序列,分成两个小的待排序序列;再将小的待排序序列,继续分解成更小的待排序序列......一直到问题不能分解为止

合并: 将分解过程中层层分解后的小的待排序序列,再层层从小到大合并,在合并过程中实现排序......最终完成整个排序

用文字描述,稍微有点抽象,我们看一个图,你应该就明白了。

image.png

归并排序代码实现

入口函数

/**
* 归并排序入口
* @param array  待排序数组
* @param n  数据规模
*/
public static void sort(Integer[] array,int n){
   // 如果数据规模小于等于1,直接返回
   if(n <= 1){
     return;
   }

  // 归并排序函数
  mergeSort(array,0,n-1);
}

分解函数

/**
* 归并排序函数(递归实现)
* @param array  待排序数组
* @param left  数组起始下标
* @param right    数组结束下标
*/
public static void mergeSort(Integer[] array,int left,int right){
   if(left >= right){
     System.out.println("满足left>=right,退出递归:   left="+left+",right="+right);
     return;
   }

   // 求解中间下标
   int mid = (left + right) / 2;
   System.out.println("当次排序:left="+left+",right="+right+",mid="+mid);

    // 分解后的左边待排序序列
    mergeSort(array,left,mid);
    // 分解后的右边待排序序列
    mergeSort(array,mid+1,right);

    // 合并函数
    merge(array,left,mid+1,right);

}

合并函数

/**
* 合并函数
* @param array
* @param left
* @param mid
* @param right
*/
public static void merge(Integer[] array,int left,int mid,int right){
  // 左边数组大小(临时数组)
  int[] leftA = new int[mid-left];
  // 右边数组大小(临时数组)
  int[] rightA = new int[right-mid+1];

  // 左边数组填充数据
  for(int i=left;i<mid;i++){
     leftA[i-left]=array[i];
  }

  // 右边数组填充数据
  for(int j=mid;j<=right;j++){
      rightA[j-mid]=array[j];
  }

  // 定义两个位置指针,标记左右数组,合并元素位置
  int L_INDEX=0,R_INDEX=0;
  // 当次合并,array数组中元素起始位置
  int k=left;

  // 比较两个数组的值,将小的一个元素
  // 放入数组array中,实现排序
   while(L_INDEX<leftA.length &&
         R_INDEX<rightA.length){
      // 谁比较小,谁将元素放入大数组中,移动指针,继续比较下一个
      if(leftA[L_INDEX] <= rightA[R_INDEX]){
          array[k++]=leftA[L_INDEX++];
       }else{
           array[k++] = rightA[R_INDEX++];
       }

   }

   // 收尾工作:如果左边的数组有剩余元素,继续填充
   while(L_INDEX<leftA.length){
      array[k++] = leftA[L_INDEX++];

   }

   // 收尾工作:如果右边的数组有剩余元素,继续填充
   while(R_INDEX<rightA.length){
        array[k++] = rightA[R_INDEX++];
   }
}

测试代码

public static void main(String[] args) {
  // 初始化测试数组
 Integer[] array = {4,5,6,1,2,3};
 // 排序前
 System.out.println("1.排序前数组:" + Arrays.deepToString(array));

 // 2.排序
 System.out.println("2.开始排序-------------------------------");
 sort(array,array.length);

 // 排序后
 System.out.println("3.排序后数组:" + Arrays.deepToString(array));
}

image.png

讨论分享

#考考你答案:
1.你知道归并排序的核心思想吗?
  1.1.归并排序,利用分治思想
  1.2.有两个过程:分解、合并
  1.3.分解过程:
    将大的待排序序列,分解成小的待排序序列,层层分解,直到不能再分解为止
  1.4.合并过程:
     将分解后的小的待排序序列,从小到大,层层合并,在合并过程中实现排序

2.你能用java实现归并排序吗?
  2.1.参考代码实现