排序算法代码整理 —— 冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、基数排序

冒泡排序

  • 冒泡排序是把大的数往后冒
  • 思路:通过对待排序序列,从前往后(从下标值小的往下标值大的),依次比较相邻元素的值,若发现逆序(下标小的数据值 > 小标大的数据的值)则交换,使较大的值从前往后移。
  • 优化的部分:如果一趟排序下来,发现没有数据进行交换,即序列的排列已经按序。可以在排序前设置一个标识位,用来描述这趟排序有没有交换数据,若没有交换数据,则直接退出循环。
  • 有序序列的下标为 [n - 2,n);无序序列的下标为[0,n - 2)
  • 代码:
import java.util.Arrays;

public class BubbleSort {
    public static void main(String[] args) {
        int[] arr = {-1, 0, 10, 9, 20};
        int tmp = 0;
        for(int i = 0; i < arr.length - 1; i++){  // 外层循环是用来表示需要进行多少趟排序
            boolean flag = false;  // 标识位,用来判断是否有进行过数据交换
            for(int j = 0; j < arr.length - 1 - i; j++){  // 内层的 for 循环,用来遍历数组,交换数据
                if(arr[j] > arr[j + 1]){ // 这里发现逆序(即下标小的 > 小标大的,需要将下标大的往后移动,而发生交换的代码)
                    flag = true;  // 若有发生交换,则将标识位置为 正
                    tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
            System.out.println("第" + (i + 1) + "趟排序后的数组");
            System.out.println(Arrays.toString(arr));
            if(!flag){
                break;
            }
        }
    }
}

冒泡排序一定要看这个,我老是忘记,这个视频真的可有意思了

选择排序

  • 在选择的过程中不发生交换数据,只有在遍历一遍确定最小值后才发生交换
  • 思路:第一次从 arr[0] ~ arr[n - 1] 中挑选最小的数据,然后与 arr[0] 发生交换,,第二次从 arr[1] ~ arr[n - 1] 中挑选最小的数据,然后与 arr[1] 发生交换…… 第 n - 1 次从 arr[n - 2] ~ arr[n - 1] 中挑选最小的值与 arr[n - 2] 交换。
  • 优化的部分:若 最小值的下标在这趟比较中,没有变化,那么就不用发生交换了
  • 有序序列的下标[0,i);无序序列的下标[i,n) i 下标是从 0 开始的
  • 代码:
import java.util.Arrays;

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

        System.out.println("排序前~");
        System.out.println(Arrays.toString(arr));

        select(arr);
    }

    public static void select(int[] arr){
        for(int i = 0; i < arr.length - 1; i++){
            int min = arr[i];
            int minIndex = i;
            for(int j = i + 1; j < arr.length; j++){
                if(min > arr[j]){
                    minIndex = j;
                    min = arr[j];
                }
            }
            if(minIndex != i){
                arr[minIndex] = arr[i];
                arr[i] = min;
            }
            System.out.println("第 " + (i + 1) +" 次排序后~");
            System.out.println(Arrays.toString(arr));
        }
    }
}

插入排序

  • 插入排序是前 i 个数是有序的,将第 i + 1 个数据放到有序队列中
  • 思路:把 n 个待排序的元素看成为一个有序表和一个无序表,开始时有序表中包含一个元素,无序表中包含 n - 1 个元素,排序过程中每次从无序表中取出第一个元素,把他的排序码依次与有序表中的元素进行比较,将它插入到有序表中的合适位置,使之成为新的有序表
  • 有序序列的下标[0,i);无序序列的下标[i,n) i 是从下标 1 开始的
  • 代码:
import java.util.Arrays;

public class InsertSort {
    public static void main(String[] args) {
        int[] arr = {17,3,25,14,20,9};
        insert(arr);
    }

