数组排序算法 --- java程序

1、数组排序算法

  • 数组a中有N个元素,将所有元素按从小到大的顺序排列。
  • 本文介绍几种常用的排序算法:冒泡排序、选择排序、直接插入排序、希尔排序、归并排序、快速排序、基数排序。

冒泡排序法:

  • 冒泡排序算法原理:

    依次比较两个相邻的元素,如果第一个比第二个大,就交换他们两个。
    即,在a[0]到a[N-1]的范围内,依次比较a[i]和a[i+1],i的值取0,1,…,N-2,若a[i]>a[i+1],则交换。第一次排序完成后,数组中最大的数放到a[N-1]处。

  • 原理图解:
    在这里插入图片描述

  • 冒泡排序法程序:

    1、分步原理推导过程。

    package org.practic.arraysort;
    import java.util.Arrays; 
    public class BubbleSort {
        public static void main(String[] args) {
            //冒泡排序法:分步推导
            int[] arr = {24, 69, 80, 57, 13};        
            StepSort(arr);       
        }
        
        //分步排序
        private static void StepSort(int[] arr) {
            //一共进行了arr.length-1 次排序
            //第一次排序,从0出开始,比较了arr.length-1 次,
            for (int i = 0; i < arr.length-1; i++) {
                ExchangeValue(arr, i);
            }
            System.out.println(Arrays.toString(arr));    //[24, 69, 57, 13, 80]
    
            //第二次排序,从0开始,比较了arr.length-2 次
            for (int i = 0; i < arr.length-2; i++) {
                ExchangeValue(arr, i);
            }
            System.out.println(Arrays.toString(arr));    //[24, 57, 13, 69, 80]
    
            //第三次排序,从0开始,比较了arr.length-3 次
            for (int i = 0; i < arr.length-3; i++) {
                ExchangeValue(arr, i);
            }
            System.out.println(Arrays.toString(arr));   //[24, 13, 57, 69, 80]
    
            //第四次排序,从0开始,比较了arr.length-4 次
            for (int i = 0; i < arr.length-4; i++) {
                ExchangeValue(arr, i);
            }
            System.out.println(Arrays.toString(arr));    //[13, 24, 57, 69, 80]
        }
    
        //数值交换
        private static void ExchangeValue(int[] arr, int i) {
                int t = arr[i];
                arr[i] = arr[i+1];
                arr[i+1] = t;      
        }
    }
    

    2、冒泡排序算法程序。

    一共进行了arr.length-1 次排序,j = 1 — arr.length-1;
    第 j 次排序比较了arr.length-j 次。

    package org.practic.arraysort;
    import java.util.Arrays;
    public class BubbleSort {
        public static void main(String[] args) {
            //冒泡排序法:
            int[] arr = {24, 69, 80, 57, 13};
    
            //外层循环控制排序次数,一共进行了arr.length-1 次排序
            for (int j = 1; j <= arr.length-1; j++) {
                //内层循环控制每次排序的比较次数
                for (int i = 0; i < arr.length - j; i++) {
                    ExchangeValue(arr, i);
                }
            }
            System.out.println(Arrays.toString(arr));
        }
    
        //数值交换
        private static void ExchangeValue(int[] arr, int i) {       
                int t = arr[i];
                arr[i] = arr[i+1];
                arr[i+1] = t;       
        }
    }
    

