Java几种常见的排序算法(复杂度,算法简介,代码实现)

目录

1. 几种常见的排序算法的复杂度

2. 冒泡排序

2.1 什么是冒泡排序?

2.2 代码实现

2.3 运行结果

3. 选择排序

3.1 什么是选择排序?

3.2 代码实现

3.3 运行结果

4. 插入排序

4.1 什么是插入排序

4.2 代码实现

4.3 运行结果

5. 希尔排序

5.1 什么是希尔排序?

5.2 代码实现

5.3 运行结果

6. 快速排序

6.1 什么是快速排序?

6.2 快速排序的思路

6.3 代码实现

6.4 运行结果

7. 归并排序

7.1 什么是归并排序?

7.2 代码实现

7.3 运行结果


写在前面:

今天,参考了下方文章,自己手动写了几种常见排序算法的代码。此文非常清晰明了,推荐:

Java的几种常见排序算法 - 小不点丶 - 博客园 (cnblogs.com)

1. 几种常见的排序算法的复杂度

 图片来自上述文章

2. 冒泡排序

2.1 什么是冒泡排序?

基本思想是:

从头开始让相邻的两个元素进行比较,符合条件就交换位置,这样就可以把最大值或者最小值放到数组的最后面了;

接着再从头开始两两比较交换,直到把最大值或者最小值放到数组的倒数第二位(即不需要与最后一位数进行对比).....

以此类推,直到排序完成。

2.2 代码实现

/*冒泡排序
1. 通过每一次遍历获取最大/最小值
2. 将最大值/最小值放在尾部/头部
3. 然后除开最大值/最小值,剩下的数据在进行遍历获取最大/最小值
**/

package com.my.demo;

public class BubbleSort {
    public static void main(String[] args) {
        int arr[] = {6, 4, 7, 8, 1};

        //打印排序前数组
        System.out.println("排序前:");  
        printArr(arr);

        //打印排序过程
        System.out.println("\n冒泡排序过程:");
        for(int i=0; i<arr.length; i++){  //外层循环
            for(int j=0; j<arr.length-1-i; j++){   //内层循环,注意内层循环的次数 arr.length-1-i
                if(arr[j] > arr[j+1]){  //注意:此代码是实现将最大值放到最后的效果排序
                    int tmp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = tmp;
                }
            }
            printArr(arr);
        }

        //打印排序后数组
        System.out.println("\n排序后:");  //打印排序后数组
        printArr(arr);
    }

    public static void printArr(int[] arr){
        for(int k=0; k<arr.length; k++){
            System.out.print(arr[k]);
            System.out.print(" ");
        }
        System.out.println();
    }
}

2.3 运行结果

排序前:
6 4 7 8 1 

冒泡排序过程:
4 6 7 1 8 
4 6 1 7 8 
4 1 6 7 8 
1 4 6 7 8 
1 4 6 7 8 

排序后:
1 4 6 7 8 

Process finished with exit code 0

3. 选择排序

3.1 什么是选择排序?

选择排序(Selection-sort)是一种简单直观的排序算法,基本思想是:

