目录

1. 冒泡排序 (Bubble Sort)

算法思路分析

代码实现

复杂度分析

2. 选择排序 (Selection Sort)

算法思路分析

代码实现

复杂度分析

3. 插入排序 (Insertion Sort)

算法思路分析

代码实现

复杂度分析

4. 快速排序 (Quick Sort)

算法思路分析

代码实现

复杂度分析

5. 归并排序 (Merge Sort)

算法思路分析

代码实现

复杂度分析

6. 堆排序 (Heap Sort)

算法思路分析

代码实现

复杂度分析

算法性能对比总结


排序算法稳定性解释:

  • 稳定排序:相等的元素在排序后保持原有的相对顺序
  • 不稳定排序:相等的元素在排序后可能改变原有的相对顺序

比如:

原始顺序是 4₁, 2, 4₂, 1, 3,排序后变成了 1, 2, 3, 4₁, 4₂

  • 原来:4₁ 在 4₂ 前面
  • 现在:4₁ 仍然在 4₂ 前面

这就是稳定排序

1. 冒泡排序 (Bubble Sort)

算法思路分析

冒泡排序是最简单的排序算法之一,其核心思想是:

  1. 相邻比较:比较相邻的两个元素,如果第一个比第二个大,则交换它们
  2. 逐步冒泡:每一轮比较后,最大的元素会"冒泡"到数组的末尾
  3. 重复执行:重复n-1轮,直到所有元素都排好序

代码实现

/*** 冒泡排序算法* * 算法思路:* 1. 比较相邻的两个元素,如果第一个比第二个大,则交换它们* 2. 对每一对相邻元素执行同样的操作,从开始第一对到结尾的最后一对* 3. 重复以上步骤,每次都能将当前最大的元素"冒泡"到数组末尾* 4. 重复n-1轮,直到没有任何一对数字需要比较*/
public static void bubbleSort(int[] arr) {int n = arr.length;// 外层循环控制排序轮数,总共需要n-1轮for (int i = 0; i < n - 1; i++) {// 设置一个标志位,用于优化算法// 如果某一轮没有发生交换,说明数组已经有序,可以提前退出boolean swapped = false;// 内层循环进行相邻元素比较和交换// 每轮排序后,最大的元素会"冒泡"到数组末尾// 因此内层循环的范围可以逐渐缩小for (int j = 0; j < n - 1 - i; j++) {// 如果前一个元素大于后一个元素,则交换它们if (arr[j] > arr[j + 1]) {// 交换元素int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;swapped = true;}}// 如果这一轮没有发生交换,说明数组已经有序,可以提前退出if (!swapped) {break;}}
}

复杂度分析

  • 时间复杂度:O(n²) - 最坏和平均情况都是O(n²)
  • 空间复杂度:O(1) - 只需要一个临时变量
  • 稳定性:稳定排序

2. 选择排序 (Selection Sort)

算法思路分析

选择排序的核心思想是:

  1. 找最小值:在未排序序列中找到最小元素
  2. 交换位置:将找到的最小元素与未排序序列的第一个元素交换位置
  3. 重复执行:重复上述过程,直到所有元素都排好序

代码实现

/*** 选择排序算法* * 算法思路:* 1. 在未排序序列中找到最小元素,存放到排序序列的起始位置* 2. 然后,再从剩余未排序元素中继续寻找最小元素* 3. 重复第二步,直到所有元素均排序完毕*/
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;}}// 将找到的最小元素与当前位置的元素交换if (minIndex != i) {int temp = arr[i];arr[i] = arr[minIndex];arr[minIndex] = temp;}}
}

复杂度分析

  • 时间复杂度:O(n²) - 无论最好、最坏、平均情况都是O(n²)
  • 空间复杂度:O(1) - 只需要一个临时变量
  • 稳定性:不稳定排序

3. 插入排序 (Insertion Sort)

算法思路分析

插入排序的核心思想是:

  1. 构建有序序列:将第一个元素看作已排序序列
  2. 逐个插入:对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
  3. 重复执行:重复上述过程,直到所有元素都插入到有序序列中

代码实现

