六大排序算法(Java版):从插入排序到快速排序(含图解)

news2024/11/25 11:31:34

目录

插入排序 (Insertion Sort)

直接插入排序的特性总结:

选择排序 (Selection Sort)

直接选择排序的特性总结

冒泡排序 (Bubble Sort) 

冒泡排序的特性总结

堆排序(Heap Sort)

堆排序的特性总结

希尔排序 (Shell Sort) 

 希尔排序的特性总结

快速排序(Quick Sort)

Hoare版

 挖坑法

 前后指针

快速排序总结

总结


在计算机科学中,排序是一个基本的算法问题。排序算法可以将一组数据按照一定的顺序排列,这有助于提高搜索、查找和其他操作的效率。本文将介绍六种常见的排序算法,包括插入排序、希尔排序、选择排序、冒泡排序、堆排序和快速排序,每种算法都有其独特的特点和适用场景。

插入排序 (Insertion Sort)

插入排序是一种简单直观的排序算法,它逐步构建有序序列。它的工作原理是从未排序部分取出一个元素,将其插入到已排序部分的适当位置。插入排序的时间复杂度为O(n^2),适用于小型数据集。就像我们玩扑克牌一样~~😁

动画演示:

代码示例:

public static void insertionSort(int[] arr) {
        int n = arr.length;
        for (int i = 1; i < n; i++) {
            int key = arr[i];
            int j = i - 1;

            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j];
                j--;
            }
            arr[j + 1] = key;
        }
    }

插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

直接插入排序的特性总结:

1. 元素集合越接近有序,直接插入排序算法的时间效率越高

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1),它是一种稳定的排序算法

4. 稳定性:稳定

选择排序 (Selection Sort)

选择排序一种简单但低效的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 

动画演示:

代码示例:

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

直接选择排序的特性总结

1. 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:不稳定

冒泡排序 (Bubble Sort) 

冒泡排序是一种基本的交换排序算法,它重复遍历数据并比较相邻元素,如果它们的顺序不正确,则交换它们。冒泡排序的时间复杂度为O(n^2),与选择排序一样,适用于小型数据集。

动画演示:

代码示例: 

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

冒泡排序的特性总结

1. 冒泡排序是一种非常容易理解的排序

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:稳定

堆排序(Heap Sort)

堆排序使用二叉堆数据结构来实现排序。它将待排序数据构建成一个堆,然后逐步将堆顶元素与最后一个元素交换,然后对剩余部分重新构建堆。堆排序的时间复杂度为O(nlogn),性能较好,特别适用于大型数据集。

动画演示:

代码示例:

public class HeapSort {
    public static void heapSort(int[] arr) {
        int n = arr.length;

        // 构建最大堆
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(arr, n, i);
        }

        // 逐个将最大元素移到末尾
        for (int i = n - 1; i > 0; i--) {
            // 交换根节点(最大值)和当前未排序部分的末尾元素
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;

            // 对剩余部分重新构建最大堆
            heapify(arr, i, 0);
        }
    }

    public static void heapify(int[] arr, int n, int i) {
        int largest = i;
        int left = 2 * i + 1;
        int right = 2 * i + 2;

        // 找到左子节点和右子节点中的最大值
        if (left < n && arr[left] > arr[largest]) {
            largest = left;
        }
        if (right < n && arr[right] > arr[largest]) {
            largest = right;
        }

        // 如果最大值不是根节点,则交换根节点和最大值,并继续堆化
        if (largest != i) {
            int swap = arr[i];
            arr[i] = arr[largest];
            arr[largest] = swap;
            heapify(arr, n, largest);
        }
    }

    public static void main(String[] args) {
        int[] arr = {12, 11, 13, 5, 6, 7};
        heapSort(arr);
        System.out.println("堆排序结果:");
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }
}

堆排序的特性总结

1. 堆排序使用堆来选数,效率就高了很多。

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(1)

4. 稳定性:不稳定

希尔排序 (Shell Sort) 

希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成多个组, 所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达 =1时,所有记录在统一组内排好序。

动画演示:

代码示例:

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 temp = arr[i];
                int j = i;
                while (j >= gap && arr[j - gap] > temp) {
                    arr[j] = arr[j - gap];
                    j -= gap;
                }
                arr[j] = temp;
            }
        }
    }

 希尔排序的特性总结

1. 希尔排序是对直接插入排序的优化。

2. 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

3. 希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定:

快速排序(Quick Sort)

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有 元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

动画演示:

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int[] array, int left, int right)
{
    if(right - left <= 1)
        return;
    // 按照基准值对array数组的 [left, right)区间中的元素进行划分
    int div = partion(array, left, right);
    // 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)
    // 递归排[left, div)
    QuickSort(array, left, div);
    // 递归排[div+1, right)
    QuickSort(array, div+1, right);
}

上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,我们在写递归框架时可想想二叉树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。

Hoare版

