八大排序算法总结

1 冒泡排序

1.1 原理

在这里插入图片描述

1.2 图解

在这里插入图片描述

1.3 代码

public static void bubbleSort(int[] nums) {
        int n = nums.length;
        // 有序区为i到n-1,每次把0到i之间的最值冒泡交换到有序区的前面,使得有序区扩大一位。
        for (int i = n-1; i >= 0; i--) {
            // 冒泡排序优化,如果在当前趟无序区的比较中,没有发生过交换,说明当前无序区已经有序,可以直接结束排序过程
            boolean swapFlag = false;
            for (int j = 0; j < i ; j++) {
                // 将最大值冒泡交换到有序区
                if (nums[j] > nums[j+1]) {
                    swapFlag = true;
                    int t = nums[j];
                    nums[j] = nums[j + 1];
                    nums[j + 1] = t;
                }
            }
            if (!swapFlag)
                break;
        }
    }

2 选择排序

2.1 原理

在这里插入图片描述

2.2 图解

在这里插入图片描述

2.3 代码

public static void selectSort(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            int min = Integer.MAX_VALUE;
            int minIdx = -1;
            // 遍历无序区,找到最小(优先级最低)的数,及其索引
            for (int j = i; j < nums.length; j++) {
                if (nums[j] < min) {
                    min = nums[j];
                    minIdx = j;
                }
            }
            // 将最小数与无序区的首位元素进行交换,从而扩大前面的无序区
            if (i != minIdx) {
                nums[minIdx] = nums[i];
                nums[i] = min;
            }
        }
    }

3 直接插入排序

3.1 原理

在这里插入图片描述

3.2 图解

在这里插入图片描述

3.3 代码

public static void insertSort(int[] nums) {
        int n = nums.length;
        for (int i = 1; i < n ; i++) {
            // 将j = i位置,即无序区的开始,囊括到有序区,并将该位置的数据,插入到合适的位置
            // 反向遍历有序区,如果当前元素不小于(优先级不低于)待插入数据),则将当前元素后移一位(当前位置不用管)
            // 找到了第一个优先级低于 待插入数据 的位置, 将待插入数据 放到这个位置的后面即可。
            int insertValue = nums[i];
            int insertIdx = 0;
            for (int j = i - 1; j >= 0 ; j--) {
                if (nums[j] >= insertValue) {
                    nums[j + 1] = nums[j];
                }
                else {
                    insertIdx = j + 1;
                    break;
                }
            }
            // 判断是否需要插入
            if (insertIdx != i)
                nums[insertIdx] = insertValue;
        }
    }

4 希尔排序 (缩小增量排序)

4.1 原理

在这里插入图片描述

4.1.1 交换法和移位法

在这里插入图片描述

4.2 图解

在这里插入图片描述

4.3 代码

4.3.1 交换法

// 希尔排序 交换法 (多次交换导致速度很慢)
    public static void shellSortSwap(int[] nums) {
        int n = nums.length;
        // 初始增量为长度的一半,每次将增量缩小一半
        int t = 0;
        int count = 0;
        for (int gap = n / 2; gap > 0 ; gap /= 2) {
            for (int i = gap; i < n  ; i++) {
                // 从每个分组的最后一个元素开始向前遍历,进行插入排序
                for (int j = i - gap; j >= 0 ; j -= gap) {
                    // 交换法
                    if (nums[j] >= nums[j + gap]) {
                        t = nums[j];
                        nums[j] = nums[j + gap];
                        nums[j + gap] = t;
                    }
                }
            }
            //System.out.println("希尔排序第"+(++count)+"轮后: " + Arrays.toString(nums));
        }
    }

4.3.2 移位法

// 希尔排序 移位法
    public static void shellSortShift(int[] nums) {
        int n = nums.length;
        // 初始增量为长度的一半,每次将增量缩小一半
        int t = 0;
        int count = 0;
        for (int gap = n / 2; gap > 0 ; gap /= 2) {
            for (int i = gap; i < n  ; i++) {
                // 从每个分组的最后一个元素开始向前遍历,进行插入排序
                int insertValue = nums[i];
                int insertIdx = i;
                for (int j = i - gap; j >= 0 ; j -= gap) {
                    // 移位法:类似插入排序中的处理
                    if (nums[j] >= insertValue) {
                        nums[j + gap] = nums[j];
                        insertIdx -= gap;
                    }
                    else break;
                }
                if (insertIdx != i)
                    nums[insertIdx] = insertValue;
            }
            //System.out.println("希尔排序第"+(++count)+"轮后: " + Arrays.toString(nums));
        }
    }