/*** 插入排序算法* * 算法思路:* 1. 从第一个元素开始,该元素可以认为已经被排序* 2. 取出下一个元素,在已经排序的元素序列中从后向前扫描* 3. 如果该元素(已排序)大于新元素,将该元素移到下一位置* 4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置* 5. 将新元素插入到该位置后* 6. 重复步骤2~5*/
public static void insertionSort(int[] arr) {int n = arr.length;// 从第二个元素开始,逐个插入到已排序序列中for (int i = 1; i < n; i++) {// 保存当前要插入的元素int key = arr[i];// j指向已排序序列的最后一个元素int j = i - 1;// 从后向前扫描已排序序列,寻找插入位置// 如果已排序序列中的元素大于key,则将其向后移动while (j >= 0 && arr[j] > key) {arr[j + 1] = arr[j];j--;}// 找到插入位置,将key插入arr[j + 1] = key;}
}

复杂度分析

  • 时间复杂度:O(n²) - 最坏和平均情况,最好情况O(n)(已排序数组)
  • 空间复杂度:O(1) - 只需要一个临时变量
  • 稳定性:稳定排序

4. 快速排序 (Quick Sort)

算法思路分析

快速排序是一种高效的排序算法,使用分治策略:

  1. 选择基准:从数组中选择一个基准元素(通常是最后一个元素)
  2. 分区操作:将数组分为两部分,左边都小于基准,右边都大于基准
  3. 递归排序:对左右两部分分别进行快速排序
  4. 合并结果:由于分区操作,合并时数组已经有序

代码实现

/*** 快速排序算法* * 算法思路:* 1. 选择一个基准元素(通常是最后一个元素)* 2. 将数组分为两部分:左边都小于基准,右边都大于基准* 3. 对左右两部分分别递归执行快速排序* 4. 由于分区操作,合并时数组已经有序*/
public static void quickSort(int[] arr) {quickSort(arr, 0, arr.length - 1);
}/*** 快速排序的递归实现* @param arr 待排序数组* @param low 起始索引* @param high 结束索引*/
private static void quickSort(int[] arr, int low, int high) {// 递归终止条件:当起始索引小于结束索引时if (low < high) {// 进行分区操作,返回基准元素的最终位置int pi = partition(arr, low, high);// 递归排序基准元素左边的子数组quickSort(arr, low, pi - 1);// 递归排序基准元素右边的子数组quickSort(arr, pi + 1, high);}
}/*** 分区操作:将数组分为两部分* @param arr 待分区数组* @param low 起始索引* @param high 结束索引* @return 基准元素的最终位置*/
private static int partition(int[] arr, int low, int high) {// 选择最后一个元素作为基准int pivot = arr[high];// i指向小于基准元素的最后一个位置int i = low - 1;// 遍历数组,将小于基准的元素移到左边for (int j = low; j < high; j++) {// 如果当前元素小于基准if (arr[j] < pivot) {i++;// 交换元素int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}}// 将基准元素放到正确的位置int temp = arr[i + 1];arr[i + 1] = arr[high];arr[high] = temp;// 返回基准元素的最终位置return i + 1;
}

复杂度分析

  • 时间复杂度:O(n log n) - 平均情况,最坏情况O(n²)
  • 空间复杂度:O(log n) - 递归调用栈的深度
  • 稳定性:不稳定排序

5. 归并排序 (Merge Sort)

算法思路分析

归并排序是一种稳定的排序算法,使用分治策略:

  1. 分解:将数组分成两个子数组,递归地对子数组进行排序
  2. 合并:将两个已排序的子数组合并成一个有序数组
  3. 递归执行:重复上述过程,直到所有元素都排好序

代码实现