    public static void insert(int[] arr){
        // 原始数组为 [17,3,25,14,20,9]
        for(int i = 1; i < arr.length; i++){
            int insertVal = arr[i];  // 用来表示准备插入的值
            int insertIndex = i - 1;  // 用来表示开始比较的下标值
            // insertIndex >= 0 保证了下标的合法性
            // insertVal < arr[insertIndex] 我们要按照从小到大的顺序排列,
            // 所以必须当要插入的值 > 当前数组的值时,才算找到合适的位置;当 要插入的值 < 当前数组的值时,必须接着往前比较,直到找到有序序列的开头或者合适的位置
            while(insertIndex >= 0 && insertVal < arr[insertIndex]){
                arr[insertIndex + 1] = arr[insertIndex]; // 把当前下标的值往后移动
                insertIndex--; // 下标值往前移动
                // 示例:第一趟的[17,3,25,14,20,9]经过第一次while循环判断后
                // [17,17,25,14,20,9] 原本的 1 下标的值,用 insertVal 保存了,所以不用担心
            }
            // 退出 while 循环则说明已经找到合适的下标,或者到了有序序列的开始位置
            // if 判断是优化代码
            // 就是有的数据可能在此次插入中,比有序序列的数据都大,insertIndex 的值没有变化 —— 没有进入 while 循环
            // 但是经过 80000 个数据的测试,其实优化后和优化前的时间所差不大
            if(insertIndex != i){
                // 一定要注意:这里赋值的时候下标值一定 得是 insertIndex + 1!!!!!!!!
                // 1)当数据遍历完毕后,要往有序序列第一个添加时,此时insertIndex = -1
                // 2) 当while循环是遇到合适的值退出时,当前 insertVal > arr[insertIndex],就算插入也要插入到它的下个位置
                arr[insertIndex + 1] = insertVal;
            }
            System.out.println("第 " + i +" 趟插入排序后");
            System.out.println(Arrays.toString(arr));
        }
    }
}

插入排序的代码要注意的小细节特别多,在写的时候一定要小心,多多注意当前下标在哪,要往哪插

希尔排序

  • 希尔排序是插入排序进行改进后的一个更高效的版本,也称为缩小增量排序
  • 思路:把记录按下标的一定增量分组,对每组使用直接插入算法排序;随着增量逐渐减小,每组包含的关键字越来越多,当增量减至 1 时,整个文件被分为一组排序后,算法便终止
  • 代码:
import java.util.Arrays;

public class ShellSort {
    public static void main(String[] args) {
        int[] arr = {8,9,1,7,2,3,5,4,6,0};
        shell(arr);
    }

    public static void shell(int[] arr){
        int count = 0;
        for(int gap = arr.length / 2; gap > 0; gap /= 2){
            for(int i = gap; i < arr.length; i++){
                int j = i - gap; // 插入排序中的第一个待比较的下标
                int tmp = arr[i]; // 准备插入的数
                while(j >= 0 && tmp < arr[j]){
                    arr[j + gap] = arr[j];
                    j -= gap;
                }
                arr[j + gap] = tmp;
            }
            System.out.println("第 "+(++count)+ " 次排序后的数组为 = " + Arrays.toString(arr));
        }
    }
}

快速排序

  • 在无序序列选择一个基准值,从数组两端开始遍历数组,将比 基准值 小的数放到基准值的左边,比基准值大的数放到基准值的右边
  • 步骤:
    • 在待排序空间选择一个基准值
    • 做 partition 使得小的数在基准值左,大的数在基准值右
    • 分治处理左右两个小区间。
package sort;

import java.util.Arrays;

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {-9, 78, 0, 23, -567, 70};
        quick(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }

    public static void quick(int[] arr, int left, int right){  // 用的是 几数取中法 + hoare
        if(left == right){
            return;
        }
        if(left > right){
            return;
        }

        int pivotIndex = partition(arr, left, right);
        quick(arr, left, pivotIndex - 1);
        quick(arr, pivotIndex + 1, right);
    }

    private static int partition(int[] arr, int left, int right) {
        int i = left;
        int j = right;
        int pivot = arr[left];

        while(i < j){
            while(i < j && arr[j] >= pivot){
                j--;
            }
            while(i < j && arr[i] <= pivot){
                i++;
            }
            swap(arr, i, j);
        }
        // 一旦退出while循环,则说明 i 和 j 相遇了
        // 交换 i 或者 j 和 left 为下标的值,因为 基准值 pivot 在left 的位置,需要把基准值换到数组中间去
        swap(arr, i, left);
        return i;
    }

    private static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

归并排序

  • 先把数组分开,然后合并。我自己理解的时候,在数组用递归分解的时候,感觉有点点难理解
  • 思路:利用对并的思想实现的排序方法,该算法采用经典的分治策略,将问题分成一些小的问题然后递归求解,而治的阶段,则将分的阶段得到的各答案“ 修补 ”在一起。
  • 代码:
import java.util.Arrays;

public class MergeSort {
    public static void main(String[] args) {
        int[] arr = {8,4,5,7,1,3,6,2};
        int[] tmp = new int[arr.length];
        mergeSort(arr, 0, arr.length - 1, tmp);
        System.out.println(Arrays.toString(arr));
    }