private static int partition(int[] array, int left, int right) {
    int i = left;
    int j = right;
    int pivot = array[left];
    while (i < j) {
        while (i < j && array[j] >= pivot) {
            j--;
        }
    while (i < j && array[i] <= pivot) {
            i++;
        }
    swap(array, i, j);
    }
    swap(array, i, left);
    return i;
}

 挖坑法

private static int partition(int[] array, int left, int right) {
    int i = left;
    int j = right;
    int pivot = array[left];
    while (i < j) {
        while (i < j && array[j] >= pivot) {
            j--;
        }
        array[i] = array[j];
        while (i < j && array[i] <= pivot) {
            i++;
        }
        array[j] = array[i];
    }
    array[i] = pivot;
    return i;
}

 前后指针

写法一: 

private static int partition(int[] array, int left, int right) {
    int prev = left ;
    int cur = left+1;
    while (cur <= right) {
        if(array[cur] < array[left] && array[++prev] != array[cur]) {
        swap(array,cur,prev);
        }
        cur++;
    }
    swap(array,prev,left);
    return prev;
}

写法二:

private static int partition(int[] array, int left, int right) {
    int d = left + 1;
    int pivot = array[left];
    for (int i = left + 1; i <= right; i++) {
        if (array[i] < pivot) {
            swap(array, i, d);
            d++;
        }
    }
    swap(array, d - 1, left);
    return d - 1;
}

快速排序总结

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2. 时间复杂度:O(N*logN)

3. 空间复杂度:O(logN)

4. 稳定性:不稳定

 

总结

在这篇博客中,我们深入探讨了六种常见的排序算法,包括插入排序、希尔排序、选择排序、冒泡排序、堆排序和快速排序。以下是对每个排序算法的简要总结:

  1. 插入排序:逐步构建有序序列,适用于小型数据集,时间复杂度为O(n^2)。

  2. 希尔排序:改进的插入排序,通过分组排序提高效率,平均时间复杂度为O(nlogn)。

  3. 选择排序:每轮选择最小元素并放在已排序部分的末尾,适用于小型数据集,时间复杂度为O(n^2)。

  4. 冒泡排序:通过交换相邻元素将最大元素逐步移动到未排序部分的末尾,适用于小型数据集,时间复杂度为O(n^2)。

  5. 堆排序:使用堆数据结构实现排序,时间复杂度为O(nlogn),适用于大型数据集。

  6. 快速排序:分治排序算法,选择基准元素,将数据分为两个子数组,时间复杂度为O(nlogn),性能良好。

每个排序算法都有其独特的特点和适用场景,选择合适的算法取决于数据规模、性能需求和具体应用场景。这些排序算法的Java示例代码和详细解释有助于理解它们的工作原理和用法。 

总之,排序算法是计算机科学中的基础知识,了解这些算法对于编写高效的程序至关重要。

下一期我会总结一下快速排序的优化方法,希望大家支持~~🤩 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/989118.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis】3、Redis主从复制、哨兵、集群

Redis主从复制 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。 默认情况下&#xff0c;每台Redis服务器…

SpringMvc 之crud增删改查应用

目录 1.创建项目 2.配置文件 2.1pom.xml文件 2.2 web.xml文件 2.3 spring-context.xml 2.4 spring-mvc.xml 2.5 spring-MyBatis.xml 2.6 jdbc.properties 数据库 2.7 generatorConfig.xml 2.8 日志文件log4j2 3.后台代码 3.1 pageBean.java 3.2切面类 3.3 biz层…

米尔电子|第十六届STM32全国研讨会即将走进11个城市

2023年9月12日至10月27日&#xff0c;以“STM32&#xff0c;不止于芯”为主题的第十六届STM32全国巡回研讨会将走进11个城市。本届研讨会为全天会议&#xff0c;我们将围绕STM32最新产品开展技术演讲和方案演示。 本次STM32全国研讨会&#xff0c;米尔电子将现场展出STM32相关…

Unity——导航系统补充说明

一、导航系统补充说明 1、导航与动画 我们可以通过设置动画状态机的变量&#xff0c;让动画匹配由玩家直接控制的角色的移动。那么自动导航的角色如何与动画系统结合呢&#xff1f; 有两个常用的属性可以获得导航代理当前的状态&#xff1a; 一是agent.velocity&#xff0c;…

GO语言网络编程(并发编程)并发介绍,Goroutine

GO语言网络编程&#xff08;并发编程&#xff09;并发介绍&#xff0c;Goroutine 1、并发介绍 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。 B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更…

Java“牵手”天猫商品详情数据,天猫商品详情API接口,天猫API接口申请指南

天猫平台商品详情接口是开放平台提供的一种API接口&#xff0c;通过调用API接口&#xff0c;开发者可以获取天猫商品的标题、价格、库存、月销量、总销量、库存、详情描述、图片等详细信息 。 获取商品详情接口API是一种用于获取电商平台上商品详情数据的接口&#xff0c;通过…