/*** 归并排序算法* * 算法思路:* 1. 将数组分成两个子数组,递归地对子数组进行排序* 2. 将两个已排序的子数组合并成一个有序数组* 3. 重复上述过程,直到所有元素都排好序*/
public static void mergeSort(int[] arr) {mergeSort(arr, 0, arr.length - 1);
}/*** 归并排序的递归实现* @param arr 待排序数组* @param left 左边界* @param right 右边界*/
private static void mergeSort(int[] arr, int left, int right) {// 递归终止条件:当左边界小于右边界时if (left < right) {// 计算中间位置int mid = left + (right - left) / 2;// 递归排序左半部分mergeSort(arr, left, mid);// 递归排序右半部分mergeSort(arr, mid + 1, right);// 合并两个已排序的子数组merge(arr, left, mid, right);}
}/*** 合并两个已排序的子数组* @param arr 原数组* @param left 左边界* @param mid 中间位置* @param right 右边界*/
private static void merge(int[] arr, int left, int mid, int right) {// 计算两个子数组的长度int n1 = mid - left + 1;int n2 = right - mid;// 创建临时数组来存储两个子数组int[] leftArray = new int[n1];int[] rightArray = new int[n2];// 复制数据到临时数组for (int i = 0; i < n1; i++) {leftArray[i] = arr[left + i];}for (int j = 0; j < n2; j++) {rightArray[j] = arr[mid + 1 + j];}// 合并两个子数组int i = 0, j = 0, k = left;// 比较两个子数组的元素,将较小的元素放入原数组while (i < n1 && j < n2) {if (leftArray[i] <= rightArray[j]) {arr[k] = leftArray[i];i++;} else {arr[k] = rightArray[j];j++;}k++;}// 将剩余的元素复制到原数组while (i < n1) {arr[k] = leftArray[i];i++;k++;}while (j < n2) {arr[k] = rightArray[j];j++;k++;}
}

复杂度分析

  • 时间复杂度:O(n log n) - 无论最好、最坏、平均情况都是O(n log n)
  • 空间复杂度:O(n) - 需要额外的数组来存储合并结果
  • 稳定性:稳定排序

6. 堆排序 (Heap Sort)

算法思路分析

堆排序是一种基于堆数据结构的排序算法:

  1. 构建堆:将数组构建成最大堆(或最小堆)
  2. 提取根节点:将堆的根节点(最大值)与最后一个元素交换
  3. 调整堆:对剩余的n-1个元素重新构建堆
  4. 重复执行:重复上述过程,直到所有元素都排好序

代码实现

/*** 堆排序算法* * 算法思路:* 1. 将数组构建成最大堆* 2. 将堆的根节点(最大值)与最后一个元素交换* 3. 对剩余的n-1个元素重新构建堆* 4. 重复上述过程,直到所有元素都排好序*/
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;// 对剩余的i个元素重新构建最大堆heapify(arr, i, 0);}
}/*** 将以root为根的子树调整为最大堆* @param arr 数组* @param n 堆的大小* @param root 根节点的索引*/
private static void heapify(int[] arr, int n, int root) {// 假设根节点是最大的int largest = root;// 计算左子节点的索引int left = 2 * root + 1;// 计算右子节点的索引int right = 2 * root + 2;// 如果左子节点存在且大于根节点if (left < n && arr[left] > arr[largest]) {largest = left;}// 如果右子节点存在且大于当前最大值if (right < n && arr[right] > arr[largest]) {largest = right;}// 如果最大值不是根节点if (largest != root) {// 交换根节点和最大值int temp = arr[root];arr[root] = arr[largest];arr[largest] = temp;// 递归调整被交换的子树heapify(arr, n, largest);}
}

复杂度分析

  • 时间复杂度:O(n log n) - 无论最好、最坏、平均情况都是O(n log n)
  • 空间复杂度:O(1) - 原地排序
  • 稳定性:不稳定排序

堆排序原理参考:堆排序步骤推演-CSDN博客


算法性能对比总结

排序算法时间复杂度空间复杂度稳定性适用场景
冒泡排序O(n²)O(1)稳定小数据量
选择排序O(n²)O(1)不稳定小数据量,交换次数少
插入排序O(n²)O(1)稳定小数据量,基本有序
快速排序O(n log n)O(log n)不稳定大数据量,实际应用
归并排序O(n log n)O(n)稳定大数据量,要求稳定
堆排序O(n log n)O(1)不稳定大数据量,原地排序

 使用建议:小数据量(n < 50):使用插入排序,代码简单且效率较高;大数据量:优先使用快速排序,平均性能最好;要求稳定性:使用归并排序;内存受限:使用堆排序,原地排序;

