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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Java中的六种经典比较排序算法:代码实现全解析

一、 前言

1.1 引入

排序算法是程序开发和计算机科学中常见的算法之一。排序算法可以对一个未排序的数据集合进行排序,使得数据集合中的元素按照一定的顺序排列。排序算法是算法分析的重要内容之一,因为排序算法的效率影响着程序的性能和稳定性。

1.2 目的

本文的目的是介绍常见的排序算法,并且通过代码示例演示它们的实现过程。本文会逐一介绍冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序等六种排序算法,并对它们的原理、思路、代码实现及时间复杂度进行详细分析。最后通过性能比较实验,比较这些算法在不同数据规模下的耗时情况,从而得出各种算法的优劣。

二、 排序算法概述

2.1 什么是排序算法

排序算法是一种对数据集合进行排序的算法,按照某种顺序重新排列数据集合中的元素。排序算法可以应用于各种领域,例如程序开发、数据库查询优化等。

2.2 排序算法分类

常见的排序算法可分为以下几类:

(1)比较排序:通过比较数据集合中元素的大小关系来进行排序。比较排序算法包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序等。

(2)非比较排序:不需要比较数据集合中元素的大小关系来进行排序,而是通过类似于哈希表的方式将数据集合中的元素进行分配。非比较排序算法包括计数排序、桶排序、基数排序等。

2.3 排序算法比较

不同的排序算法有不同的时间复杂度和空间复杂度,不同的应用场景需要选择不同的排序算法。下表列出了常见的排序算法,以及它们的时间复杂度和空间复杂度。

排序算法 平均时间复杂度 最优时间复杂度 最坏时间复杂度 空间复杂度 排序稳定性
冒泡排序(Bubble Sort) O(n^2) O(n) O(n^2) O(1) 稳定
选择排序(Selection Sort) O(n^2) O(n^2) O(n^2) O(1) 不稳定
插入排序(Insertion Sort) O(n^2) O(n) O(n^2) O(1) 稳定
快速排序(Quick Sort) O(nlogn) O(nlogn) O(n^2) O(logn)~O(n) 不稳定
归并排序(Merge Sort) O(nlogn) O(nlogn) O(nlogn) O(n) 稳定
堆排序(Heap Sort) O(nlogn) O(nlogn) O(nlogn) O(1) 不稳定
计数排序(Counting Sort) O(n+k) O(n+k) O(n+k) O(k) 稳定
基数排序(Radix Sort) O(kn) O(kn) O(kn) O(n+k) 稳定

这些是时间复杂度的表示法,常常用来衡量算法的效率和实用性:

时间复杂度 含义
O(1) 常数时间复杂度
O(logn) 对数时间复杂度
O(n) 线性时间复杂度
O(nlogn) 线性对数时间复杂度
O(n^2) 平方时间复杂度
O(kn) 线性乘以常数时间复杂度
O(n+k) 线性加常数时间复杂度

根据表格中的数据,我们可以得出一些结论:

(1)冒泡排序、选择排序和插入排序虽然实现简单,但其时间复杂度都比较高,不适合处理大规模的数据集合。

(2)希尔排序的时间复杂度比较稳定,是一种比较实用的排序算法。

(3)归并排序和快速排序都是基于分治思想的排序算法,它们的时间复杂度比较低,是处理大规模数据集合的不二选择。

三、 冒泡排序

3.1 原理与思想

冒泡排序是一种比较简单的排序算法,它重复地遍历要进行排序的数组,比较相邻两个元素的大小,如果前一个元素大于后一个元素,则交换它们的位置。这样一遍遍历下来,每次都将数组中最大的元素“冒泡”到最后面。如此操作,直到所有元素都排列好位置。

3.2 代码实现

下面是冒泡排序的代码实现:

