几种常见排序算法的简单学习总结

 如下是我对一些常见的排序算法的简单总结:包括有直接插入排序、希尔排序、直接选择排序、冒泡排序、快速排序、归并排序、桶排序(基数排序)。

目录如下:

1、直接插入排序

2、希尔排序

3、直接选择排序

4、冒泡排序

5、快速排序

6、二路归并排序

7、基数排序(桶排序)

1、直接插入排序

基本思想: 顺序地把待排序的元素数据按照其关键字值的大小插入到已排序数据元素子集合的适合位置。子集合的数据元素个数从只有一个数据元素开始,逐次增大,当子集合大小最终和集合大小相同时,排序完成。

例图理解:

代码实现:以下的代码仅代表我个人思路,有更好的实现可以自行选择

   public static int[] InsertSort(int[] arry){
        int temp;
        for (int i = 1; i < arry.length; i++) {
            int j=i;
            while (j>0&&arry[j]<arry[j-1]){
                temp=arry[j-1];
                arry[j-1]=arry[j];
                arry[j]=temp;
                j--;
            }
        }
        return arry;
    }

2、希尔排序

基本思想:把待排序的数据元素分成若干个小组,对同一小组内的数据元素用直接插入法排序;小组的个数逐次减少;当完成了所有元素都在一个组内的排序后,排序过程结束。希尔排序又叫做缩小增量排序

例图理解:

增量d 的范围:  1<= d < 待排序数组的长度 
增量的取值: 一般的初次取序列(数组)的一半为增量,以后每次减半,直到增量为1。
第一个增量=数组的长度/2,
第二个增量= 第一个增量/2,
第三个增量=第二个增量/2,
以此类推,最后一个增量=1。

代码实现:以下的代码仅代表我个人思路,有更好的实现可以自行选择

 public static int[]  ShellSort(int[] array){
        if(array.length<=1&&array==null){
            return array ;
        }
        for(int incr=array.length/2;incr>0;incr/=2){   //incr为每一次的增量
            for (int i = 0; i <incr; i++) {     //增量为incr,即有incr组数据
                //下面进行直接插入排序
                for(int j=i+incr;j<array.length;j+=incr){
                    int k=j;
                    while (k-incr>=0&&array[k-incr]>array[k]){
                       int temp=array[k-incr];
                        array[k-incr]=array[k];
                        array[k]=temp;
                        k-=incr;
                    }
                }
            }
        }
         return  array;
    }

3、直接选择排序

基本思想:第一趟从n个元素的数据序列中选出关键字最小(或最大)的元素并放到最前(或最后)位置,下一趟再从n-1个元素中选出最小(或最大)的元素并放到次前(后)位置,以此类推,经过n-1趟完成排序。

例图理解:

代码实现:以下的代码仅代表我个人思路,有更好的实现可以自行选择

public static int[] SelectSort(int[] array){
        for (int i = 0; i < array.length; i++) {
            int mindex=i;
            for (int j=i;j<array.length;j++){
               if(array[j]<=array[mindex]){
                   mindex=j;
               }
            }

            if(mindex!=i){
                int temp=array[i];
                array[i]=array[mindex];
                array[mindex]=temp;
            }
        }
        return  array;
    }

4、冒泡排序

基本思想:

设数组a中存了n个数据,循环进行n-1趟如下的排序过程:

第一趟时,依次比较相邻两个元素啊a[i]和a[i+1](i=0,1,2,3.......n-2),若为逆序,即a[i]>a[i+1],则交换两个数据元素,否则不交换,这样数值最大的数据元素将被放置在a[n-1]中;第2趟时,数据元素个数减1,即数据元素个数为n-1,操作方法与第1趟类似,这样整个n个数据元素集合中数值次大的数据元素就放置在了a[n-2]中。这样一直重复做操作,直到排序完成为止。
 

例图理解:

代码实现:以下的代码仅代表我个人思路,有更好的实现可以自行选择。

public static  int[]  BubbleSort(int[]  array){
        int flag=1;  
        for (int i = 0; i < array.length&&flag==1; i++) {
            flag=0;    //将flag置为0,下面有交换位置的操作的话,就会置为1
            for(int j=0;j<array.length-i-1;j++){
                if(array[j]>array[j+1]){
                    flag=1;
                    int temp=array[j];
                    array[j]=array[j+1];
                    array[j+1]=temp;
                }
            }
        }
        return array;
    }