推荐:Java的Arrays.sort(),此方法已经为我们提供了高效的排序实现

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PHP语言基础知识(超详细)第一节

一. PHP简介: PHP即“超文本预处理器”,创建于1994年,是一种通用开源脚本语言。PHP是在服务器端执行的脚本语言,与C语言类似,是常用的网站编程语言。PHP独特的语法混合了C、Java、Perl以及 PHP 自创的语法。利于学习,使用广泛,主要适用于Web开发领域。 二. PHP的优点:…

Reloaded-II项目:解决GitHub下载Mod缺少DLL文件的问题

Reloaded-II项目&#xff1a;解决GitHub下载Mod缺少DLL文件的问题 问题现象分析 在使用Reloaded-II项目加载从GitHub下载的"Debug Stuff"模组时&#xff0c;用户遇到了一个常见的技术问题&#xff1a;系统提示缺少DLL文件&#xff0c;导致模组无法正常运行。这种情况…

0-1搭建springboot+vue的教务管理系统(核心源码)

目录 后端核心代码&#xff1a; control层 service 层 mapper层 后端核心代码&#xff1a; control层&#xff1a; classControlsImpl package com.itheima.controls.impl;import com.itheima.mapper.ClassMapper; import com.itheima.pojo.Clazz; import com.itheima.po…

Ubuntu中man手册不全解决以及man手册中英文切换方法

步入正题之前&#xff0c;先来帮助大家了解一下man手册的作用&#xff0c;让大家对其有更深的理解并充分利用一、man 手册的作用​man 手册&#xff0c;即 manual pages&#xff0c;是 Linux 系统自带的帮助文档系统。通过 man 命令&#xff0c;用户能快速获取系统中几乎所有命…

数据结构----线性表(栈及其栈的实现)C语言 学习笔记

栈&#xff1a;线性逻辑结构栈的分类 顺序栈&#xff1a;顺序存储结构实现的栈链式栈&#xff1a;链式存储结构实现的栈相关概念线性表&#xff1a;可以在任意位置操作栈&#xff1a;对线性表进行约束只能在一端插入和删除操作的线性表&#xff0c;中间不允许操作。栈底&#x…

手滑误操作? vue + Element UI 封装二次确认框 | 附源码

一诺最近在做后台管理系统时&#xff0c;遇到一个很常见但又容易被忽视的小问题&#xff1a;单选框切换时&#xff0c;用户一不小心点错&#xff0c;原有配置就没了&#xff0c;数据丢失&#xff0c;后悔也来不及。你是不是也遇到过类似的场景&#xff1f;比如切换网络模式、切…

力扣刷题367——有效的完全平方数

力扣刷题367——有效的完全平方数&#xff08;69的相似题&#xff09; 题目&#xff1a; 给你一个正整数 num 。如果 num 是一个完全平方数&#xff0c;则返回 true &#xff0c;否则返回 false 。 完全平方数 是一个可以写成某个整数的平方的整数。换句话说&#xff0c;它可以…

kubernetes架构原理与集群环境部署

kubernetes架构原理与集群环境部署概述为什么需要 KubernetesKubernetes 带来的挑战kubernetes架构解析master 节点的组件(1)API server(2)scheduler(3)Controller Manager(4)etcdNode 节点包含的组件(1)容器运行时(2)kubelet(3)kube-proxy代理kubernetes 网络插件(1)Flannel 网…

Python爬虫实战:Requests与Selenium详解

目录 一 网络爬虫的了解 1 爬虫库 urllib库 requests库 scrapy库 selenium库 2 注意&#xff01;&#xff01;&#xff01; 二 requests库 1 request库的安装 2 认识网页资源 3 获取网页资源 4 小案例 5 代理服务器 三 selenium 1 准备工作 2 应用 3 实例 一 网…

什么是乐观锁?什么是悲观锁?

