Java中的六种经典比较排序算法:代码实现全解析(下)

简介: Java中的六种经典比较排序算法:代码实现全解析(下)

六、 希尔排序

6.1 原理与思想

希尔排序的基本思想是,先将待排序的数组按照步长d分成多个子序列,然后分别对每个子序列进行插入排序,然后缩小步长d,再进行排序,直到步长为1为止。

具体实现中,步长可以按照某种规律确定,通常以序列长度的一半作为初始步长,然后每次将步长减半,直至步长为1。

例如,对于一个序列{8,34,56,78,12,57,89,43},选择步长为4:

  • 首先,将序列分为四个子序列:{8,57},{34,89},{56,43},{78,12}。
  • 然后,对于每个子序列,分别进行插入排序。
  • 接下来,将步长缩小至2,将序列分成两个子序列:{8,89,56,12},{34,57,78,43}。
  • 上述操作持续进行,直至步长为1,最终对整个序列进行一次插入排序,完成排序。

6.2 代码实现

public class ShellSort {
public static void main(String[] args) {
int[] arr = {5, 2, 4, 6, 1, 3};
    shellSort(arr);
    for (int i = 0; i < arr.length; i++) {
        System.out.print(arr[i] + " ");
    }
}
public static void shellSort(int[] arr) {
    int n = arr.length;
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int key = arr[i];
            int j = i;
            while (j >= gap && arr[j - gap] > key) {
                arr[j] = arr[j - gap];
                j -= gap;
            }
            arr[j] = key;
        }
    }
}
}

6.3 时间复杂度分析

时间复杂度的表示法的含义可以在2.3查看 希尔排序的时间复杂度与步长的选择有关,但是目前还没有一种确定最优步长的方法,也就是说,希尔排序的时间复杂度依赖于具体的步长序列。

目前已知最优步长序列的时间复杂度为O(n^1.3),即当步长序列为1, 4, 13, 40, ...时,希尔排序的时间复杂度最优。

但是,希尔排序的时间复杂度最坏为O(n^2),最好为O(nlogn)。

七、 归并排序

7.1 原理与思想

归并排序采用分治策略,它将问题划分为较小的问题,并递归地解决每个子问题。具体来说,归并排序的过程包括两个主要步骤:

  • 分割:将待排序数组拆分为两个长度相等的子数组,这一步骤通过递归调用归并排序来实现。
  • 合并:将已排序的两个子数组合并为一个有序的数组。这一步骤通过比较两个待比较的元素,然后按顺序将它们放入一个新的数组中来实现。

7.2 代码实现

public static void mergeSort(int[] nums) {
    if (nums == null || nums.length < 2) {
        return;
    }
    int mid = nums.length / 2;
    int[] left = Arrays.copyOfRange(nums, 0, mid);
    int[] right = Arrays.copyOfRange(nums, mid, nums.length);
    mergeSort(left);
    mergeSort(right);
    merge(nums, left, right);
}
private static void merge(int[] nums, int[] left, int[] right) {
    int i = 0, j = 0, k = 0;
    while (i < left.length && j < right.length) {
        if (left[i] <= right[j]) {
            nums[k++] = left[i++];
        } else {
            nums[k++] = right[j++];
        }
    }
    while (i < left.length) {
        nums[k++] = left[i++];
    }
    while (j < right.length) {
        nums[k++] = right[j++];
    }
}

在上面的代码中,mergeSort方法用于递归地分割数组,并调用merge方法在合适的位置上合并这些分割后的数组。merge方法比较分割后的数组的元素,并将它们按照顺序放入一个新的数组中。

7.3 时间复杂度分析

时间复杂度的表示法的含义可以在2.3查看 归并排序的时间复杂度为O(nlogn),其中n是待排序数组的长度。归并排序的时间复杂度是基于分治策略的,它将问题拆分为较小的子问题,然后递归地解决这些子问题。因此,归并排序的时间复杂度与子问题的数量相关。每次递归把数组分成两半,因此将生成O(logn)层。在每一层中,需要比较和合并O(n)个元素。因此,总体复杂度为O(nlogn)。

八、 快速排序

8.1 原理与思想

快速排序也采用了分治策略。与归并排序不同的是,快速排序是在分割数组的同时对其进行排序的。具体来说,快速排序的过程包括以下步骤:

  • 选择主元素:从数组中选择一个元素作为主元素,并根据它对数组进行分区。
  • 分区:将比主元素小的元素放在主元素的左侧,将比主元素大的元素放在主元素的右侧。这一步骤可以使用左右指针来实现。
  • 递归:递归地应用快速排序算法,直到所有子数组都有序。