    public static void mergeSort(int[] arr, int left, int right, int[] tmp){
        if(left < right){
            int mid = (left + right) / 2;
            // 开始分解数组
            // 先分解 mid 的左边
            mergeSort(arr, left, mid, tmp);
            // 再分解有右边的
            mergeSort(arr, mid + 1, right,tmp);
            // 分解好之后再合并
            merge(arr, left, mid, right, tmp);
        }
    }

    private static void merge(int[] arr, int left, int mid, int right, int[] tmp) {
        int i = left;  // 遍历左边部分时的下标
        int j = mid + 1; // 遍历右边部分时的下标
        int index = 0; // 临时数组的下标变化
        while(i <= mid && j <= right){  // 当左边的下标和右边的下边都符合条件时,才可能进行下边的
            if(arr[i] <= arr[j]){
                tmp[index++] = arr[i++];
            }else{
                tmp[index++] = arr[j++];
            }
        }
        // 左半边没有剩余的数
        while(i <= mid){
            tmp[index++] = arr[i++];
        }
        // 右边还有剩余的数
        while(j <= right){
            tmp[index++] = arr[j++];
        }
        // 将 tmp 数组的值拷贝到 arr 中
        index = 0;
        int tmpIndex = left;
        while(tmpIndex <= right){
            arr[tmpIndex++] = tmp[index++];
        }
    }
}

基数排序

  • 这是一个典型的用空间换时间的排序
  • 思路:将所有待比较数值同一为同样的数位长度,数位较短的数前面补 0,然后从最低位开始,以此进行以此排序。这样从最低为排序一直到最高位排序完成后,数列就变成一个有序序列
  • 演示:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 代码:
import java.util.Arrays;

public class RadixSort {
    public static void main(String[] args) {
        int[] arr = {53,3,542,748,14,214};
        radixSort(arr);
        System.out.println("排序后的数组 arr=" + Arrays.toString(arr));
    }

    private static void radixSort(int[] arr){
        // 定义一个二维数组,表示 10 个桶(0 ~9)
        // 1. 二维数组包含 10 个一维数组
        // 2. 每个一维数组用来保存该位数下 值 为 一维数组下标的数
        // 3. 因为不了解每个一维数组中应该放入多少个数,为了防止溢出,所以每个一维数组的长度设为 arr.length
        int[][] bucket = new int[10][arr.length];

        // 在从桶中取数据的时候不知道要从该桶中取出多少个数据,即不知道每个桶中有多少个数据
        // 所以,设置 bucketElementCounts 这个数组用来记录 每个桶中有多少个数据
        int[] bucketElementCounts = new int[10];

        // 想知道应该遍历多少次,就应该先知道原始数组中最大数据的位数是多少
        int max = arr[0];
        for(int i = 1; i < arr.length; i++){ // 1)找出原始数组中的最大值
            if(max < arr[i]){
                max = arr[i];
            }
        }
        // 2)确定最大数据的位数
        int maxLength = (max + "").length();

        // 循环开始
        for(int i = 0, n = 1; i < maxLength; i++, n *= 10){ // 确定循环次数
            for(int j = 0; j < arr.length; j++){ // 给原始数组中的每一个值找到合适的桶
                int digitOfElement = arr[j] / n % 10;
                bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
                bucketElementCounts[digitOfElement]++;
            }
            // for 循环结束,意味着把数据给桶中放的过程完成
            // 从桶中取数据
            int index = 0; // 用来定位数组的下标
            for(int k = 0; k < bucketElementCounts.length; k++){
                if(bucketElementCounts[k] != 0) {
                    for (int l = 0; l < bucketElementCounts[k]; l++) {
                        arr[index++] = bucket[k][l];
                    }
                }
                // 假设:这一轮在比较 个位数的值,则下一轮开始十位数的值的比较
                // 在开始之前,必须将桶中数据的个数置为 0,即将 bucketElementCounts 数组的每一个置为 0
                bucketElementCounts[k] = 0;
            }
            // for 循环结束,意味着将桶中的数据也全部取出了,则开始下一轮
        }
    }
}

排序算法的总结

在这里插入图片描述
还有基数排序,它的时间复杂度是O(n * k),其中 n 为数据的大小,k 为桶的个数,基数排序算法也是稳定

发布了98 篇原创文章 · 获赞 5 · 访问量 6450

猜你喜欢

转载自blog.csdn.net/weixin_43580746/article/details/105366785