选择排序法:

  • 选择排序算法原理:

    首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

    即,第一次用a[0]依次和后面元素比较,如果其他数比a[0]大就交换,不然就不换。第一次完毕,最小值出现在了最小索引a[0]处。第二次再用a[1]依次和后面元素比较,依次进行。

  • 选择排序算法原理图解: 在这里插入图片描述

  • 选择排序法程序:

    1、分步原理推导过程。

    一共进行了arr.length-1 次排序,j = 1 — arr.length-1;
    ​ 第 j 次排序比较了arr.length-j 次。

    package org.practic.arraysort;
    import java.util.Arrays;
    public class SelectionSort {
        public static void main(String[] args) {
            int[] arr = {24, 69, 80, 57, 13};
            //分步推理
            StepSort(arr);
        }
    
        private static void StepSort(int[] arr) {
            //一共进行了arr.length-1 次排序
            //第1次排序,用arr[0]依次与后面的元素比较,一共比较了arr.length-1 次
            for (int i = 1; i <= arr.length-1; i++) {
                if(arr[0] > arr[i]){
                    ExchangeValue(arr,0,i);
                }
            }
            System.out.println(Arrays.toString(arr));   //[13, 69, 80, 57, 24]
    
            //第2次排序,用arr[1]依次与后面的元素比较,一共比较了arr.length-2 次
            for (int i = 2; i <= arr.length-1; i++) {
                if(arr[1] > arr[i]){
                    ExchangeValue(arr,1,i);
                }
            }
            System.out.println(Arrays.toString(arr));   //[13, 24, 80, 69, 57]
    
            //第3次排序,用arr[2]依次与后面的元素比较,一共比较了arr.length-3 次
            for (int i = 3; i <= arr.length-1; i++) {
                if(arr[2] > arr[i]){
                    ExchangeValue(arr,2,i);
                }
            }
            System.out.println(Arrays.toString(arr));   //[13, 24, 57, 80, 69]
    
            //第4次排序,用arr[3]依次与后面的元素比较,一共比较了arr.length-1 次
            for (int i = 4; i <= arr.length-1; i++) {
                if(arr[3] > arr[i]){
                    ExchangeValue(arr,3,i);
                }
            }
            System.out.println(Arrays.toString(arr));   //[13, 24, 57, 69, 80]
        }
    
        //数值交换
        private static void ExchangeValue(int[] arr,int j, int i) {
                int t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
        }
    }
    

    2、选择排序算法程序。

    一共进行了arr.length-1 次排序,j = 0 — arr.length-1-1;
    ​ 第 j 次排序用arr[j]和后面的元素依次比较。

    package org.practic.arraysort;
    import java.util.Arrays;
    public class BubbleSort {
        public static void main(String[] args) {
            //选择排序法
            int[] arr = {24, 69, 80, 57, 13};                
            //外层循环控制排序次数,一共进行了arr.length-1 次排序
            for (int j = 0; j < arr.length-1; j++) {
                //里层循环控制每次排序比较的次数,用arr[j]和后面的元素依次比较。
                for (int i = j+1; i <= arr.length - 1; i++) {               
                    if (arr[j] > arr[i]) {
                        ExchangeValue(arr, j, i);
                    }
                }
            }
            System.out.println(Arrays.toString(arr));   //
        }
    
     //数值交换
        private static void ExchangeValue(int[] arr, int i) {
             int t = arr[i];
             arr[i] = arr[i+1];
             arr[i+1] = t;
        }
    }
    

直接插入排序:

  • 直接插入排序算法原理:

    将一个记录插入到一个长度为m 的有序表中,使之仍保持有序,从而得到一个新的长度为m+1的有序列表。

    假设有一组元素{k1,k2…,kn},排序开始就认为k1是一个有序序列,让k2插入上述表长为1的有序序列,使之成为一个表长为2的有序序列,然后让k3插入上述表长为2的有序序列,使之成为一个表长为3的有序序列,以此类推,最后让kn插入表长为n-1的有序序列,得到一个表长为n的有序序列。

  • 原理图解:
    在这里插入图片描述

  • 直接插入法程序:

    public class DirectInsertionSort {
        public static void main(String[] args) {
            int[] arr ={24, 69, 80, 57, 13, 18, 51};
    
            //外层循环控制比较次数,一共进行了arr.length-1 次
            for (int j = 1; j < arr.length; j++) {
                //里层循环控制每次拿当前元素与之前的有序表中的元素比较
                for (int i = j; i > 0; i--) {
                    if(arr[i] < arr[i-1]){
                    ExchangeValue(arr,i,i-1);
                    }
                }
            }
            System.out.println(Arrays.toString(arr));   //[13, 18, 24, 51, 57, 69, 80]
        }
    
        //数值交换
        private static void ExchangeValue(int[] arr,int j, int i) {
            int t = arr[j];
            arr[j] = arr[i];
            arr[i] = t;
        }
    }
    
    public class DirectInsertionSort {
        public static void main(String[] args) {             
            int[] arr1 ={22, 69, 86, 79, 13, 4, 49};
            for (int i = 1; i < arr1.length; i++) {
                int j = i;
                while(j > 0 && arr1[j] < arr1[j-1]) {
                    ExchangeValue(arr1, j, j - 1);
                    j--;
                }
            }
            System.out.println(Arrays.toString(arr1));
        }
    
        //数值交换
        private static void ExchangeValue(int[] arr,int j, int i) {
            int t = arr[j];
            arr[j] = arr[i];
            arr[i] = t;
        }
    }
    