5 快速排序

5.1 原理

在这里插入图片描述

5.2 图解

在这里插入图片描述

5.3 代码

选择基准值的方式有多种:例如左边界、中间位置、右边界以及随机位置,这里采用左边界。

	public static void quickSort(int[] nums, int left, int right) {
        if (left < right) {
            // 治
            int pivot_loc = partition(nums, left, right);
            // 分
            quickSort(nums, left, pivot_loc - 1);
            quickSort(nums, pivot_loc + 1, right);
        }
    }

    public static int partition(int[] nums, int left, int right) {		
        /*   // 随机一个位置作为基准值(轴值)
    	int pivotLoc = left + new Random().nextInt(right - left + 1);
        int pivot = nums[pivotLoc];
        */
        int pivot = nums[left];  // 选择第一个元素作为轴值
        // 以最左边为轴值时,在交换值时应该先右后左
        while (left != right) {
            // 从右往左找到比轴值小的元素
            while (nums[right] >= pivot && left < right) {
                right--;
            }
            // 将这个元素的值赋到left位置
            if (left < right) {
                nums[left] = nums[right];
                left++;
            }
            // 从左往右找到比轴值大的元素
            while (nums[left] < pivot && left < right) {
               left++;
            }
            // 将这个元素的值赋到right位置
            if (left < right) {
                nums[right] = nums[left];
                right--;
            }
        }
        nums[left] = pivot;
        return left;
    }

6 归并排序

6.1 原理

在这里插入图片描述

6.2 图解

在这里插入图片描述

6.3 代码

少量空间换时间

public static void mergeSort(int[] nums) {
        int[] temp = new int[nums.length];
        divideAndConquer(nums, 0 , nums.length - 1, temp);
    }

    public static void divideAndConquer(int[] nums, int left, int right, int[] temp) {
        if (left < right) {
            int mid = left + (right - left) / 2;
            // System.out.printf("正在分解数组[%d-%d]为[%d-%d][%d-%d]\n", left, right, left, mid, mid + 1, right);
            //向左递归不断分解
            divideAndConquer(nums, left, mid, temp);
            //向右递归不断分解
            divideAndConquer(nums, mid + 1, right, temp);

            // 第一次到这里,数组应该被分为n份,每份长度为1,即l=r
            // System.out.printf("正在合并数组[%d-%d][%d-%d]为[%d-%d]\n", left, mid, mid + 1, right, left, right);
            merge(nums, left, mid, right, temp);
        }
    }

    public static void merge(int[] nums, int left, int mid, int right, int[] temp) {
        // 归并排序“治”的过程,实际上就是 合并两个有序序列的过程。
        int i = left;
        int j = mid + 1;
        int t = 0;

        while (i <= mid && j <= right) {
            if (nums[i] <= nums[j]) {
                temp[t] = nums[i];
                t++;
                i++;
            }
            else {
                temp[t] = nums[j];
                t++;
                j++;
            }
        }
        while (i <= mid) {
            temp[t] = nums[i];
            i++;
            t++;
        }
        while (j <= right) {
            temp[t] = nums[j];
            j++;
            t++;
        }
        // 将temp数组的当此有效元素 拷贝到原数组nums中
        // 注意这里temp数组和nums数组映射关系不同
        // temp  0 -> right - left - 1    nums  left -> right
        t = 0;
        int tempLeft = left;
        while (tempLeft <= right) {
            nums[tempLeft] = temp[t];
            t++;
            tempLeft++;
        }

    }

7 基数排序

7.1 原理

在这里插入图片描述

7.2 图解

在这里插入图片描述

7.3 代码

