【JAVA】归并排序

 

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。(速度仅次于快速排序,为稳定排序算法一般用于对总体无序,但是各子项相对有序的数列。)

算法描述

归并操作的工作原理如下:

第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置

第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

重复步骤3直到某一指针超出序列尾,将另一序列剩下的所有元素直接复制到合并序列尾

特点

归并排序是稳定的排序.即相等的元素的顺序不会改变。

如输入记录 1(1) 3(2) 2(3) 2(4) 5(5) (括号中是记录的关键字)时,输出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按输入的顺序。

这对要排序数据包含多个信息而要按其中的某一个信息排序,要求其它信息尽量按输入的顺序排列时很重要。

归并排序的比较次数小于快速排序的比较次数,移动次数一般多于快速排序的移动次数。

代码

package 归并排序;

import java.util.ArrayList;
import java.util.List;

/**
 * 归并排序
 */
//顾名思义,先进行递归,后进行合并。

public class Collections {
    /**
     * 递归
     *
     * @param list
     */
    public static void sort(List list) {
        int l = 0;
        //list.size()-1指的是最后一个索引
        int r = list.size() - 1;
        //链表用的是数组,索引从0开始。
    }


    /**
     * 归:分治
     */
    //先将集合传进来,通过数组或链表将其分成两个数组或两个链表。
    //实际上是将两个数组或链表的索引范围从逻辑上分治出来。
    //比如:0~10的数组,我们将其分为0~5的数组和6~10的数组。
    //而实际上还是一个数组,并没有真正分开。

    //list;链表\left;起始索引\right:结束索引
    //在这里要对List进行分,而分的条件是:left < right
    //而left == right的时候就说明只剩一个数组了,
    //即数组里剩下的未比较的元素就剩一个元素了。
    private static void devide(List list, int left, int right) {
        //这里分的时候,要先计算一下中值索引。
        int mid = (left + right) / 2;
        if (left < right) {
            //左边
            devide( list, left, mid );
            //右边
            devide( list, mid + 1, right );
            //这里不断进行递归,只要不像等,就一直在归。
            //一旦left和right相等的时候,就要并了。

            //} else {
            //这里要注意,当左右两边都分完的时候,
            //原数组里就剩下一个元素了。
            //此时,递归也就自动终止并返回分好ed数组了。
            //所以这里不用再判断,而是直接进行合并。

            /*并(merge)*/
            //我们可以将该方法写到下面的块里。
            //但是,为了方便,我们就在下面写并的方法。

            //合并两个数组
            merge( list, left, mid, mid + 1, right );

            //这里我们在写的时候,我们可以反着来写这个方法。
            //将光标放到上面哪一行代码。组合键alt+enter,
            //然后选择Creat method 'merge' in 'Collections'
        }
    }

    /**
     * 并
     *
     * @param list
     * @param left
     * @param mid
     * @param i
     * @param right
     */
    private static void merge(List list, int left, int mid, int i, int right) {

        //合并的时候,我们可以创建一个临时集合。
        Object[] temp = new Object[list.size()];
        //list.size()指的是数组长度

        int index = left;
        int ls = left, le = mid;//左边的起始索引和结束索引
        int rs = i, re = right;//右边的起始索引和结束索引

        //左又都是从第一个开始比,比完后。
        //如果左边的元素小的话将其放到临时数组里,
        //然后左边的索引加一,再比下一个。
        //如果右边的元素小的话将其放到临时数组里,
        //然后右边的索引加一,再比下一个。
        //如此一直到结束。
        //所以我们初始化一个索引为left,来记录索引。
        //index不能从0开始,而应该从left开始。
        //因为,先在我们比较的数组是逻辑数组,
        //它们的起始索引不一定是从0开始的。


        //至少把一边的所有数组元素按顺序放入临时数组。
        while (ls <= le && rs <= re) {
            //这个两个条件都必须满足,不然就退出了。
            //因为这样就意味着有一边已经排完了。

            //if(list.get(ls)<list.get(rs)){...}
            //这里对象是不能直接比的,比的应该是数据。

            //但是我们在这里可以实现Comparable接口。
            Comparable o1 = (Comparable) list.get( ls );
            Comparable o2 = (Comparable) list.get( rs );
            if (o1.compareTo( o2 ) == -1) {
                //这里判断o1是否小于o2

                //o1小于o2时则排左边
                //因为是升序排,所以先将o1放进临时数组里。
                temp[index] = o1;
                ls++;
            } else {
                //o1大于o2时则排右边
                temp[index] = o2;
                rs++;
            }
            index++;
            //至少把一边的所有数组元素按顺序放入临时数组。
            //但是还是会有剩下的元素。

            // 判断左边是否有剩余的元素。
            if (ls <= le) {
                for (int j = ls; j <= le; j++) {
                    temp[index++] = list.get( j );
                }
            }
            //也可能左边没有剩余的元素,那么我们就

            //判断右边是否有剩余元素

            //这里之所以还要等于re的原因是因为
            // temp[index] = o1;如果在这里放完了
            //ls++;会多加一,ls就比le大了。
            //所以,省的那个元素的索引ls正好是等于le的。
            if (rs <= re) {
                for (int k = rs; k <= re; k++) {
                    temp[index++] = list.get( k );
                }
            }

            //将临时数组里的元素放回原来的数组里
            for (int l = left; l <= right; l++) {
                list.set( l, temp[l] );
            }

        }
    }

    public static void main(String[] args) {
        List s = new ArrayList();
        s.add( new Student( 121, "张三", 'm' ) );
        s.add( new Student( 123, "李四", 'm' ) );
        s.add( new Student( 122, "王五", 'm' ) );
        java.util.Collections.sort( s );
        System.out.println( s );
    }
}
[Student{id=121, name='张三', sex=m}, Student{id=122, name='王五', sex=m}, Student{id=123, name='李四', sex=m}]
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_浮舍_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值