希尔排序法:

  • 希尔排序算法原理:

    希尔排序算法一般指希尔排序(Shell Sort),也称缩小增量排序。希尔排序法(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序的方法,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。

    即,先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。

    • 增量的合理选择:

      最普通的选择就是使用数组长度的一半,即:d1=数组长度/2,d2=d1/2。这种增量从效率来说,不是非常好。

  • 希尔排序法的原理图示:
    在这里插入图片描述

  • 希尔排序法的程序:

    1、增量选择为数组长度的一半。
    原始步长序列:N / 2,N / 4,…,1(重复除以2);

    public class ShellSort {
        public static void main(String[] args) {
            int[] arr = {46,55, 13,42,17,94,5,70};  
            //d 增量 d = arr.length/2
            for (int d = arr.length/2; d > 0; d /= 2) {
            	//以选择的增量为长度,进行比较
                for (int i = d; i < arr.length; i++) {
                    for (int j = i; j > d - 1; j -= d) {
                        if (arr[j] < arr[j - d]) {
                            ExchangeValue(arr, j, j - d);
                        }
                    }
                }
            }
            System.out.println(Arrays.toString(arr)); 
        }    
    
        private static void ExchangeValue(int[] arr, int i,int j) {
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
    
        }
    }
    

    2、增量选择为克努特序列。
    克努特(Knuth)的步长序列:1,4,13,…,(3 k - 1)/ 2;

    public class ShellSort {
        public static void main(String[] args) {
    
            int[] arr1 = {46,55, 13,42,17,94,5,70};  
            //克努特序列
            int t = 1;
            while (t <= arr1.length/3){
                t = t*3 + 1;
            }
    
            for (int h = t; h > 0; h = (h-1)/3) {
                //以选择的增量为长度,进行比较
                for (int i = h; i < arr1.length; i++) {
                    for (int j = i; j > h - 1; j -= h) {
                        if (arr1[j] < arr1[j - h]) {
                            ExchangeValue(arr1, j, j - h);
                        }
                    }
                }
            }
            System.out.println(Arrays.toString(arr1));
        }
    
        private static void ExchangeValue(int[] arr, int i,int j) {
            int t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
    }
    

归并排序:

  • 归并排序算法原理:
    归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

  • 归并排序算法原理图示:
    在这里插入图片描述
    分阶段:可以理解为就是递归拆分子序列的过程;
    治阶段:我们需要将两个已经有序的子序列合并成一个有序序列,比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],实现步骤如下:
    在这里插入图片描述
    在这里插入图片描述(参考资料:图解归并排序算法

  • 归并排序算法程序:

    public class MergeSort {
        public static void main(String[] args) {
            int[] arr = {13,8,93,20,47,38,65};
            splitSort(arr, 0, arr.length-1);
            System.out.println(Arrays.toString(arr));
        }
    
        //拆分
        public static void splitSort(int[] arr, int startIndex, int endIndex){
            int centerIndex = (startIndex+endIndex)/2;
            if(startIndex < endIndex){
                //拆分左边
                splitSort(arr,startIndex,centerIndex);
                //拆分右边
                splitSort(arr,centerIndex+1,endIndex);
                //合并
                mergerSort(arr, startIndex, centerIndex, endIndex);
            }
        }
    
        //合并
        public static void mergerSort(int[] arr, int startIndex, int centerIndex, int endIndex){
            //定义一个临时数组
            int[] tempArray = new int[endIndex-startIndex+1];
            //定义临时数组的起始索引
            int index = 0;
            //定义左边数组的起始索引
            int i = startIndex;
            //定义右边数组的起始索引
            int j = centerIndex+1;
    
            //比较合并
            while ( i <= centerIndex && j <= endIndex){
                if (arr[i] < arr[j]){
                    tempArray[index] = arr[i];
                    i++;
                }else{
                    tempArray[index] = arr[j];
                    j++;
                }
                index++;
            }
    
            //处理左边剩余
            while(i<=centerIndex){
                tempArray[index] = arr[i];
                i++;
                index++;
            }
    
            //处理右边剩余
            while( j <= endIndex ){
                tempArray[index] = arr[j];
                j++;
                index++;
            }
    
            //将临时数组的元素放回原数组
            for (int k = 0; k < tempArray.length; k++) {
                arr[k+startIndex] = tempArray[k];
            }
        }
    }
    

快速排序法:

  • 快速排序算法原理:
    快速排序(Quicksort)是对冒泡排序的一种改进。思想为:
    在这里插入图片描述

  • 实现思路:
    在这里插入图片描述
    在这里插入图片描述

  • 快速排序算法程序:

    public class QuickSort {
        public static void main(String[] args) {
            int[] arr = {2, 1, 0, 6, 9, 0, 10};
            quickSort(arr,0,arr.length-1);
            System.out.println(Arrays.toString(arr));
        }
        
        public static void quickSort(int[] arr,int startIndex,int endIndex){
            if(startIndex<endIndex){
                //找到基准数的索引
                int index=getIndex(arr,startIndex,endIndex);
    
                //对左右两边进行递归调用
                quickSort(arr,startIndex,index);
                quickSort(arr,index+1,endIndex);
            }
        }
    
        //挖坑填数,找出基准数
        public static int getIndex(int[] arr, int startIndex, int endIndex){
            int i = startIndex;
            int j = endIndex;
    
            //定义基准数,用第一个作为参数
            int x = arr[i];
    
            while(i<j) {
                //由后向前找比他小的数,找到后挖出此数填到前一个坑中
                while(i < j && arr[j] >= x){
                    j--;
                }
                //填坑
                if(i<j){
                    arr[i] = arr[j];
                    i++;
                }
    
                //由前向后找比他大的数,找到后挖出此数填到前一个坑中
                while(i < j && arr[i] <= x){
                    i++;
                }
                //填坑
                if(i<j){
                    arr[j] = arr[i];
                    j--;
                }
            }
            arr[i] = x;
            return i;
        }
    }
    

基数排序法 :

  • 基数排序算法原理:

    基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或 bin sort,顾名思义,它是透过元素的部分信息,将要排序的元素分配至某些“桶”中,以达到排序的作用。基数排序法是属于稳定性的排序,在某些时候,基数排序法的效率高于其它的稳定性排序法。

    基数排序不同于之前所介绍的排序算法,它不需要对元素进行比较,只需要对元素进行“分配”与“收集”两种操作即可完成。

  • 实现方法:

    最高位优先(Most Significant Digit first)法,简称MSD法:从元素的最左边开始,先按k1排序分组,同一组中记录,关键码k1相等,再对各组按k2排序分成子组,之后,对后面的关键码继续这样的排序分组,直到按最次位关键码kd对各子组排序后。再将各组连接起来,便得到一个有序序列。

    最低位优先(Least Significant Digit first)法,简称LSD法:从元素的最右边开始,先从kd开始排序,再对kd-1进行排序,依次重复,直到对k1排序后便得到一个有序序列。

  • 解法分析:

    以LSD为例,假设原来有一串数值为:73, 22, 93, 43, 55, 14, 28, 65, 39, 81。

    首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
    在这里插入图片描述
    然后将这些桶子中的数值按桶编号顺序取出来,重新串接在一起,成为以下的数列:81, 22, 73, 93, 43, 14, 55, 65, 28, 39

    接着再进行一次分配,这次是根据十位数来分配:
    在这里插入图片描述
    然后将这些桶子中的数值重新串接起来,成为以下的数列:14, 22, 28, 39, 43, 55, 65, 73, 81, 93。这时候整个数列已经排序完毕。

    如果排序的对象有三位数以上,则持续进行以上的动 作直至最高位数为止。

  • LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好。MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。在进行完最低位数的分配后再合并回单一的数组中。(详细可见添加链接描述

  • 基数排序算法程序:

package org.practic.arraysort;
import java.util.Arrays;
public class RadixSort {
    //基数排序
    public static void main(String[] args) {
        int[] arr = {73, 22, 93, 43, 55, 14, 28, 65, 39, 81};
        radixSort(arr);
        System.out.println(Arrays.toString(arr));
    }

    private static void radixSort(int[] arr) {
        //定义一个二维数组,用于10个桶分配数据
        int[][] tempArr = new int[10][arr.length];
        //定义一个统计数组,统计每个桶中放入的元素个数
        int [] count = new int[10];
        //获取数组中的最大值,计算最大值的位数,确定需要分配收集的轮次
        int max = getMax(arr);
        int len = String.valueOf(max).length();
        //获取各位上的数字,放入相应的桶中
        for (int i = 0,n = 1; i < len; i++,n *= 10) {
            for (int j = 0; j < arr.length; j++) {
                int s = arr[j] / n % 10;
                tempArr[s][count[s]++] = arr[j];
            }
            //取出桶中的数
            //遍历统计数组
            int index = 0;
            for (int p = 0; p < count.length; p++) {
                if( count[p] != 0){
                    for (int k = 0; k < count[p]; k++) {
                        arr[index] = tempArr[p][k];
                        index++;
                    }
                }
                //取完之后,清除上次统计的个数
                count[p] = 0;
            }
        }
    }

    private static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if( max < arr[i] ){
                max = arr[i];
            }
        }
        return max;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值