public static void radixSort(int[] nums) {
        int n = nums.length;
        // 创建10个桶,第i个桶存储当前位为i的数据,为了防止溢出,每个桶的大小应该和原数组大小相同
        int[][] buckets = new int[10][n];
        // 记录每轮排序后,每个桶实际放了多少个数据
        int[] bucketsTop = new int[10];

        // 排序前,先遍历依次:1、找到数组最小值; 2、找到数组最大绝对值,计算最大位数
        int min = 0;
        for (int num : nums) {
            min = Math.min(num, min);
        }
        // 如果min < 0,说明存在负数,将所有数据加上 -min 使得全部为正数
        int max = 0;
        if (min < 0) {
            for (int i = 0; i < nums.length; i++) {
                nums[i] += -min;
                max = Math.max(max, nums[i]);
            }
        }
        // 计算最大位长度
        int maxBitLength = (max + "").length();
        for (int b = 0; b <= maxBitLength; b++) {
            // 遍历原数组,按照当前位每个数据的位值,将其放到对应的桶
            int bit = (int) Math.pow(10, b);
            for (int i = 0; i < n; i++) {
                int digit = (nums[i] / bit) % 10;
                buckets[digit][bucketsTop[digit]] = nums[i];
                bucketsTop[digit]++;
            }
            // 遍历所有桶,将桶中数据按顺序放回原数组
            int index = 0;
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < bucketsTop[i]; j++) {
                    nums[index] = buckets[i][j];
                    index++;
                }
                // 每一轮处理后,应该把桶的数据下标置0
                bucketsTop[i] = 0;
            }
        }
        // 如果min < 0,说明存在负数,恢复排序好的数据,所有数据加上 min
        if (min < 0) {
            for (int i = 0; i < nums.length; i++) {
                nums[i] += min;
            }
        }
    }

8 堆排序

9 比较

9.1 时空复杂度

搬一张尚硅谷的图
在这里插入图片描述

9.2 实际测试

9.2.1 代码

public class SortTest {
    public static void main(String[] args) {
        int length = 100000000;
        int[] nums = new int[length];
        for (int i = 0; i < length; i++) {
            nums[i] = (int) (Math.random() * length);
        }
        System.out.printf("测试 %d 长度随机数组进行升序排序的耗时:\n", length);
        System.out.println("冒泡排序: " + bubbleSortTest(nums) + "ms");
        System.out.println("选择排序: " + selectSortTest(nums) + "ms");
        System.out.println("插入排序: " + insertSortTest(nums) + "ms");
        System.out.println("希尔排序交换法: " + shellSortSwap(nums) + "ms");
        System.out.println("希尔排序移位法: " + shellSortShift(nums) + "ms");
        System.out.println("快速排序左边基准值法: " + quickSortLeftPivotTest(nums) + "ms");
        System.out.println("快速排序随机基准值法: " + quickSortRandomPivotTest(nums) + "ms");
        System.out.println("归并排序:" + mergeSortTest(nums) + "ms");
        System.out.println("基数排序:" + radixSortTest(nums) + "ms");
    }

    public static long bubbleSortTest(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        BubbleSort.bubbleSort(arr);
        return System.currentTimeMillis() - start;
    }

    public static long selectSortTest(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        SelectSort.selectSort(arr);
        return System.currentTimeMillis() - start;
    }

    public static long insertSortTest(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        InsertSort.insertSort(arr);
        return System.currentTimeMillis() - start;
    }

    public static long shellSortSwap(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        ShellSort.shellSortSwap(arr);
        return System.currentTimeMillis() - start;
    }

    public static long shellSortShift(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        ShellSort.shellSortShift(arr);
        return System.currentTimeMillis() - start;
    }

    public static long quickSortLeftPivotTest(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        QuickSort.quickSort(arr, 0, arr.length - 1);
        return System.currentTimeMillis() - start;
    }

    public static long quickSortRandomPivotTest(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        QuickSort.randomQuickSort(arr, 0, arr.length - 1);
        return System.currentTimeMillis() - start;
    }

    public static long mergeSortTest(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        MergeSort.mergeSort(arr);
        return System.currentTimeMillis() - start;
    }

    public static long radixSortTest(int[] nums) {
        int[] arr = Arrays.copyOf(nums, nums.length);
        long start = System.currentTimeMillis();
        RadixSort.radixSort(arr);
        return System.currentTimeMillis() - start;
    }
}

9.2.2 结果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值