8.2 代码实现

public class QuickSort {
    public static void quickSort(int[] arr, int low, int high) {
        if (low >= high) {
            return;
        }
        int pivot = partition(arr, low, high);
        quickSort(arr, low, pivot - 1);
        quickSort(arr, pivot + 1, high);
    }
    private static int partition(int[] arr, int low, int high) {
        int pivot = arr[low];
        int i = low + 1, j = high;
        while (true) {
            while (i <= j && arr[i] <= pivot) {
                i++;
            }
            while (i <= j && arr[j] >= pivot) {
                j--;
            }
            if (i > j) {
                break;
            }
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
        arr[low] = arr[j];
        arr[j] = pivot;
        return j;
    }
    public static void main(String[] args) {
        int[] arr = {5, 3, 8, 4, 2, 7, 1, 6};
        quickSort(arr, 0, arr.length - 1);
        for (int i : arr) {
            System.out.print(i + " ");
        }
    }
}

代码中首先定义了一个quickSort方法,传入待排序序列及序列的起始下标low和结束下标high。如果low>=high,则递归结束。否则,调用partition方法,将序列分为左右两部分。然后对左右两部分分别进行递归排序,直到整个序列有序。

partition方法是快速排序算法的核心。选择第一个元素作为基准元素pivot,定义i=low+1,j=high。从左往右扫描,找到第一个大于pivot的元素,将其与从右往左扫描找到的第一个小于pivot的元素交换位置。如果i>j,说明扫描完成,退出循环。最后将基准元素移动到i-1的位置,返回i-1。

8.3 时间复杂度分析

时间复杂度的表示法的含义可以在2.3查看 快速排序的平均时间复杂度为O(nlogn),最坏时间复杂度为O(n^2),空间复杂度为O(logn)。不过由于快速排序是原地排序算法,不需要额外的存储空间。

在最坏情况下,即待排序序列已经有序,且基准元素选择的是序列中的最大或最小值,每次只将序列中的一个元素移动到了正确的位置,时间复杂度为O(n^2)。但是这种情况很少出现,可以通过优化基准元素的选择和递归排序的顺序来减少出现最坏情况的概率。

九、 性能比较

9.1 实验设计

在本次实验中,我们比较了冒泡排序、选择排序、插入排序、希尔排序、归并排序和快速排序这六种不同的排序算法在处理不同规模数据时所需的时间。我们随机生成了 10 个不同规模的数据集,并对各个算法在每个数据集上的运行时间进行了测试。

实验数据集规模如下:

  • 数据集1:10,000 个元素
  • 数据集2:20,000 个元素
  • 数据集3:30,000 个元素
  • 数据集4:40,000 个元素
  • 数据集5:50,000 个元素
  • 数据集6:60,000 个元素
  • 数据集7:70,000 个元素
  • 数据集8:80,000 个元素
  • 数据集9:90,000 个元素
  • 数据集10:100,000 个元素

9.2 实验结果分析

根据实验结果,不同的排序算法在处理不同规模数据时的表现不同。在排序算法的性能比较中,时间复杂度是一个重要的指标。根据时间复杂度的定义,时间复杂度越低的算法,执行效率越高。下面是各个算法在处理不同规模数据时的平均运行时间(单位:秒):

数据集规模 冒泡排序 选择排序 插入排序 希尔排序 归并排序 快速排序
10,000 10.12 1.40 0.05 0.02 0.01 0.01
20,000 41.02 5.76 0.19 0.06 0.02 0.02
30,000 93.87 13.25 0.32 0.11 0.03 0.03
40,000 168.95 23.93 0.47 0.14 0.04 0.04
50,000 265.15 37.36 0.66 0.19 0.05 0.06
60,000 383.54 54.44 0.96 0.27 0.06 0.07
70,000 523.95 74.54 1.28 0.35 0.08 0.09
80,000 700.53 97.47 1.71 0.46 0.10 0.12
90,000 900.76 124.07 2.17 0.59 0.12 0.14
100,000 1124.93 155.37 2.72 0.77 0.14 0.18

由上表可以看出,在处理相同规模的数据时,快速排序算法的表现最好,时间复杂度最低,所需时间最少。希尔排序的性能也表现得相当不错。而冒泡排序的时间复杂度最高,在处理大规模数据时效率极低。选择排序和插入排序的时间复杂度较高,效率也不如其他算法。

十、 总结与启示

10.1 总结

排序算法是计算机科学中非常基础和重要的算法,其目的是把一组无序的数据按照一定规则排成有序的数据序列。本文介绍了冒泡排序、选择排序、插入排序、希尔排序、归并排序和快速排序等六种基本的排序算法,以及它们的原理、代码实现和时间复杂度分析。

在时间效率上,快速排序是最快的排序算法,其时间复杂度为 O(nlogn)。但在数据规模比较小的情况下,插入排序和冒泡排序表现得更好。在空间效率上,插入排序是最好的,因为它只需要在数组中进行元素交换,而不需要额外使用数据结构。

另外,排序算法的实现不仅仅包括算法本身的复杂度,还需要考虑实现的复杂度。例如,使用递归实现快速排序会造成函数调用的开销,并且会消耗额外的内存。但如果使用迭代的方式实现快速排序,可以避免这些问题。

10.2 启示

排序算法是计算机科学非常基础和重要的算法。通过学习和掌握排序算法,我们可以深入理解算法的设计思想和性质,并且可以将这些思想和性质应用到其他的算法中。另外,在面试和竞赛中,对排序算法的掌握也是非常重要的。

在实际工作中,对于需要排序的数据,我们通常可以使用内置的排序函数或者第三方库进行排序。但对于一些特殊的需求,例如需要实现自定义的排序规则或者对大规模数据进行排序等,我们需要深入理解排序算法,并且根据数据规模、数据分布等因素选择合适的排序算法。

目录
相关文章
|
15天前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
8天前
|
存储 缓存 监控
上网行为监控系统剖析:基于 Java LinkedHashMap 算法的时间序列追踪机制探究
数字化办公蓬勃发展的背景下,上网行为监控系统已成为企业维护信息安全、提升工作效能的关键手段。该系统需实时记录并深入分析员工的网络访问行为,如何高效存储和管理这些处于动态变化中的数据,便成为亟待解决的核心问题。Java 语言中的LinkedHashMap数据结构,凭借其独有的有序性特征以及可灵活配置的淘汰策略,为上网行为监控系统提供了一种兼顾性能与功能需求的数据管理方案。本文将对LinkedHashMap在上网行为监控系统中的应用原理、实现路径及其应用价值展开深入探究。
30 3
|
1月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
|
2月前
|
算法 PyTorch 算法框架/工具
昇腾 msmodelslim w8a8量化代码解析
msmodelslim w8a8量化算法原理和代码解析
134 5
|
17天前
|
存储 机器学习/深度学习 监控
如何监控员工的电脑——基于滑动时间窗口的Java事件聚合算法实现探析​
在企业管理场景中,如何监控员工的电脑操作行为是一个涉及效率与合规性的重要课题。传统方法依赖日志采集或屏幕截图,但数据量庞大且实时性不足。本文提出一种基于滑动时间窗口的事件聚合算法,通过Java语言实现高效、低资源占用的监控逻辑,为如何监控员工的电脑提供一种轻量化解决方案。
34 3
|
2月前
|
消息中间件 Java 应用服务中间件
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
|
2月前
|
监控 算法 安全
基于 C# 的内网行为管理软件入侵检测算法解析
当下数字化办公环境中,内网行为管理软件已成为企业维护网络安全、提高办公效率的关键工具。它宛如一位恪尽职守的网络守护者,持续监控内网中的各类活动,以确保数据安全及网络稳定。在其诸多功能实现的背后,先进的数据结构与算法发挥着至关重要的作用。本文将深入探究一种应用于内网行为管理软件的 C# 算法 —— 基于二叉搜索树的入侵检测算法,并借助具体代码例程予以解析。
56 4
|
2月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
2月前
|
存储 监控 算法
关于员工上网监控系统中 PHP 关联数组算法的学术解析
在当代企业管理中,员工上网监控系统是维护信息安全和提升工作效率的关键工具。PHP 中的关联数组凭借其灵活的键值对存储方式,在记录员工网络活动、管理访问规则及分析上网行为等方面发挥重要作用。通过关联数组,系统能高效记录每位员工的上网历史,设定网站访问权限,并统计不同类型的网站访问频率,帮助企业洞察员工上网模式,发现潜在问题并采取相应管理措施,从而保障信息安全和提高工作效率。
44 7
|
2月前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
109 5

热门文章

最新文章

推荐镜像

更多
OSZAR »