public static void bubbleSort(int[] arr) {
    int len = arr.length;
    for (int i = 0; i < len - 1; i++) {
        for (int j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

3.3 时间复杂度分析

时间复杂度的表示法的含义可以在2.3查看 冒泡排序的时间复杂度为 O(n^2),因此在处理大规模数据时,效率较低。具体来说,最坏情况下需要执行 n*(n-1)/2 次比较和交换,而最优情况下则只需要执行 n-1 次比较和 0 次交换。在平均情况下,冒泡排序需要执行 n*(n-1)/4 次比较和交换。由于时间复杂度为 O(n^2),因此冒泡排序不适合处理大规模数据的排序问题,但由于其思想简单,实现容易,并且常常被用作教学用例,以帮助学生理解排序算法的基本原理。

四、 选择排序

4.1 原理与思想

选择排序是一种简单直观的排序算法,它的基本思想是:每次在待排序的数组中选取最小的元素,然后把它和数组的第一个元素交换位置,接着在剩下的元素中再选取最小的元素,放在已排好序的数组的最后面。如此操作,直到所有元素都排列好位置。

4.2 代码实现

public static void selectionSort(int[] arr) {
    int len = arr.length;
    for (int i = 0; i < len - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        int temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
    }
}

4.3 时间复杂度分析

时间复杂度的表示法的含义可以在2.3查看

选择排序的时间复杂度为 O(n^2),因此与冒泡排序一样,不适合处理大规模数据的排序问题。具体来说,在平均情况下需要执行 n*(n-1)/2 次比较和 n-1 次交换。在最坏情况下,需要执行 n*(n-1)/2 次比较和 n-1 次交换。在最优情况下,也需要执行 n*(n-1)/2 次比较和 0 次交换。虽然时间复杂度比较高,但实现简单,不占用额外的内存空间。

五、 插入排序

5.1 原理与思想

插入排序是一种简单直观的排序算法,它的基本思想是:将待排序的数组分为已排好序的部分和未排序的部分,从未排序的部分中取出一个元素插入到已排好序的部分中,使得插入后仍然有序。如此操作,直到所有元素都排列好位置。

5.2 代码实现

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

5.3 时间复杂度分析

时间复杂度的表示法的含义可以在2.3查看

对于插入排序,时间复杂度取决于需要进行排序的数据的数量以及数据的状态。最好情况下,当数据已经按照从小到大的顺序排序时,插入排序的时间复杂度为O(n)。最坏情况下,当数据以从大到小的顺序排序时,插入排序的时间复杂度为O(n^2)。由于插入排序在大多数情况下执行效率很高,因为它仅仅需要比较少量的元素。

目录
相关文章
|
11天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
10天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
35 2
|
14天前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
66 6
|
6天前
|
Java 测试技术 API
Java 反射机制:深入解析与应用实践
《Java反射机制:深入解析与应用实践》全面解析Java反射API,探讨其内部运作原理、应用场景及最佳实践,帮助开发者掌握利用反射增强程序灵活性与可扩展性的技巧。
|
11天前
|
存储 算法 Java
Java Set深度解析:为何它能成为“无重复”的代名词?
Java的集合框架中,Set接口以其“无重复”特性著称。本文解析了Set的实现原理,包括HashSet和TreeSet的不同数据结构和算法,以及如何通过示例代码实现最佳实践。选择合适的Set实现类和正确实现自定义对象的hashCode()和equals()方法是关键。
23 4
|
14天前
|
Java 编译器 数据库连接
Java中的异常处理机制深度解析####
本文深入探讨了Java编程语言中异常处理机制的核心原理、类型及其最佳实践,旨在帮助开发者更好地理解和应用这一关键特性。通过实例分析,揭示了try-catch-finally结构的重要性,以及如何利用自定义异常提升代码的健壮性和可读性。文章还讨论了异常处理在大型项目中的最佳实践,为提高软件质量提供指导。 ####
|
18天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
19天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
53 4
|
18天前
|
存储 分布式计算 Java
存算分离与计算向数据移动:深度解析与Java实现
【11月更文挑战第10天】随着大数据时代的到来,数据量的激增给传统的数据处理架构带来了巨大的挑战。传统的“存算一体”架构,即计算资源与存储资源紧密耦合,在处理海量数据时逐渐显露出其局限性。为了应对这些挑战,存算分离(Disaggregated Storage and Compute Architecture)和计算向数据移动(Compute Moves to Data)两种架构应运而生,成为大数据处理领域的热门技术。
39 2
|
18天前
|
设计模式 安全 Java
Java编程中的单例模式深入解析
【10月更文挑战第31天】在编程世界中,设计模式就像是建筑中的蓝图,它们定义了解决常见问题的最佳实践。本文将通过浅显易懂的语言带你深入了解Java中广泛应用的单例模式,并展示如何实现它。
下一篇
无影云桌面