有一个巧妙的地方,就是这个flag标记位:因为有时候待排列的数据元素其实基本已经有序,不用再循环比较了。而在每次内循环前flag=0,用标记位flag来标记每一次内循环中是否有交换操作,如果有一次内循环已经没有了交换的话,flag不会置为1,则后面就不用再进入内循环了。提前结束了排序,大大增强了排序的性能。

5、快速排序

基本思想:

快速排序所采用的思想是分治的思想。所谓分治,就是指以一个数为基准,将序列中的其他数往它两边“扔”。以从小到大排序为例,比它小的都“扔”到它的左边,比它大的都“扔”到它的右边,然后左右两边再分别重复这个操作,不停地分,直至分到每一个分区的基准数的左边或者右边都只剩一个数为止。这时排序也就完成了。

三个步骤:

(1)选择基准:在待排序列中,按照某种方式挑出一个元素,作为 "基准"。

(2)分割操作:以该基准在序列中的实际位置,把序列分成两个子序列。此时,在基准左边的元素        都比该基准小,在基准右边的元素都比基准大。

(3)递归地对两个序列进行快速排序,直到序列为空或者只有一个元素。

例图理解:(百度搜到的一组含有意思的图解)

以初始序列“6 1 2 7 9 3 4 5 10 8”为例:两端开始“探测”,先从找一个小于6的数,再从找一个大于6的数,然后交换他们。这里可以用两个变量i和j,分别指向序列最左边和最右边。我们为这两个变量起个好听的名字“哨兵i”和“哨兵j”。刚开始的时候让哨兵i指向序列的最左边(即i=1),指向数字6。让哨兵j指向序列的最右边(即=10),指向数字。

首先哨兵j开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵j先出动,这一点非常重要(请自己想一想为什么)。哨兵j一步一步地向左挪动(即j–),直到找到一个小于6的数停下来。接下来哨兵i再一步一步向右挪动(即i++),直到找到一个数大于6的数停下来。最后哨兵j停在了数字5面前,哨兵i停在了数字7面前。

现在交换哨兵i和哨兵j所指向的元素的值。交换之后的序列如下:

6 1 2 5 9 3 4 7 10 8

到此,第一次交换结束。接下来开始哨兵j继续向左挪动(再友情提醒,每次必须是哨兵j先出发)。他发现了4(比基准数6要小,满足要求)之后停了下来。哨兵i也继续向右挪动的,他发现了9(比基准数6要大,满足要求)之后停了下来。此时再次进行交换,交换之后的序列如下:

6 1 2 5 4 3 9 7 10 8

第二次交换结束,“探测”继续。哨兵j继续向左挪动,他发现了3(比基准数6要小,满足要求)之后又停了下来。哨兵i继续向右移动,糟啦!此时哨兵i和哨兵j相遇了,哨兵i和哨兵j都走到3面前。说明此时“探测”结束。我们将基准数6和3进行交换。交换之后的序列如下:

3 1 2 5 4 6 9 7 10 8

             

 

到此第一轮“探测”真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。回顾一下刚才的过程,其实哨兵j的使命就是要找小于基准数的数,而哨兵i的使命就是要找大于基准数的数,直到i和j碰头为止。

OK,解释完毕。现在基准数6已经归位,它正好处在序列的第6位。此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是“3 1 2 5 4”,右边的序列是“9 7 10 8”。接下来还需要分别处理这两个序列。

总体过程例图:

代码实现:以下的代码仅代表我个人思路,有更好的实现可以自行选择

   public static void quickSort(int[] array,int low,int high){
        int i,j,temp,t;
        if(low>high){
            return ;
        }
        i=low;
        j=high;
        temp = array[low];    //temp即为基准位

        while (i<j) {
            //先看右边,依次往左递减
            while (temp<=array[j]&&i<j) {
                j--;
            }
            //再看左边,依次往右递增
            while (temp>=array[i]&&i<j) {
                i++;
            }

            //如果满足条件则交换
            if (i<j) {
                t = array[j];
                array[j] = array[i];
                array[i] = t;
            }

        }

        //到此处已经找到了满足左小右大的位置,最后将基准为与此位置的数字交换
        array[low] = array[i];
        array[i] = temp;


        //递归调用左半数组
        quickSort(array, low, j-1);
        //递归调用右半数组
        quickSort(array, j+1, high);
    }

