当前位置: 首页 > article >正文

十大排序算法之线性时间非比较类排序

线性时间非比较类排序

线性时间的算法执行效率也较高,从时间占用上看,线性时间非比较类排序要优于非线性时间排序,但其空间复杂度较非线性时间排序要大一些。因为线性时间非比较类排序算法会额外申请一定的空间进行分配排序,这也是它的典型特点——以空间换时间。而且,线性时间非比较类排序对待排序元素的要求较为严格,如计数排序要求待排序序列的差值范围不能太大,桶排序要求元素的分布要尽量均匀等。

线性时间非比较类排序的优势和局限性

线性时间算法的高效性

线性时间比较类排序算法具有较高的执行效率。相对于非线性时间排序算法,线性时间算法在时间占用上更为优越。它们能够在O(n)的时间复杂度内完成排序,这意味着算法的执行时间随着待排序元素数量的增加而线性增长。这使得线性时间算法成为处理大规模数据集的理想选择,能够快速有效地完成排序任务。

以空间换时间的策略

线性时间比较类排序算法常常以空间换时间的策略来提升排序性能。这些算法会额外申请一定的空间来进行元素分配和排序操作。例如,计数排序和桶排序都需要额外的空间用于分配元素。虽然这增加了空间复杂度,但却能够在时间复杂度上获得显著的提升。以空间换时间的策略使得线性时间比较类排序算法在某些场景下非常适用。

限制和要求

线性时间比较类排序算法对待排序元素有一定的限制和要求。例如,计数排序要求待排序序列的差值范围不能太大,否则会导致额外的空间消耗。桶排序则要求元素的分布尽量均匀,以充分利用桶的优势。因此,在选择线性时间比较类排序算法时,需要根据待排序数据的特点来确定算法的适用性。

例子

在Java编程语言中,实现线性时间比较类排序算法可以帮助我们更直观地理解其工作原理和优势。以下以基数排序(Radix Sort)为例,这是一种线性时间复杂度的非比较类排序算法,适用于整数排序且对数值范围有一定要求。

public class RadixSort {
    // 基数排序函数
    public static void radixsort(int[] arr) {
        if (arr == null || arr.length == 0)
            return;

        int max = Arrays.stream(arr).max().getAsInt(); // 找出数组中的最大值

        for (int exp = 1; max / exp > 0; exp *= 10) { // 按照每一位进行计数排序
            countingSort(arr, exp);
        }
    }

    // 计数排序辅助函数
    private static void countingSort(int[] arr, int exp) {
        int n = arr.length;
        int[] output = new int[n]; // 输出数组
        int[] count = new int[10]; // 计数数组

        Arrays.fill(count, 0);

        // 计算每个位上出现的次数
        for (int i = 0; i < n; i++) {
            count[(arr[i] / exp) % 10]++;
        }

        // 将计数数组转换为前缀和,便于计算输出位置
        for (int i = 1; i < 10; i++) {
            count[i] += count[i - 1];
        }

        // 根据计数数组将元素放到正确的位置
        for (int i = n - 1; i >= 0; i--) {
            output[count[(arr[i] / exp) % 10] - 1] = arr[i];
            count[(arr[i] / exp) % 10]--;
        }

        // 将临时数组复制回原数组
        System.arraycopy(output, 0, arr, 0, n);
    }

    // 测试代码
    public static void main(String[] args) {
        int[] array = {170, 45, 75, 90, 802, 24, 2, 66};
        radixsort(array);
        System.out.println(Arrays.toString(array));
    }
}

上面这段Java代码展示了基数排序的基本实现,它首先找到数组中的最大值来确定需要处理的位数,然后逐个按照每一位进行计数排序。虽然基数排序的空间复杂度相对较高,但它能在线性时间内完成排序,尤其对于大规模整数排序场景具有很高的实用价值。

除了基数排序,还有一种线性时间非比较类排序算法——桶排序(Bucket Sort),它适用于待排序元素在一定范围内且分布均匀的情况。桶排序的思想是将数组中的元素分到有限数量的“桶”中,然后对每个桶分别进行排序,最后按顺序合并所有桶中的元素。

以下是Java实现桶排序的基本示例:

public class BucketSort {
    // 桶排序函数
    public static void bucketSort(int[] arr, int bucketSize) {
        if (arr == null || arr.length == 0)
            return;

        int minValue = Arrays.stream(arr).min().getAsInt();
        int maxValue = Arrays.stream(arr).max().getAsInt();

        // 创建桶
        List<List<Integer>> buckets = new ArrayList<>();
        for (int i = 0; i <= maxValue - minValue; i += bucketSize) {
            buckets.add(new ArrayList<>());
        }

        // 将元素分配到各个桶中
        for (int num : arr) {
            int index = (num - minValue) / bucketSize;
            buckets.get(index).add(num);
        }

        // 对每个桶进行排序,这里使用插入排序作为子排序算法
        for (List<Integer> bucket : buckets) {
            Collections.sort(bucket);
        }

        // 合并所有已排序的桶
        int index = 0;
        for (List<Integer> bucket : buckets) {
            for (Integer num : bucket) {
                arr[index++] = num;
            }
        }
    }

    // 测试代码
    public static void main(String[] args) {
        int[] array = {5, 3, 8, 1, 9, 6, 7, 2, 4};
        bucketSort(array, 3);
        System.out.println(Arrays.toString(array));
    }
}

在上面这段Java代码中,首先计算出数组的最小值和最大值以确定桶的数量,并初始化桶列表。接着遍历输入数组,根据元素值将其放入对应的桶中。之后对每个桶内部采用插入排序或其他适合的小规模排序算法进行排序。最后,按照桶的顺序合并所有已排序的桶,从而得到最终排序结果。
虽然桶排序在理想情况下可以达到线性时间复杂度,但其性能取决于输入数据的分布情况以及所选桶大小等因素。如果数据分布不均匀或者桶大小选择不当,可能会导致实际运行效率下降。

上面的两个例子会在后面的文章里详细讲解

总结

线性时间非比较类排序算法具有高效的执行效率和较低的时间复杂度,适用于处理大规模数据集的排序任务。它们以空间换时间的策略,在一定的限制和要求下,能够快速有效地完成排序操作。然而,需要根据待排序数据的特点来选择合适的算法,以充分发挥线性时间比较类排序算法的优势。


http://www.kler.cn/a/233665.html

相关文章:

  • 文心一言 VS 讯飞星火 VS chatgpt (388)-- 算法导论24.5 8题
  • 【网络面试篇】其他面试题——Cookie、Session、DNS、CDN、SSL/TLS、加密概念
  • ssd作为hdd缓存加速方案
  • Visa指令简介
  • 软件设计开发规程,制度(word原件)
  • pdf转excel;pdf中表格提取
  • 电商小程序05用户注册
  • 吉他学习:C大调第一把位音阶,四四拍曲目练习 小星星,练习的目的
  • Mac OS 取消隔离扩展属性
  • HCIA-HarmonyOS设备开发认证V2.0-3.2.轻量系统内核基础-时间管理
  • #vu3# element plus表格的序号字段
  • STM32CubeMX,定时器之定时功能,入门学习,如何设置prescaler,以及timer计算PWM输入捕获方法(重要)
  • C语言笔试题之求出二叉树的最大深度(递归解决)
  • 【MATLAB源码-第138期】基于matlab的D2D蜂窝通信仿真,对比启发式算法,最优化算法和随机算法的性能。
  • Centos7.9安装SQLserver2017数据库
  • 【Make编译控制 01】程序编译与执行
  • 备战蓝桥杯---动态规划(基础3)
  • 虚拟飞控计算机:飞行控制系统验证与优化的利器
  • 汇编语言程序设计(二)十六位汇编框架、子程序与堆栈
  • 小周带你读论文-2之“草履虫都能看懂的Transformer老活儿新整“Attention is all you need(4)
  • 2024年-视觉AI检测的面试题目总结
  • 如何实现视线(目光)的检测与实时跟踪
  • 《CSS 简易速速上手小册》第5章:CSS 动画与过渡(2024 最新版)
  • 【社交电商】带直播电商功能,可以DIY前端,可以H5和小程序一般商城常用功能齐全
  • C++Linux网络编程day02:select模型
  • 基于完全二叉树实现线段树-- [爆竹声中一岁除,线段树下苦踌躇]