数据结构与算法 排序算法(二) 附有详细动画流程分析图!!

本文详细介绍了四种排序算法:希尔排序的交换法和位移法,归并排序的分治思想,快速排序的递归策略,以及一种自定义排序规则。每种算法都提供了Java实现,并分析了它们的性能和优缺点。希尔排序位移法在10万次数据排序中耗时约19毫秒,归并排序约20毫秒,快速排序约17毫秒,而自定义排序耗时约7秒。这些排序算法在效率和代码复杂性上各有特点,适合不同的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据结构与算法 排序算法(二)

Tips: 采用java语言, 关注博主,底部附有完整代码

上一篇

本篇介绍排序算法:

  • 希尔排序(选择法/交换法)
  • 希尔排序(位移法)
  • 归并排序(分治排序)
  • 快速排序
  • 自定义排序
算法名称10万次数据耗时效果图
希尔排序选择法 10万次耗时约 20秒
位移法10万次耗时约 19毫秒
希尔排序gif
归并排序10万次耗时约 20毫秒归并排序(分治排序)gif
快速排序10万次耗时约17毫秒快速排序gif
自定义排序10万次耗时约7秒左右

希尔排序(选择法/交换法)

重点:

  • 倒叙输出结果 (根据步长来倒叙)

  • 交换

  • 默认之前的数 始终是有序的

  • 始终和前面的数相比较

原理:

假设现在有10个数据,依次隔 length / 2次来排序

  • 第一次排序 隔5个元素来排序 (10 / 2 = 5)

  • 第二次排序 隔2个元素来排序(5 / 2 = 2)

  • 第三次排序 隔1个元素来排序(2 / 2 = 1)

这样一来,就能将相邻的数字替换到大致位置, 减少了交换的次数

效果图:

希尔排序gif

倒叙输出结果 (根据步长来倒叙)

image-20220531155557614

交换

交换和上一篇中的冒泡排序交换一样

根据步长倒叙排序

image-20220531162924620

这段代码稍微有点复杂,多看几次就懂了!!

这段代码懂了,在看希尔排序代码就十分清晰了!

完整代码

# 希尔排序(选择法/交换法)

// ints数据: 12, 4, 16, 1, 20, 21, 5, 8, 10, 3
public static void method2(int[] ints) {
        int count = 0;

        // step = 步长
        for (int step = ints.length / 2; step > 0; step /= 2) {
          // 排序次数
            for (int i = step; i < ints.length; i++) {
                // 交换
                for (int j = i - step; j >= 0; j -= step) {

                    // 当前元素 > 前一个元素 就交换位置
                    if (ints[j] > ints[j + step]) {
                        int temp = ints[j + step];
                        ints[j + step] = ints[j];
                        ints[j] = temp;
                    }
                }
            }
          
            count++;
            System.out.println("第" + (count) + "次排序,步长为" + step + " 结果为:" + Arrays.toString(ints));
        }
        System.out.println("希尔排序选择法(交换法)最终结果为:" + Arrays.toString(ints));
    }

最终结果为:

第1次排序,步长为5 结果为:[12, 4, 8, 1, 3, 21, 5, 16, 10, 20]
第2次排序,步长为2 结果为:[3, 1, 5, 4, 8, 16, 10, 20, 12, 21]
第3次排序,步长为1 结果为:[1, 3, 4, 5, 8, 10, 12, 16, 20, 21]
希尔排序选择法(交换法)最终结果为:[1, 3, 4, 5, 8, 10, 12, 16, 20, 21]

优缺点分析

优点:

  • 代码变复杂了,看着像是一位高手

缺点:

  • 10个数据排序时间和 冒泡排序 接近…
  • 存在大量毫无意义的交换
  • 代码变复杂,时间还是很久, 没卵用.

希尔排序(位移法)

位移法和交换法逻辑几乎相同只是:

  • 位移法 类似于插入排序,没有大量的交换 节省时间
  • 交换法 采用大量的交换 浪费时间

