LeetCode-多语言实现冒泡排序以及算法优化改进

目录

一、冒泡排序算法

二、应用场景/前提条件

🌈 优点

📢 缺点

三、经典算法实现并优化改进

方法一:记录最后一次交换位置,下一轮只遍历到该位置

方法二:添加标志位跟踪是否发生交换,无交换则提前终止(针对大部分有序的数组)

方法三 :鸡尾酒排序(双向冒泡排序)

四、习题演练

测验


一、冒泡排序算法

        冒泡排序(Bubble Sort)是一种简单直观的排序算法,其基本思想是:在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;如此重复遍历下去,直到没有再需要交换的元素,最终完成排序。由此可得,在排序过程中,大的数据往下沉,小的数据往上浮,就像水中气泡上升一样,于是将这种排序算法形象地称为冒泡排序。

时间复杂度: 最佳 O(n) | 平均 O(n²) | 最差 O(n²)

空间复杂度: O(1)

稳定性:稳定(相等元素不交换)

二、应用场景/前提条件

  • 适用于小规模数据
  • 对几乎已排序的数据效率较高

🌈 优点

  • 代码简单,容易实现
  • 适合小规模数据排序
  • 对于几乎已经排好序的数据,效率较高
  • 稳定的排序算法

📢 缺点

  • 时间复杂度高,为O(n²)
  • 随着元素数量增加,效率急剧下降
  • 每次只能将一个元素移动到其最终位置,效率不高

三、经典算法实现并优化改进

相较于传统的实现方法来说,有几个可以优化的点,以下使用JS实现演示:

方法一:记录最后一次交换位置,下一轮只遍历到该位置

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>JavaScript 冒泡排序</title>
</head>
<script>function bubbleSort(arr) {let i = arr.length - 1;//设置初始比较范围到数组末尾while(i > 0){ // 外层循环控制排序轮数,当i = 0时,表示没有发生交换,排序完成let position = 0;for (let j = 0; j < i; j++) { // 遍历未排序部分if(arr[j] > arr[ j+ 1]){ // 比较相邻元素position = j; //记录最后一次交换的位置let temp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = temp; // 使用临时变量temp交换两个元素的位置}}i = position; // 下一轮只需比较到上次最后交换的位置,后续每轮都会将当前未排序部分的最大值"冒泡"到正确位置,比较范围逐渐缩小(由i = position控制)console.log(arr.toString());}return arr;}let array = [59 , 34 , 25 , 67 ,15 ,87 ,10 ,99 ,3 ,45]let res_arr = bubbleSort(array)console.log("使用冒泡排序算法的最终排序结果是:"+ res_arr)
</script>
</html>

初始数组:[59, 34, 25, 67, 15, 87, 10, 99, 3, 45]

第1轮遍历(i=9):

  • 比较59和34 → 交换 → [34,59,25,67,15,87,10,99,3,45]
  • 比较59和25 → 交换 → [34,25,59,67,15,87,10,99,3,45]
  • 比较59和67 → 不交换
  • 比较67和15 → 交换 → [34,25,59,15,67,87,10,99,3,45]
  • 比较67和87 → 不交换
  • 比较87和10 → 交换 → [34,25,59,15,67,10,87,99,3,45]
  • 比较87和99 → 不交换
  • 比较99和3 → 交换 → [34,25,59,15,67,10,87,3,99,45]
  • 比较99和45 → 交换 → [34,25,59,15,67,10,87,3,45,99]
    最后一次交换位置:8(99和45交换的位置)

第2轮遍历(i=8):

  • 比较34和25 → 交换 → [25,34,59,15,67,10,87,3,45,99]
  • 比较34和59 → 不交换
  • 比较59和15 → 交换 → [25,34,15,59,67,10,87,3,45,99]
  • 比较59和67 → 不交换
  • 比较67和10 → 交换 → [25,34,15,59,10,67,87,3,45,99]
  • 比较67和87 → 不交换
  • 比较87和3 → 交换 → [25,34,15,59,10,67,3,87,45,99]
  • 比较87和45 → 交换 → [25,34,15,59,10,67,3,45,87,99]
    最后一次交换位置:7(87和45交换的位置)

依次类推.......

第7轮遍历(i=2):

  • 比较10和15 → 不交换
  • 比较15和3 → 交换 → [10,3,15,25,34,45,59,67,87,99]
    最后一次交换位置:1(15和3交换的位置)

第8轮遍历(i=1):

  • 比较10和3 → 交换 → [3,10,15,25,34,45,59,67,87,99]
    最后一次交换位置:0(10和3交换的位置)

最终排序结果:[3,10,15,25,34,45,59,67,87,99]

整个过程共进行了8轮遍历,每次遍历都会将当前未排序部分的最大值"冒泡"到正确位置。随着排序的进行,需要比较的元素越来越少,效率逐渐提高。(这段代码的优化在于记录了最后一次交换的位置,避免了不必要的比较,提高了效率。每次循环后都会打印当前数组状态,方便观察排序过程。)

方法二:添加标志位跟踪是否发生交换,无交换则提前终止(针对大部分有序的数组)

function optimizedBubbleSort(arr) {const n = arr.length;for (let i = 0; i < n - 1; i++) {let swapped = false;for (let j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];swapped = true;}}// 如果没有交换操作,数组已排序完成if (!swapped) break;}return arr;
}

