排序算法(3)----归并排序
来源:互联网 发布:kali linux配置ip地址 编辑:程序博客网 时间:2024/06/05 07:16
这篇博文分为四个部分:
- 归并排序基本思想
- 基础代码实现与解析
- 代码一次优化
- 代码二次优化(自底向上的归并排序)
1.归并排序基本思想
* 3)* [归并排序] :小到大排序* 1.循环的将每一个部分都分为原来的一半,直到最后每一部分只剩下一个元素* 2.循环的将每两个部分进行合并并且排序,直到最后全部合并* O(n*log n)
2.基础代码实现与解析
static int[] mergeSort(int[] arr) { return __mergeSort(arr, 0, arr.length - 1);}
private static int[] __mergeSort(int[] arr, int left, int right) { if (left >= right) { return arr; } //得到中点位置的下标middle,注意这里最好不要使用 int middle = (right + left) / 2 ,防止 right + left 数据太大造成溢出 int mid = (right - left) / 2 + left; //1.循环的将每一个部分都分为原来的一半,直到最后每一部分只剩下一个元素 __mergeSort(arr, left, mid); __mergeSort(arr, mid + 1, right); //2.循环的将每两个部分进行合并并且排序,直到最后全部合并 // if 条件判断当这两部分需要排序时(即前一部分的最大值要比后一部分的最小值大时),进行排序,否则直接合并即可 if (arr[mid] > arr[mid + 1]) { __merge(arr, left, mid, right); } return arr;}
private static int[] __mergeSort(int[] arr, int left, int right) { if (left >= right) { return arr; } //得到中点位置的下标middle,注意这里最好不要使用 int middle = (right + left) / 2 ,防止 right + left 数据太大造成溢出 int mid = (right - left) / 2 + left; //1.循环的将每一个部分都分为原来的一半,直到最后每一部分只剩下一个元素 __mergeSort(arr, left, mid); __mergeSort(arr, mid + 1, right); //2.循环的将每两个部分进行合并并且排序,直到最后全部合并 // if 条件判断当这两部分需要排序时(即前一部分的最大值要比后一部分的最小值大时),进行排序,否则直接合并即可 if (arr[mid] > arr[mid + 1]) { __merge(arr, left, mid, right); } return arr;}//将 arr[left....mid] 与 arr[mid + 1....right] 两个部分进行归并private static int[] __merge(int[] arr, int left, int mid, int right) { int[] temarr = new int[right + 1 - left]; //开辟新的空间,这个空间是用来进行排序 // 将 arr 中所有元素复制一份,到 temarr 中,注意这里 arr 是从下标 left 开始的,而 temarr 是从下标 0 开始的 for (int i = left; i < right + 1; i++) { temarr[i - left] = arr[i]; } int i = left; //作为前一部分将要排序的元素的下标 int j = mid + 1; //作为后一部分将要排序的元素的下标 for (int k = left; k <= right; k++) { //k为arr数组中本轮归并中最小值将要覆盖掉的下标 //防止越界,要保证i始终属于前一部分,j始终属于后一部分 if (i > mid) { //表明前一部分已经遍历结束,只需要把后一部分的值依次填入即可 arr[k] = temarr[j - left]; j++; } else if (j > right) { arr[k] = temarr[i - left]; //同理,后一部分已经遍历完毕,只需依次填入前一部分即可 i++; } //一般情况 : 遍历整个数组,比较 temarr[i - left] 与 temarr[j - left] 的大小,将小的直接覆盖原数组arr,将它排到前面,并且将排完序的下标后移 else if (temarr[i - left] < temarr[j - left]) { arr[k] = temarr[i - left]; i++; } else { arr[k] = temarr[j - left]; j++; } } return temarr;}
3.代码一次优化
由于最初归并排序分割到最后会只剩下一个元素,然后这一个元素不断的通过递归的方式合并合并再合并,因此这里可以不递归切分到底,而在只剩下10个元素的时候,使用插入排序对它进行排序,然后直接向上合并.
这种做法权衡了性能的消耗.
在这里只需要修改 __mergeSort(int[] arr,int left,int right) 这个方法:
private static int[] __mergeSort(int[] arr, int left, int right) {//优化 if (left >= right) {//优化 return arr;//优化 } /*优化开始*/ if (right - left <= 10) { int[] temparr = new int[right - left + 1]; for (int i = left; i <= right; i++) { temparr[i - left] = arr[i]; } //使用插入排序进行底层的排序,优化归并排序不断递归到底的消耗 temparr = InsertionSort.insertionSort_3(temparr); for (int i = left; i <= right; i++) { //把 temparr 再回填回到 arr 中 arr[i] = temparr[i - left]; } return arr; } /*优化结束*/ //得到中点位置的下标middle,注意这里最好不要使用 int middle = (right + left) / 2 ,防止 right + left 数据太大造成溢出 int mid = (right - left) / 2 + left; //1.循环的将每一个部分都分为原来的一半,直到最后每一部分只剩下一个元素 __mergeSort(arr, left, mid); __mergeSort(arr, mid + 1, right); //2.循环的将每两个部分进行合并并且排序,直到最后全部合并 // if 条件判断当这两部分需要排序时(即前一部分的最大值要比后一部分的最小值大时),进行排序,否则直接合并即可 if (arr[mid] > arr[mid + 1]) { __merge(arr, left, mid, right); } return arr; }
4.代码二次优化(自底向上的归并排序)
注意看归并排序的第一步:将每个大的部分都切分为原来的一半,直到最后只剩下一个元素.
如果是从计算机本身的视角来看,他始终是同一个数组,因此循环的二分操作可以帮助我们理解原理,但是对计算机来说却无异于徒增负担,所以这里就没有必要执行切分操作.
[思路]:我们可以直接把它看做是已经切分成了多个小部分,直接归并就好
我们直接修改 mergeSort(int[] arr) 这个方法就好:
static int[] mergeSort(int[] arr) { for (int part = 1; part <= arr.length; part += part) { for (int i = 0; i + part < arr.length; i += 2 * part) { if ((i + 2 * part - 1 > arr.length - 1)) { __merge(arr, i, i + part - 1, arr.length - 1); } else { __merge(arr, i, i + part - 1, i + 2 * part - 1); } } } return arr;}经过这一步的优化,我们甚至都不需要
__mergeSort(int[] arr, int left, int right)
这个方法,就可以实现归并排序
阅读全文
0 0
- 排序算法(3)----归并排序
- 排序算法-归并排序
- 排序算法------归并排序
- 排序算法-归并排序
- 排序算法---归并排序
- 排序算法--归并排序
- 排序算法--归并排序
- 排序算法-归并排序
- 排序算法--归并排序
- 排序算法--归并排序
- 排序算法:归并排序
- 排序算法-归并排序
- 排序算法:归并排序
- 【排序算法】归并排序
- 排序算法--归并排序
- 排序算法-归并排序
- 排序算法--归并排序
- 排序算法:归并排序
- C++类的实例化对象的大小之sizeof()
- Oracle存储过程和存储函数(1)
- android小白进阶MVP模式开发(三步曲),让你全面理解MVP进阶(二)
- Keil 按下Build编译全部文件的原因
- 免费馅饼(简单DP
- 排序算法(3)----归并排序
- 【Spring Boot实战】一 -- 简单了解Spring Boot
- 图像分类——细粒度的概念
- 线性非线性光耦 传输特性
- JVM-栈帧
- Vue 中$els 的用法和理解
- 程序员-PM怎么让项目预估的时间更加准确
- Leetcoed--Binary Tree Inorder Traversal (二叉搜索树--中序遍历)
- 润乾报表5填报表中如何添加html代码