6、二路归并排序

基本思想::将原始无序序列划分为子序列,先使每个子序列有序,然后将有序的子序列合并,得到完全有序的序列。

步骤:首先将初始序列的n个记录看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2向上取整(n为奇数时,最后一个序列的长度为1)的有序序列。在此基础上,再对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列。以此类推,直到得到一个长度为n的有序序列为止。
 

例图理解:

 

代码实现:以下的代码仅代表我个人思路,有更好的实现可以自行选择

public static  int[] dichotomyMerge(int[] array){

    //二路分组的尽头,也是递归结束的地方
    if(array.length<3){//含有两个或者一个元素
        if(array[0]>array[array.length-1]){//对2个或者1个元素进行排序
            int tmp=array[0];
            array[0]=array[array.length-1];
            array[array.length-1]=tmp;
        }
        return array;
    }

    //说明分组还没结束,继续二路分组
    int center =array.length/2;
    int[] left =Arrays.copyOfRange(array,0,center);
    int[] right = Arrays.copyOfRange(array,center,array.length);
    left=dichotomyMerge(left);
    right=dichotomyMerge(right);


    //分完组之后,要进行二路归并(此时每个组都是有序的)
    int i=0, l =0, r=0;//i:新数组的下标,l:left数组的下标,r:rigth数组的下标
    while (l<left.length&&r<right.length) {//二路归并,直到其中一路完全归入新数组
        if(left[l]<right[r]){
            array[i++]=left[l++];
        }else if(left[l]==right[r]){
            array[i++]=left[l++];
            array[i++]=right[r++];
        }else {
            array[i++]=right[r++];
        }
    }
    
    if(l==left.length){//将另外一路也归入新数组
        for (int j = r; j < right.length; j++) {
            array[i++]=right[j];
        }
    }else{//将另外一路也归入新数组
        for (int j = l; j < left.length; j++) {
            array[i++]=left[j];
        }
    }
    return array;

};

7、基数排序(桶排序)

基本思想:

将数据分到有限数量的桶子里。每个桶子再个别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)。
      例如要对大小为[1..1000]范围内的n个整数A[1..n]排序,可以把桶设为大小为10的范围,具体而言,设集合B[1]存储[1..10]的整数,集合B[2]存储(10..20]的整数,……集合B[i]存储((i-1)*10, i*10]的整数,i = 1,2,..100。总共有100个桶。然后对A[1..n]从头到尾扫描一遍,把每个A[i]放入对应的桶B[j]中。 然后再对这100个桶中每个桶里的数字排序,这时可用冒泡,选择,乃至快排,一般来说任何排序法都可以。最后依次输出每个桶里面的数字,且每个桶中的数字从小到大输出,这样就得到所有数字排好序的一个序列了。   

例图理解:

 

代码实现:以下的代码仅代表我个人思路,有更好的实现可以自行选择

  /*基数排序(桶排序)*/
    public static int[] bucketSort(int[] array) {
        // buckets为桶的集合,每个桶为一个LinkedList,里面存Interger类型
        ArrayList<LinkedList<Integer>>  buckets = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            buckets.add(new LinkedList<>());
        }

        //获取给的数据中最大值,以确定后面桶排序的次数
        int Max=0;
        for (int value : array) {
            if (value > Max) {
                Max = value;
            }
        }
        int len=String.valueOf(Max).length();


      
       //注意:我在这个里是以正整数为例来找下标。根据业务的不同,寻找下标的方法也不同。
        for (int i = 0; i <len; i++) {
            int[] arr=array.clone();    //创建一个数组用于计算下标。
            // 数据放入桶中完成第i+1次排序
            for(int j=0;j<array.length;j++){
                  //寻找下标前,先将arr[]除以Math.pow(10,i)
                  int flag=(int)arr[j]/((int) Math.pow(10,i));  
                  int index=flag%10;   //找到对应的桶
                  buckets.get(index).offer(array[j]);    //将数据放到对应的桶中去,并且在桶中排序
           }

            //将所有数据取出来,更新array数组
            int dex = 0;
            for (LinkedList<Integer> bucket : buckets) {
               while (!bucket.isEmpty()){
                   array[dex]=bucket.poll();
                   dex++;
               }
            }
            
        }
        return  array;
    }

好了,如上就是我个人对一些常见的排序算法的总结学习!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值