完整代码

# 希尔排序(位移法)
  
public static void method3(int[] ints) {
        System.out.println("希尔排序 - 位移法");
        System.out.println("原始数据" + Arrays.toString(ints));

        long oldTime = System.currentTimeMillis();
        // step = 步长
        for (int step = ints.length / 2; step > 0; step /= 2) {
            for (int i = step; i < ints.length; i++) {

                // 当前数据
                int currentValue = ints[i];
                int j;

                // 倒叙 每一个元素
                // ints[j] 倒叙后的每一个元素
                for (j = i - 1; j >= 0 && currentValue < ints[j]; j--) {
                    // 元素后移 给需要插入的元素腾位置
                    ints[j + 1] = ints[j];
                }
                // 需要插入的元素 = currentValue
                ints[j + 1] = currentValue;
              
            }
            System.out.printf("步长为%d,当前数据 =%s\n", step, Arrays.toString(ints));
        }

        System.out.println("希尔排序-位移法:" + Arrays.toString(ints));
    }

结果:

希尔排序 - 位移法
原始数据[12, 4, 16, 1, 20, 21, 5, 8, 10, 3]
步长为5,当前数据 =[12, 4, 16, 1, 3, 5, 8, 10, 20, 21]
步长为2,当前数据 =[1, 3, 12, 4, 5, 8, 10, 16, 20, 21]
步长为1,当前数据 =[1, 3, 4, 5, 8, 10, 12, 16, 20, 21]
希尔排序-位移法:[1, 3, 4, 5, 8, 10, 12, 16, 20, 21]

优缺点分析

优点:

  • 省时 10万次数据约19毫秒

归并排序(分治排序)

重点:

  • 合并两个有序数组
  • 递归

原理:

归并排序一共有2步

  • 归(分)
  • 并(治)

归(分): 将数组无限拆分,直到数组中有1个值,或者2个值,然后依次排序判断

**并(治)😗*是将一个数组分一分为二为2个数组

  • 数组1
  • 数组2

数组1和数组2都是有序的,然后根据两个指针依次合并到一个数组中,最终就完成了排序

来看看动画图:

归并排序(分治排序)gif

合并两个数组

image-20220601155651714

这段代码还是很简单的

那如果要是先插入一个ints1,在插入一个ints2,这样依次插入该怎么操作呢?

image-20220601160409249

可以看出,根据自己写的数据,就可以实现先插入一个ints1 , 在插入一个 ints2

看懂这段代码,在来看归并排序就简单了!

递归


// 测试递归
testRecursion(100, 0);

private static void testRecursion(int num, int position) {
    if (num >= 1) {
        testRecursion(num / 2, ++position);
    }
    System.out.println("num:" + num + "\tposition = " + position);
}

结果:

num:0 position = 7
num:1 position = 7
num:3 position = 6
num:6 position = 5
num:12 position = 4
num:25 position = 3
num:50 position = 2
num:100 position = 1

完整代码

# 归并排序()的代码
  
private static void mergeSort(int[] ints, int low, int middle, int high) {
        // 并(合并操作) 操作

        int firstStart = low;
        int firstEnd = middle;

        int secondStart = middle + 1;
        int secondEnd = high;

        // 临时位置
        int[] temp = new int[high - low + 1];
        int index = 0;

        // 第一个指针是否到最后 && 第二个指针是否到最后
        while (firstStart <= firstEnd && secondStart <= secondEnd) {
            // 左侧的元素 比右侧的小
            if (ints[firstStart] <= ints[secondStart]) {
                // 添加左侧的元素到集合中
                temp[index] = ints[firstStart];
                firstStart++;
            } else {
                // 添加右侧的元素到集合中
                temp[index] = ints[secondStart];
                secondStart++;
            }
            index++;
        }

        // first数组 有剩余
        while (firstStart <= firstEnd) {
            temp[index] = ints[firstStart];
            firstStart++;
            index++;
        }

        // second数组 有剩余
        while (secondStart <= secondEnd) {
            temp[index] = ints[secondStart];
            secondStart++;
            index++;
        }

        // 重新给 ints 赋值
        for (int i = 0; i < temp.length; i++) {
            ints[i + low] = temp[i];
        }

//        System.out.println("现在元素:" + Arrays.toString(ints));

    }