&#x1f512; 深入浅出&#xff1a;乐观锁 vs 悲观锁终极对决&#xff01;面试必考知识点详解 各位CSDN的小伙伴们好呀&#xff01;&#x1f44b; 我是雪碧聊技术&#xff0c;今天给大家带来高并发编程中的核心概念——乐观锁与悲观锁的深度解析&#xff01;&#x1f4bb; 无论…

HTML前端性能优化完整指南

图片优化&#xff1a;性能优化的重中之重 重新审视图片的必要性 在开始优化之前&#xff0c;首先需要思考一个根本问题&#xff1a;要实现预期的视觉效果&#xff0c;真的需要使用图片吗&#xff1f; 随着Web技术的快速发展&#xff0c;许多以往只能通过图片实现的效果&…

数据炼金术:用Python做智能数据整理员

数据炼金术&#xff1a;用Python做智能数据整理员 解锁自动化魔法&#xff1a;文件批量重命名Excel智能清洗数据净化全流程实战 一、数据整理的困境与破局之道 你是否面临这些数据噩梦场景&#xff1f; &#x1f9e9; ​​混乱文件目录​​&#xff1a;最终版_报告_V4(1).doc…

HTML基础P1 | HTML基本元素

HTML标签标签名放在<>中&#xff0c;如<body>大部分标签成对出现&#xff0c;如<h1>为开始标签&#xff0c;</h1>为其对应的结束标签&#xff0c;少数标签只有开始标签&#xff0c;如换行标签<br/>&#xff0c;成为"单标签"有的标签中…

LVS集群搭建

集群是为了解决某个特定问题将多台计算机组合起来形成的单个系统知识点&#xff1a;1.关键术语&#xff1a;VS&#xff1a;Virtual Server&#xff08;调度器&#xff09;RS&#xff1a;Real Server&#xff08;真实服务器&#xff09;CIP&#xff1a;Client IP&#xff08;客户…

吴恩达《AI for everyone》第一周课程笔记

课程的核心目标&#xff1a;- AI是什么&#xff1f; - AI能做什么&#xff1f; - AI最擅长什么类型的任务&#xff1f; - AI怎么做决策&#xff1f; - 企业为什么需要AI战略&#xff1f;导航Machine Learning 机器学习> 最常见的机器学习类型&#xff1a; > 人工智能中最…

iOS App 电池消耗管理与优化 提升用户体验的完整指南

在当今智能手机的使用中&#xff0c;电池寿命和续航能力是用户选择App时的重要考虑因素之一。iOS设备的电池管理功能较为封闭&#xff0c;这也让开发者、产品经理以及普通用户对于App的电池消耗有时无法全面了解。而如果你的App因电池消耗过快而遭到用户卸载&#xff0c;无论功…

关于用git上传远程库的一些常见命令使用和常见问题:

克隆远程库gitee到本地用命令git clone git clone https://gitee.com/automated-piggy-senior/20250717-test.gitLinux/macOS 终端&#xff1a; 执行 touch readme.txt&#xff08;创建空文件&#xff09;&#xff0c;或 echo "这是说明文件" > readme.txt&#…

想删除表中重复数据,只留下一条,sql怎么写

PostgreSQL 方法: DELETE FROM tbl_case_model WHERE id NOT IN (SELECT MIN(id) -- 保留id最小的记录FROM tbl_case_modelGROUP BYcolumn1, -- 替换为实际重复列名column2, -- 继续添加重复列... -- [所有需要比较的列] );因为我这次遇到的情况比较特殊&#xff0…

微服务中token鉴权设计的4种方式

1. JWT鉴权 「概述」&#xff1a;JWT是一种用于双方之间安全传输信息的简洁的、URL安全的令牌标准。它基于JSON格式&#xff0c;包含三个部分&#xff1a;头部&#xff08;Header&#xff09;、负载&#xff08;Payload&#xff09;和签名&#xff08;Signature&#xff09;。J…

nodejs搭建

1.创建一个空文件夹&#xff0c;在vscode中打开 2.执行命令开启package文件 npm init -y3.设置根目录文件app.js 先执行 npm install express 命令安装 express 模块 执行 npm install cors 命令安装 cors 模块 // app.js const express require(express) const app express…