排序算法(3)----归并排序

来源:互联网 发布:kali linux配置ip地址 编辑:程序博客网 时间:2024/06/05 07:16

这篇博文分为四个部分:

  1. 归并排序基本思想
  2. 基础代码实现与解析
  3. 代码一次优化
  4. 代码二次优化(自底向上的归并排序)

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++) {  //karr数组中本轮归并中最小值将要覆盖掉的下标        //防止越界,要保证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)
这个方法,就可以实现归并排序

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机移动网络信号不好怎么办 移动的4g网络差怎么办 4g移动网络慢怎么办 房间没有4g网络怎么办 oppo显示2g网络怎么办 oppo只有2g网络怎么办 移动卡4g网络慢怎么办 易信专线电话用完了怎么办 手机拨打电话时黑屏怎么办 网易号文章一直审核中怎么办 登陆积分会员忘记密码怎么办 易信密码忘记了怎么办 滴滴永久封停的号怎么办 微信版本过低怎么办 报关时通关单号没录怎么办 公司注销了行政许可证怎么办呢 报关项数超过50项怎么办 出口报关件数报多了怎么办 关税少交被海关缉私查到怎么办 外贸报关hs编码报错怎么办 报关金额少报了怎么办 进口报关金额少报了怎么办 报关重量报少了怎么办 金额报多100倍怎么办 ems没扫描到单号怎么办 回国海关被税了怎么办 代购买错东西了怎么办 征信报告有逾期怎么办 evus信息填错了怎么办 清关一个月了该怎么办 清关一直不发怎么办 清关一个月了怎么办 香港ems到西安海关税怎么办 寄东西被海关扣留怎么办 天津港新舱单品名错误核销怎么办 移动手机不能用联通卡怎么办 移动手机联通卡网速卡怎么办 移动手机不支持联通4g怎么办 汽车分离轴承异响怎么办 至尊宝被冻结了怎么办 qq冻结了至尊宝怎么办