重排链表(算法时间复杂度O(NlogN))

给定一个单链表 L1​→L2​→⋯→Ln−1​→Ln​&#xff0c;请编写程序将链表重新排列为 Ln​→L1​→Ln−1​→L2​→⋯。例如&#xff1a;给定L为1→2→3→4→5→6&#xff0c;则输出应该为6→1→5→2→4→3。 输入格式&#xff1a; 每个输入包含1个测试用例。每个测试用例…

2023-9-8 扩展欧几里得算法

题目链接&#xff1a;扩展欧几里得算法 #include <iostream> #include <algorithm>using namespace std;int exgcd(int a, int b, int &x, int &y) {if(!b){x 1, y 0;return a;}int d exgcd(b, a % b, y, x);y - a / b * x;return d; }int main() {int…

数据结构之栈的实现(附源码)

目录 一、栈的概念及结构 ​二、栈的实现 三、初学栈的练习题 一、栈的概念及结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出…

Android SDK 上手指南||第十一章 虚拟与物理设备

第十一章 虚拟与物理设备 在之前的文章里&#xff0c;大家已经了解了Android项目当中的基本元素、接触了用户界面的设计以及数据存储方案。接下来&#xff0c;我们将一同探索如何在物理及虚拟设备上运行自己的应用程序并与之互动。在系列文章的下一篇中&#xff0c;我们将分步…

【漏洞复现】一米OA存在任意文件读取漏洞

漏洞描述 一米OA协同办公系统,集成了OA办公自动化系统、手机客户端、专业报表工具,为全国千万企业用户提供全功能、性价比高的OA软件。 该OA系统的getfile.jsp文件存在任意文件上传漏洞,攻击者通过漏洞可以获取服务器的敏感信息。 免责声明 技术文章仅供参考,任何个人和…

浅谈高并发分布式架构演进路径

前言 一直很想写一篇能覆盖分布式方面的文章&#xff0c;但当写的时候发现分布式技术发展到现在阶段&#xff0c;有太多的最佳实践和方法论&#xff0c;网上的文章也特别多&#xff0c;根本无从下手。思来想去&#xff0c;发现很多最佳实践都是有一定的理论支撑&#xff0c;我…

图书管理系统 毕业设计

图书管理系统 毕业设计 摘 要 摘要&#xff1a;二十一世纪是信息的世纪&#xff0c;随着社会经济的发展&#xff0c;社会信息化程度也越来越高&#xff0c;学校作为教育与科技的先行者&#xff0c;优秀的技术往往会被所率先学校采用。优秀的学校管理决策者一定会毫不犹豫地选…

vue中获取农历时间

1.下载依赖 npm i chinese-lunar-calendar 2.局部使用 import { getLunar } from chinese-lunar-calendar; 3.使用 getLunar(new Date().getFullYear(), new Date().getMonth() 1, new Date().getDate()) 结果

【Java】高效利用异常处理技巧

文章目录 前言Java异常体系Error和ExceptionThrowable 常见的错误和异常编译时异常运行时异常Error 异常的处理捕获异常声明抛出异常类型 手动抛出异常对象&#xff1a;throw语法格式注意事项 自定义异常为什么需要自定义异常类 小结 前言 在使用计算机语言进行项目开发的过程…

C++ 学习之深拷贝 和 浅拷贝

前言 在C中&#xff0c;浅拷贝和深拷贝是涉及对象复制的两种不同方式&#xff0c;它们之间的关键区别在于拷贝对象时是否复制对象所指向的数据。 正文 浅拷贝&#xff08;Shallow Copy&#xff09;&#xff1a; 浅拷贝只复制对象本身&#xff0c;而不复制对象所指向的数据。…

redis(1)-hiredis-Windows下的编译

1.linux编译说明文档 GitHub - sewenew/redis-plus-plus: Redis client written in C 2.hiredis 编译 2.1 hiredis下载 https://github.com/redis/hiredis.git 2.2 hiredis cmake编译 2.2.1 配置生成:ConfigeGenerateOpen Project 配置源目录&#xff1a;…

【收藏】2023年成人高考题型分析、考试技巧

答题技巧 考试过程中&#xff0c;会做的题目按照自己的方法做&#xff0c;会做的务必一定要拿到分&#xff1b;不会做的&#xff0c;多得一分是一分。 1.字迹一定要工整&#xff0c;切勿潦草&#xff0c;减少涂改&#xff0c;尤其是语文&#xff0c;主观题和作文是没有标准答…

ubuntu下安装vscode代码编辑器

1、安装gcc&#xff08;安装前准备工作&#xff09; ubuntu下安装gcc报错&#xff1a; sudo apt get install gcc 正在等待缓存锁&#xff1a;无法获得锁 /var/lib/dpkg/lock-frontend。锁正由进程 6809&#xff08;unattended-upgr&#xff09;持有 方式1&#xff1a;没有…