# 归并排序()的代码
private static void sort(int[] ints, int low, int high) {
        // 归(分) 操作
        // 中间位置
        int middle = (high + low) / 2;

        if (low < high) {
            // first 排序
            sort(ints, low, middle);

            // second 排序
            sort(ints, middle + 1, high);

            // 开始排序
            mergeSort(ints, low, middle, high);
        }
    }
// 调用归并排序
  sort(ints, 0, ints.length - 1);

优缺点分析:

优点:

  • 节省时间 10万次数据平均耗时20毫秒

缺点:

  • 需要开启新的空间
  • 需要用到递归,增加了代码难度
  • 需要写2个方法
    • 归(递归)

快速排序

重点:

  • 递归
  • 开始位置,结束位置,临时位置

原理:

在数组中找3个位置

  • 开始位置
  • 结束位置
  • 临时位置

假设目前是从小到大排序,那么

  • 开始位置就是最小的值
  • 结束位置就是最大的值

如果开始位置 > 临时位置 那么 结束位置 = 开始位置

如果结束位置 < 临时位置 那么 开始位置 = 结束位置

如果 开始位置 = 结束位置 那么 临时位置 = (开始位置/结束位置)

看一眼流程图:

快速排序gif

开始位置,结束位置,临时位置

第一次排序时

  • 开始位置 = 0
  • 结束位置 = ints.length -1
  • 临时位置 = 0 (默认临时位置就是开始位置)

完整代码

 private static void sort(int[] ints, int left, int right) {
        // 如果 left >= right 则不排序
        if (left >= right) {
            return;
        }
        // 最左侧下标
        int start = left;

        // 最右侧下标
        int end = right;

        // 临时比较的位置
        int tempValue = ints[start];

        // 左侧索引 < 右侧索引
        // 比 middleValue 大的 放到右侧
        // 比 middleValue 小的 放到左侧
        while (start < end) {

            // 从右侧开始找比 tempValue 小的数
            while (start < end && ints[end] >= tempValue) {
                end--;
            }
            // 开始位置 = 结束位置
            ints[start] = ints[end];

            // 从左侧开始找比 tempValue 大的数
            while (start < end && ints[start] <= tempValue) {
                start++;
            }

            // 结束位置 = 开始位置
            ints[end] = ints[start];
        }

        // 此时 start = end
        ints[start] = tempValue;
        // 递归 开始排序左侧
        sort(ints, left, start);

        // 递归 开始排序右侧
        sort(ints, start + 1, right);
    }

优缺点分析

优点:

  • 耗时短 10万次数据耗时17秒左右

缺点:

  • 代码稍微复杂一点
  • 使用到了递归

自定义排序

这是我自己想到的一个排序规则,不算算法,但是比冒泡快

完整代码

 for (int i = 0; i < ints.length - 1; i++) {

   if (ints[i] > ints[i + 1]) {


     // 交换 。。
     int temp = ints[i];
     ints[i] = ints[i + 1];
     ints[i + 1] = temp;

     // 如果交换了 就退2个
     if (i >= 2) {
       i -= 2;
     } else {
       // 防止前两个数据下标越界
       i = -1;
     }
   }
 }

思路很简单, 就是如果交换了的话,让重新for,直到最后循环完毕

10万个数据耗时 7秒左右

上一篇:数据结构与算法 排序算法(一) 附有详细动画流程分析图

完整代码

原创不易,您的点赞就是对我最大的支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s10g

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值