(1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置(第一趟先假设第一个元素就是最小值)。

(2)然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾......

(3)以此类推,直到所有元素均排序完毕。

3.2 代码实现

/* 选择排序
1. 将第一个值看成最小值,然后和后续的比较,找出最小值和下标
2. 交换本次遍历的起始值和最小值
3. 每次遍历的时候,将前面找出的最小值,看成一个有序的列表,后面的看成无序的列表,然后每次遍历无序列表找出最小值。
**/
package com.my.demo;

public class SelectionSort {

    public static void main(String[] args) {
        int arr[] = {9, 3, 8, 1, 5, 2};
        
        //打印排序前数组
        System.out.println("排序前:");  
        printArr(arr);

        //打印排序过程
        int min;  //存放每轮(外层循环的)循环的最小值
        int index;  //存放每轮(外层循环的)循环的最小值的索引
        System.out.println("\n选择排序过程:");
        for(int i=0; i<arr.length; i++){  //外层循环
            min = arr[i];  //假设数组未排序部分的第一个是最小值
            index = i; //最小值的下标
            for(int j=i+1; j<arr.length; j++){  //内层循环,一次循环结束,即找出数组未排序部分的最小值及其下标
                if(min > arr[j]){
                    min = arr[j];
                    index = j;
                }
            }
            //将最小值与本次循环开始设置的假设最小值,进行交换
            //将i前面的数据看成一个排好的队列,i后面的看成一个无序队列。每次只需要未排序部分的最小值,做替换。
            int tmp = arr[i];
            arr[i] = min;
            arr[index] = tmp;

            printArr(arr);
        }

        //打印排序后数组
        System.out.println("\n选择排序后:");  //打印排序后数组
        printArr(arr);
    }

    public static void printArr(int[] arr){
        for(int k=0; k<arr.length; k++){
            System.out.print(arr[k]);
            System.out.print(" ");
        }
        System.out.println();
    }
}

3.3 运行结果

排序前:
9 3 8 1 5 2 

选择排序过程:
1 3 8 9 5 2 
1 2 8 9 5 3 
1 2 3 9 5 8 
1 2 3 5 9 8 
1 2 3 5 8 9 
1 2 3 5 8 9 

选择排序后:
1 2 3 5 8 9 

Process finished with exit code 0

4. 插入排序

4.1 什么是插入排序

插入排序是一种简单直观的排序算法,基本思想是:

(1)通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

(2)插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间...

(3)直到所有元素均排序完毕。

4.2 代码实现

/* 插入排序
1. 默认从第二个数据开始比较。
2. 如果第二个数据比第一个小,则交换。然后再用第三个数据比较,如果比前面小,则插入。否则,退出循环。
3. 说明:默认将第一数据看成有序列表,后面无序的列表循环每一个数据,如果比前面的数据小则插入(交换)。否则退出。
 **/

package com.my.demo;

public class InsertionSort {

    public static void main(String[] args) {
        int arr[] = {5, 3, 6, 1, 9, 0};

        //打印排序前数组
        System.out.println("排序前:");  
        printArr(arr);

        //打印排序过程
        System.out.println("\n插入排序过程:");
        for(int i=1; i<arr.length; i++){  //外层循环,i=1从第二个开始比较,第一个则默认为已经拍好的有序列表
            for(int j=i; j>0; j--){  //内层循环,与前面排好序的有序列表比较,如果后面的数据小于前面的则交换
                if(arr[j] < arr[j-1]){   //该第j项比它前面的第j-1个小,则有可能比第j-2个也小,所以先j和j-1互换一次,然后再继续往前比
                    int tmp = arr[j-1];
                    arr[j-1] = arr[j];
                    arr[j] = tmp;
                } else {  //如果第j个已经比第j-1个大了,那肯定比第j-2个往前的数据都大,因为前面j-1个是已经从小到大排好序了的
                    break;  //如果不小于,说明插入完毕,退出内层for循环
                }
            }
            printArr(arr);
        }

        //打印排序后数组
        System.out.println("\n插入排序后:");  //打印排序后数组
        printArr(arr);
    }

    public static void printArr(int[] arr){
        for(int k=0; k<arr.length; k++){
            System.out.print(arr[k]);
            System.out.print(" ");
        }
        System.out.println();
    }
}

4.3 运行结果

排序前:
5 3 6 1 9 0 

插入排序过程:
3 5 6 1 9 0 
3 5 6 1 9 0 
1 3 5 6 9 0 
1 3 5 6 9 0 
0 1 3 5 6 9 

插入排序后:
0 1 3 5 6 9 

Process finished with exit code 0

5. 希尔排序

5.1 什么是希尔排序?

希尔排序(Shell's sort)是插入排序的一种,是直接插入排序算法的一种更高版本的改进版本。

基本思想是:

通过间隔多个数据来进行插入排序。

(1)将数组列在一个表中,并对列分别进行插入排序。

(2)重复这过程,不过每次用更长的列(步长更长了,列数更少了)来进行。

(3)最后整个表就只有一列了。

5.2 代码实现

/**
 * 希尔排序(Shell’s Sort)是插入排序的一种,是直接插入排序算法的一种更高版本的改进版本。
 *
 * 把记录按步长gap分组,对每组记录采用直接插入排序方法进行排序;
 * 随着步长逐渐减小,所分成的组包含的记录越来越多;
 * 当步长值减小到1时,整个数据合成一组,构成一组有序记录,完成排序;
 */

package com.my.demo;

public class ShellSort {
    public static void main(String[] args) {
//        int[] arr = {27, 12, 53, 28, 19, 44, 36, 79, 10};
        int[] arr = {9, 1, 2, 5, 7, 4, 8, 6, 3, 5};

        //打印排序前数组
        System.out.println("排序前:");
        printArr(arr);

        //打印排序过程
        System.out.println("\n希尔排序过程:");
        shellSort(arr);

        //打印排序后数组
        System.out.println("\n希尔排序后:");  //打印排序后数组
        printArr(arr);
    }

    public static void shellSort(int[] arr){
        //第一层循环,设置每一趟排序的步长。把所有数据按步长gap分组。通过减半的方式来实现。
        for(int step= arr.length/2; step>0; step=step/2){
            //第二层循环,具体实现一趟排序,把所有数据过一遍。对每组记录采用直接插入排序方法进行排序
            //在这层循环中,每一次循环,即每一个i的取值,完成一组记录(这里的记录,不是完整的所有数据,只是一部分)的排序
            //对一个步长区间进行比较 [step,arr.length]
            for(int i=step; i<arr.length; i++){
                int value = arr[i];
                int j;  //这里声明的原因是,不只是在第三层循环使用

                //第三层循环,具体实现每一组记录的排序
                //对步长区间中具体的元素进行比较
                for(j=i-step; j>=0 && arr[j]>value; j=j-step){
                    //j为左区间的取值,j+step为右区间与左区间的对应值。
                    arr[j+step] = arr[j]; //注意:这里没有直接用arr[i]=arr[j]
                }
                //此时j为一个负数,[j + step]为左区间上的初始交换值
                arr[j+step] = value;  //因为退出第三层循环之前,减去了一次step,这里加回去,再赋值
            }
            printArr(arr);
        }
    }

    public static void printArr(int[] arr){
        for(int k=0; k<arr.length; k++){
            System.out.print(arr[k]);
            System.out.print(" ");
        }
        System.out.println();
    }

}

5.3 运行结果

排序前:
9 1 2 5 7 4 8 6 3 5 

希尔排序过程:
4 1 2 3 5 9 8 6 5 7 
2 1 4 3 5 6 5 7 8 9 
1 2 3 4 5 5 6 7 8 9 

希尔排序后:
1 2 3 4 5 5 6 7 8 9 

Process finished with exit code 0

6. 快速排序

6.1 什么是快速排序?

(推荐此文,说得非常清晰:基于Java实现的快速排序)

是一种排序执行效率很高的排序算法,利用分治法来对待排序序列进行分治排序,基本思想是:

(1)通过一趟排序将待排记录分隔成独立的两部分,其中的一部分比关键字小,后面一部分比关键字大.

(2)然后再对这前后的两部分分别采用这种方式进行排序,通过递归的运算.

(3)最终达到整个序列有序。

6.2 快速排序的思路

这里用一个数组来逐步逐步说明快速排序的思路:

(1)假设对数组{7, 1, 3, 5, 13, 9, 3, 6, 11}进行快速排序。

(2)首先在这个序列中找一个数作为基准数,为了方便可以取第一个数。

(3)遍历数组,将小于基准数的放置于基准数左边,大于基准数的放置于基准数右边

(4)完成第一轮/层遍历之后,得到类似于这种排序的数组{3, 1, 3, 5, 6, 7, 9, 13, 11}。即:在初始状态下7是第一个位置,现在需要把7挪到中间的某个位置k,也即k位置是两边数的分界点。

(6)那如何做到把小于和大于基准数7的值分别放置于两边呢?

我们采用双指针法从数组的两端分别进行比对

先从最右位置往左开始找直到找到一个小于基准数的值,记录下该值的位置(记作 i)。

再从最左位置往右找直到找到一个大于基准数的值,记录下该值的位置(记作 j)。

如果位置i<j,则交换i和j两个位置上的值,然后继续从(j-1)的位置往前(i+1)的位置往后重复上面比对基准数然后交换的步骤。

(7)如果执行到i==j,表示本次比对已经结束,将最后 的位置的值与基准数做交换。

此时基准数就找到了临界点的位置k,位置k两边的数组都比当前位置k上的基准值或都更小或都更大。

即:基准值7已经把数组分为了两半,基准值7算是已归位(找到排序后的位置)

(8)通过相同的排序思想,利用递归算法,分别对7两边的数组进行快速排序,左边对[left, k-1]子数组排序,右边则是[k+1, right]子数组排序

(9)直到所有元素均排序完毕。

6.3 代码实现

/**
 * 快速排序
 * 核心算法:递归
 */

package com.my.demo;

public class QuickSort {

    public static void main(String[] args) {
        int[] arr = {7, 2, 5, 8, 1, 4, 6, 9, 0};
        System.out.println("排序前:");  //打印排序前数组
        printArr(arr);

        System.out.println("\n快速排序过程:");
        quickSort(arr, 0, arr.length-1);  //调用快排方法,传3个参数

        System.out.println("\n快速排序后:");  //打印排序后数组
        printArr(arr);
    }

    public static void quickSort(int[] arr, int left, int right){
        if(left > right){  //递归结束的出口,这个不能漏
            return ;
        }

        int base = arr[left];  //将待排序的部分的最左边一个,作为基准,存在base中
        int i = left, j = right;
        while(i != j){  //如果没有再在上面的if语句中退出,则i<=j的。这里设置循环退出的条件是i==j
            while(arr[j] >= base && i < j){ //注意:顺序很重要,要先从右边开始往左找,直到找到比base值小的数,j记录下标
                j--;
            }
            while( arr[i] <= base && i < j){ //再从左往后找,直到找到比base值大的数,i记录下标
                i++;
            }

            if(i<j){  // 上面两个循环结束,表示找到了位置或者(i>=j)了,交换两个数在数组中的位置,即j和i下标所对应的数
                int tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
            }
        }
        //跳出循环,此时已经完成了一轮排序,即已经将原数组分为两部分,一部分比基准数小,一部分比基准数大。现在就差将基准数放到这两部分中间的位置了
        //下方将基准数放到中间的位置,实现基准数归为
        arr[left] = arr[i];
        arr[i] = base;
        printArr(arr);

        //递归调用
        //在当前基准数的左边和右边,分别执行上面的操作
        //i的索引处为上面已确定好的当前的基准值的位置,无需再处理
        quickSort(arr, left, i-1);
        quickSort(arr, i+1, right);
    }

    public static void printArr(int[] arr){
        for(int k=0; k<arr.length; k++){
            System.out.print(arr[k]);
            System.out.print(" ");
        }
        System.out.println();
    }
}

6.4 运行结果

排序前:
7 2 5 8 1 4 6 9 0 

快速排序过程:
6 2 5 0 1 4 7 9 8 
4 2 5 0 1 6 7 9 8 
0 2 1 4 5 6 7 9 8 
0 2 1 4 5 6 7 9 8 
0 1 2 4 5 6 7 9 8 
0 1 2 4 5 6 7 9 8 
0 1 2 4 5 6 7 9 8 
0 1 2 4 5 6 7 8 9 
0 1 2 4 5 6 7 8 9 

快速排序后:
0 1 2 4 5 6 7 8 9 

Process finished with exit code 0

7. 归并排序

7.1 什么是归并排序?

归并排序是一种概念上最简单的排序算法,与快速排序一样,归并排序也是基于分治法的。基本思想是:

(1)归并排序将待排序的元素序列分成两个长度相等的子序列,为每一个子序列排序。

(2)然后再将他们合并成一个子序列。合并两个子序列的过程也就是两路归并。

(3)直到所有元素均排序完毕。

即:将数组先对半拆成最小单位,然后将两半数据合并成一个有序的列表。

7.2 代码实现

/**
 * 合并排序
 * 将列表按照对等的方式进行拆分
 * 拆分小最小快的时候,在将最小块按照原来的拆分,进行合并
 * 合并的时候,通过左右两块的左边开始比较大小。小的数据放入新的块中
 * 说明:简单一点就是先对半拆成最小单位,然后将两半数据合并成一个有序的列表。
 */

package com.my.demo;//package com.my.demo;

public class MergeSort {

    public static void main(String[] args) {
        int[] arr = {27, 12, 53, 28, 19, 44, 36, 79, 10};
//        int[] arr = {7, 2, 5, 8, 1, 4, 6, 9, 0};
        System.out.println("排序前:");  //打印排序前数组
        printArr(arr);

        System.out.println("\n合并排序过程:");
        mergeSort(arr, 0, arr.length-1);

        System.out.println("\n合并排序后:");  //打印排序后数组
        printArr(arr);

    }

    //两路归并算法,两个排好序的子序列合并为一个子序列
    //通过递归分别实现两个子序列的排序
    public static void mergeSort(int[] arr, int start, int end){
        if(start<end){
            int mid = (start + end)/2;
            mergeSort(arr, start, mid);
            mergeSort(arr, mid+1, end);
            merge(arr, start, mid, end);
            printArr(arr);
        }
    }

    //实现两个子序列的合并过程
    public static void merge(int[] arr, int left, int mid, int right){
        int[] tmp = new int[arr.length]; //暂存数组,辅助作用
        int pl = left;  //左子序列的起点
        int pr = mid+1;  //右子序列的起点
        int p = left;  //类似指针,用于协助往暂存数组中逐个放排序好的数据

        while(pl<=mid && pr<=right){
            if(arr[pl]<=arr[pr]){
                tmp[p] = arr[pl];
                p++;
                pl++;
            } else {
                tmp[p] = arr[pr];
                p++;
                pr++;
            }
        }

        while(pl<=mid){ //如果左子序列未检测完,直接将后面所有元素加到合并的序列中
            tmp[p] = arr[pl];
            p++;
            pl++;
        }
        while(pr<=right){ //如果右子序列未检测完,直接将后面所有元素加到合并的序列中
            tmp[p] = arr[pr];
            p++;
            pr++;
        }

        //将暂存序列中的数据复制回原数组
        //注意:原数组的起点和重点,并非0和length-1. 而是正在处理的该子序列在数组中的实际位置的索引。
        //跟递归有关
        for(int i=left; i<=right; i++){
            arr[i] = tmp[i];
        }
    }

    public static void printArr(int[] arr){
        for(int k=0; k<arr.length; k++){
            System.out.print(arr[k]);
            System.out.print(" ");
        }
        System.out.println();
    }
}

7.3 运行结果

排序前:
27 12 53 28 19 44 36 79 10 

合并排序过程:
12 27 53 28 19 44 36 79 10 
12 27 53 28 19 44 36 79 10 
12 27 53 19 28 44 36 79 10 
12 19 27 28 53 44 36 79 10 
12 19 27 28 53 36 44 79 10 
12 19 27 28 53 36 44 10 79 
12 19 27 28 53 10 36 44 79 
10 12 19 27 28 36 44 53 79 

合并排序后:
10 12 19 27 28 36 44 53 79 

Process finished with exit code 0

猜你喜欢

转载自blog.csdn.net/sulia1234567890/article/details/121016922
今日推荐