方法三 :鸡尾酒排序(双向冒泡排序)

鸡尾酒排序是冒泡排序的一种变体,它从低到高然后从高到低来回排序,比冒泡排序的效率稍微高一点,在每次排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大值和最小值),从而使排序次数几乎减少了一半。


<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>JavaScript 双向冒泡排序</title>
</head>
<script>function bubbleSort(arr) {let low = 0;let high = arr.length - 1;let temp;while (low < high) {// 正向遍历:找最大值for (let j = low; j < high; j++) {if (arr[j] > arr[j + 1]) {temp = arr[j + 1];arr[j + 1] = arr[j];arr[j] = temp;}}--high;console.log("正向结果:" + arr.toString());// 反向遍历:找最小值for (let j = high; j > low; j--) {if (arr[j] < arr[j - 1]) {temp = arr[j - 1];arr[j - 1] = arr[j];arr[j] = temp;}}++low;console.log("反向结果:" + arr.toString());}return arr;}let array = [59, 34, 25, 67, 15, 87, 10, 99, 3, 45];let res_arr = bubbleSort(array);console.log("最终排序结果:" + res_arr);
</script>
</html>

以下是数组 [59, 34, 25, 67, 15, 87, 10, 99, 3, 45] 的完整鸡尾酒手动排序过程:


排序过程

  1. 初始数组
    [59, 34, 25, 67, 15, 87, 10, 99, 3, 45]

  2. 第1轮正向遍历‌(从左到右找最大值)

    • 比较并交换:59↔34, 59↔25, 59↔67(不交换), 67↔15, 67↔87(不交换), 87↔10, 87↔99(不交换), 99↔3, 99↔45
    • 结果‌:[34, 25, 59, 15, 67, 10, 87, 3, 45, 99](最大值99到末尾)
  3. 第1轮反向遍历‌(从右到左找最小值)

    • 比较并交换:45↔3, 45↔87(不交换), 87↔10, 87↔67(不交换), 67↔15, 67↔59(不交换), 59↔25, 59↔34(不交换)
    • 结果‌:[3, 34, 25, 59, 15, 67, 10, 87, 45, 99](最小值3到开头)
  4. 第2轮正向遍历

    • 比较并交换:34↔25, 34↔59(不交换), 59↔15, 59↔67(不交换), 67↔10, 67↔87(不交换), 87↔45
    • 结果‌:[3, 25, 34, 15, 59, 10, 67, 45, 87, 99]
  5. 第2轮反向遍历

    • 比较并交换:45↔67(不交换), 67↔10, 67↔59(不交换), 59↔15, 59↔34(不交换), 34↔25
    • 结果‌:[3, 10, 25, 15, 34, 59, 45, 67, 87, 99]
  6. 第3轮正向遍历

    • 比较并交换:10↔25(不交换), 25↔15, 25↔34(不交换), 34↔59(不交换), 59↔45, 59↔67(不交换)
    • 结果‌:[3, 10, 15, 25, 34, 45, 59, 67, 87, 99]
  7. 第3轮反向遍历

    • 比较并交换:59↔45(不交换), 45↔34(不交换), 34↔25(不交换), 25↔15(不交换)
    • 无交换发生,排序完成‌。

最终结果

[3, 10, 15, 25, 34, 45, 59, 67, 87, 99]

  1. 交替优化‌:每轮正向遍历将最大值推向右端,反向遍历将最小值推向左端。
  2. 提前终止‌:若某轮遍历无交换,说明数组已有序(如第3轮反向遍历后终止)。
  3. 效率对比‌:比传统冒泡排序减少约50%的轮次(本例仅需3轮双向遍历)。

优化思路:

  1. 双向遍历‌:传统冒泡排序只单向比较(从左到右),而这里同时从左到右和从右到左遍历,可以更快地将最大值和最小值"冒泡"到正确位置。
  2. 缩小范围‌:每次遍历后,未排序部分的边界(lowhigh)会动态调整,减少不必要的比较次数。

对于双向冒泡排序(鸡尾酒排序)的时间复杂度和空间复杂度分析如下:

⏳ 时间复杂度

💾 空间复杂度

  • 原地排序‌:仅使用常数级临时变量(如 templowhigh
  • 额外空间与输入规模无关 → ‌O(1)

🧠 核心结论

指标双向冒泡排序
最坏时间复杂度O(n²)
最好时间复杂度O(n)
平均时间复杂度O(n²)
空间复杂度O(1)(原地排序)

⚠️ ‌说明‌:尽管双向遍历优化了部分场景(如两端元素有序时效率更高),但时间复杂度量级与传统冒泡排序一致。

四、习题演练

2023下半年软件设计师上午题——冒泡排序_冒泡排序选择题

下面推荐一些可以用来练手的 LeetCode 题目:

  • 75. 颜色分类 - 可使用冒泡排序解决,但有更优的解法
  • 283. 移动零 - 可用冒泡排序的思想将零元素"冒泡"到数组末尾
  • 912. 排序数组 - 可使用冒泡排序,但因为数据规模较大,可能会超时

需要注意的是,使用冒泡排序不一定是最优解,仅使用冒泡排序也可能无法解决问题,不过这些题目都能够直接或间接体现冒泡排序核心思想,可以熟悉这个算法。

测验

  1. 冒泡排序是稳定的排序算法吗,为什么 ?
  2. 对于已经排好序的数组,优化版冒泡排序的时间复杂度是多少?
  3. 冒泡排序每一轮遍历后,数组尾部会有什么特点?
  4. 如何优化冒泡排序以提高效率?

测验答案

  1. 是的,冒泡排序是稳定的排序算法。因为只有当前一个元素大于后一个元素时才交换,相等元素不会改变相对位置。
  2. 对于已经排好序的数组,优化版冒泡排序的时间复杂度是O(n)。因为第一轮遍历不会发生交换,优化版会检测到这点并提前终止。
  3. 冒泡排序每一轮遍历后,数组尾部会有一个元素到达其最终位置,且是当前未排序部分中的最大元素。第i轮结束后,末尾i个元素已排好序。
  4. 优化冒泡排序的方法:
    • 添加标志位跟踪是否发生交换,无交换则提前终止
    • 记录最后一次交换位置,下一轮只遍历到该位置
    • 使用双向冒泡(鸡尾酒排序),同时将最大值上浮和最小值下沉

 

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

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

相关文章

JAVA毕业设计227—基于SpringBoot+hadoop+spark+Vue的大数据房屋维修系统(源代码+数据库)

毕设所有选题&#xff1a; https://blog.csdn.net/2303_76227485/article/details/131104075 基于SpringBoothadoopsparkVue的大数据房屋维修系统(源代码数据库)227 一、系统介绍 本项目前后端分离&#xff0c;分为业主、维修人员、管理员三种角色 1、业主&#xff1a; 登…

MADlib —— 基于 SQL 的数据挖掘解决方案(9)—— 数据探索之概率统计

目录 一、概率 1. 概率的定义 2. 概率质量函数与概率密度函数 3. 条件概率 4. 期望值 二、MADlib 的概率相关函数 1. 函数语法 2. 示例 &#xff08;1&#xff09;求标准正态分布下&#xff0c;1 的概率密度函数 &#xff08;2&#xff09;求标准正态分布下&#xff…

耳蜗里的春天

早春的郑州飘着细雨&#xff0c;我牵着女儿小满的手走进市残疾人康复中心时&#xff0c;玻璃门内突然传来一阵清脆的笑声。穿天蓝色毛衣的小女孩戴着粉色耳蜗&#xff0c;正踮脚拍打着墙上的卡通贴画&#xff0c;银色的连接线在她耳后晃动&#xff0c;像一只折翼却仍在起舞的蝴…

OCR(光学字符识别)算法

OCR&#xff08;光学字符识别&#xff09;算法在景区护照阅读器中的应用是核心技术之一&#xff0c;它通过图像处理和机器学习快速提取护照信息&#xff0c;显著提升自动化水平。以下是其具体应用场景、技术实现及优化方向&#xff1a; 一、OCR在护照阅读器中的核心作用 关键信…

html打印合同模板

概述&#xff08;吐槽&#xff09;&#xff1a;记录一个html打印合同模板的功能&#xff0c;技术栈有点杂&#xff0c;千禧年出产老系统的数据库是sqlserver2008&#xff0c;原系统框架是c#&#xff0c;无法二开&#xff0c;因为原系统的合同生成功能出现bug&#xff0c;没有供…

DeepCritic: SFT+RL两阶段训练突破LLM自我监督!显著提升大模型的自我批判能力!!

摘要&#xff1a;随着大型语言模型&#xff08;LLMs&#xff09;的迅速发展&#xff0c;对其输出进行准确反馈和可扩展监督成为一个迫切且关键的问题。利用LLMs作为批评模型以实现自动化监督是一个有前景的解决方案。在本研究中&#xff0c;我们专注于研究并提升LLMs在数学批评…

【深度学习】深度学习中的张量:从多维数组到智能计算单元

✅ 一、n维数组&#xff08;张量&#xff0c;Tensor&#xff09; 1. 定义 张量&#xff08;Tensor&#xff09;是一个通用的n维数组数据结构。 它的维度&#xff08;维数&#xff09;决定了它的形状&#xff0c;例如&#xff1a; 维度名称举例说明0维标量&#xff08;scalar…

以太网MDI信号PCB EMC设计要点

1. PHY侧和RJ45连接器侧通用MDI布局建议 1. MDI差分对保持对称走线&#xff0c;走线上的焊盘封装应一致&#xff0c;焊盘放置位置也应对称。可以减少EMI测试中的模式转换。   2. MDI走线应保持阻抗匹配&#xff0c;从而减少信号线上的反射。   3. MDI走线下需有连续完整的接…

深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙

WebGL&#xff1a;在浏览器中解锁3D世界的魔法钥匙 引言&#xff1a;网页的边界正在消失 在数字化浪潮的推动下&#xff0c;网页早已不再是静态信息的展示窗口。如今&#xff0c;我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室&#xff0c;甚至沉浸式的V…

pysnmp模块中 GET、SET、WALK操作详细分步解析

1. SNMP GET 操作详解 1.1 核心代码结构 from pysnmp.hlapi import *# 定义参数 community public # SNMPv2c 社区名 target_ip 192.168.1.1 # 目标设备 IP oid 1.3.6.1.2.1.1.1.0 # 要查询的 OID# 发起 GET 请求 error_indication, error_status, error_index, …

接收rabbitmq消息

以下是一个使用纯Java&#xff08;非Spring Boot&#xff09;接收RabbitMQ消息的完整实现&#xff0c;包含Maven依赖和持续监听消息的循环&#xff1a; 1. 首先添加Maven依赖 (pom.xml) <dependencies><!-- RabbitMQ Java Client --><dependency><group…

SQL进阶之旅 Day 23:事务隔离级别与性能优化

【SQL进阶之旅 Day 23】事务隔离级别与性能优化 文章简述 在数据库系统中&#xff0c;事务是确保数据一致性和完整性的核心机制。随着业务复杂度的提升&#xff0c;如何合理设置事务隔离级别以平衡并发性能与数据一致性成为开发人员必须掌握的关键技能。本文深入解析事务隔离级…

六.原型模式

一.原型模式的定义 原型模式是一种创建型设计模式&#xff0c;通过复制现有对象&#xff08;原型&#xff09;生成新对象&#xff0c;避免重复初始化成本。需了解以下关键概念&#xff1a; ‌浅拷贝‌&#xff1a;复制基本类型字段&#xff0c;引用类型字段共享内存地址&#…

【笔记】LoRA 理论与实现|大模型轻量级微调

论文链接&#xff1a;LoRA: Low-Rank Adaptation of Large Language Models 官方实现&#xff1a;microsoft/LoRA 非官方实现&#xff1a;huggingface/peft、huggingface/diffusers 这篇文章要介绍的是一种大模型/扩散模型的微调方法&#xff0c;叫做低秩适应&#xff08;也就是…

Cilium动手实验室: 精通之旅---15.Isovalent Enterprise for Cilium: Network Policies

Cilium动手实验室: 精通之旅---15.Isovalent Enterprise for Cilium: Network Policies 1. 环境信息2. 测试环境部署3. 默认规则3.1 测试默认规则3.2 小测验 4. 网络策略可视化4.1 通过可视化创建策略4.2 小测试 5. 测试策略5.1 应用策略5.2 流量观测5.3 Hubble观测5.4 小测试 …

opencv RGB图像转灰度图

这段代码的作用是将一个 3通道的 RGB 图像&#xff08;CV_8UC3&#xff09;转换为灰度图像&#xff08;CV_8UC1&#xff09;&#xff0c;并使用 OpenCV 的 parallel_for_ 对图像处理进行并行加速。 &#x1f50d; 一、函数功能总结 if (CV_8UC3 img.type()) {// 创建灰度图 d…

React Hooks 的原理、常用函数及用途详解

1. ​​Hooks 是什么&#xff1f;​​ Hooks 是 React 16.8 引入的函数式组件特性&#xff0c;允许在不编写 class 的情况下使用 state 和其他 React 特性&#xff08;如生命周期、副作用等&#xff09;。​​本质是一类特殊函数​​&#xff0c;它们挂载到 React 的调度系统中…

学习路之PHP--webman协程学习

学习路之PHP--webman协程学习 一、准备二、配置三、启动四、使用 协程是一种比线程更轻量级的用户级并发机制&#xff0c;能够在进程中实现多任务调度。它通过手动控制挂起和恢复来实现协程间的切换&#xff0c;避免了进程上下文切换的开销 一、准备 PHP > 8.1 Workerman &g…

linux libusb使用libusb_claim_interface失败(-6,Resource busy)解决方案

linux libusb使用libusb_claim_interface失败&#xff08;-6&#xff0c;Resource busy&#xff09;解决方案 ✅ 问题原因&#x1f6e0;️ 解决方案&#x1f538; 方法一&#xff1a;分离内核驱动 libusb_detach_kernel_driver()&#x1f538; 方法二&#xff1a;使用 usb-devi…

使用mpu6500/6050, PID,互补滤波实现一个简单的飞行自稳控制系统

首先&#xff0c;参考ai给出的客机飞机的比较平稳的最大仰府&#xff0c;偏转&#xff0c;和防滚角度&#xff0c;如下&#xff1a; 客机的最大平稳仰俯&#xff08;Pitch&#xff09;、偏转&#xff08;Yaw&#xff09;和防滚&#xff08;Roll&#xff09;角度&#xff0c;通…