你确定懂冒泡排序?用动画的方式讲懂冒泡排序及其优化方式

简介: 基本概念冒泡排序是一种基础的排序算法。其基本思想是通过不断地比较相邻元素并在必要时进行交换,将最大(或最小)的元素"冒"到序列的一端。排序步骤先来感受到冒泡排序的步骤吧

基本概念

冒泡排序是一种基础的排序算法。其基本思想是通过不断地比较相邻元素并在必要时进行交换,将最大(或最小)的元素"冒"到序列的一端。

排序步骤

先来感受到冒泡排序的步骤吧

以数组 [5, 3, 8, 4, 6]为例,冒泡排序的步骤如下:

  • 第一轮排序:

比较相邻的元素。第一次比较5和3,5大于3,交换它们两个,数组变成 [3, 5, 8, 4, 6];接着比较5和8,5小于8,不用交换,然后比较8和4,8大于4,交换,数组变为 [3, 5, 4, 8, 6];最后比较8和6,8大于6,交换,数组变为 [3, 5, 4, 6, 8]。这样,第一轮比较结束后,最大的数8被排到了最后。

  • 第二轮排序:

再次从前向后比较相邻的元素,这次因为8已经是最大的元素在末尾,所以不再参与比较。先比较3和5,3小于5,不用交换;然后比较5和4,5大于4,交换,数组变为 [3, 4, 5, 6, 8];接着比较5和6,5小于6,不用交换。这样,第二轮排序结束,第二大的元素6也排到了它应该在的位置。

  • 后续轮排序:

如此反复进行,每一轮比较的元素对都比上一轮少一对。直至完成所有的排序。

最终,数组 [5, 3, 8, 4, 6] 被排序为 [3, 4, 5, 6, 8]

冒泡排序的实现

let array = [5, 3, 8, 4, 6];
for(let i = 0; i < array.length; i++) {
  for(let j = 0; j < array.length - i - 1; j++) {
    if(array[j] > array[j + 1]) {
      // 交换元素
      let temp = array[j];
      array[j] = array[j + 1];
      array[j + 1] = temp;
    }
  }
}
console.log(array);  // 输出: [3, 
4, 5, 6, 8]

此算法的时间复杂度为O(n^2),因此在处理大量数据时可能效率较低。

优化策略

交换标记

如果在一次遍历过程中没有发生过交换,那么意味着序列已经是有序的,不需要继续排序。我们可以通过设置一个标记来优化算法。如果在某次遍历中没有发生交换,则直接结束排序。

这个优化对于已经部分有序的序列来说,可以大幅度提高效率。

let array = [5, 3, 8, 4, 6];
let swapped = true;
while(swapped) {
  swapped = false;
  for(let i = 0; i < array.length - 1; i++) {
    if(array[i] > array[i + 1]) {
      let temp = array[i];
      array[i] = array[i + 1];
      array[i + 1] = temp;
      swapped = true;
    }
  }
}
console.log(array);  // 输出: [3, 4, 5, 6, 8]

双向冒泡排序

一趟遍历只能确保最大(或最小)的数被移到序列一端,在双向冒泡排序中,一趟遍历包括了两个过程,一个从头至尾,一个从尾至头,这样就能确保在一趟遍历后,最大和最小的数都被移到了正确的位置。

let array = [5, 3, 8, 4, 6];
let swapped;
let start = 0;
let end = array.length - 1;
while(start < end) {
  for(let i = start; i < end; i++) {
    if(array[i] > array[i + 1]) {
      let temp = array[i];
      array[i] = array[i + 1];
      array[i + 1] = temp;
      swapped = i;
    }
  }
  end = swapped;
  for(let i = end; i > start; i--) {
    if(array[i] < array[i - 1]) {
      let temp = array[i];
      array[i] = array[i - 1];
      array[i - 1] = temp;
      swapped = i;
    }
  }
  start = swapped;
}
console.log(array);  // 输出: [3, 4, 5, 6, 8]

记录上次交换的位置

在实际的数据序列中,尾部的有序序列通常不只一个,因此我们可以记住最后一次交换发生的位置,下一次比较到这个位置即可。

let array = [5, 3, 8, 4, 6];
let lastExchangeIndex = 0;
let sortBorder = array.length - 1;
for(let i =
 0; i < array.length - 1; i++) {
  let isSorted = true;
  for(let j = 0; j < sortBorder; j++) {
    if(array[j] > array[j + 1]) {
      let temp = array[j];
      array[j] = array[j + 1];
      array[j + 1] = temp;
      isSorted = false;
      lastExchangeIndex = j;
    }
  }
  sortBorder = lastExchangeIndex;
  if(isSorted) {
    break;
  }
}
console.log(array);  // 输出: [3, 4, 5, 6, 8]

目录
相关文章
ArcEngine 创建工作空间工厂对象IWorkSpaceFactory的两种方式
ArcEngine 创建工作空间工厂对象IWorkSpaceFactory有两种方式: 第一种使用ae的工厂方法:  IWorkspaceFactory pWsFactory = new ShapefileWorkspaceFactoryClass();  IFeatureWorkspace  pWorkSpace = pWsFactory.
4193 0
|
9月前
|
消息中间件 缓存 PHP
PHP性能优化:从基础到进阶的实战指南####
本文旨在为开发者提供一份全面的PHP性能优化指南,涵盖从代码层面的基础优化到服务器配置的高级策略。通过具体实例分析,揭示如何有效减少页面加载时间、降低资源消耗,并提升用户体验。无论你是PHP新手还是资深开发者,都能在本文中找到实用的技巧和建议,助你打造更高效、更稳定的Web应用。 ####
|
11月前
|
缓存 算法 架构师
京东面试:如何设计600Wqps高并发ID?如何解决时钟回拨问题?
资深架构师尼恩在其读者交流群中分享了关于分布式ID系统的设计与实现,特别是针对高并发场景下的解决方案。他强调了分布式ID系统在高并发核心组件中的重要性,并详细介绍了百度的UidGenerator,这是一个基于Snowflake算法改进的Java实现,旨在解决分布式系统中的唯一ID生成问题。UidGenerator通过自定义workerId位数和初始化策略,支持虚拟化环境下的实例自动重启和漂移,其单机QPS可达600万。此外尼恩的技术分享不仅有助于提升面试表现,还能帮助开发者在实际项目中应对高并发挑战。
京东面试:如何设计600Wqps高并发ID?如何解决时钟回拨问题?
|
11月前
|
消息中间件 网络协议 C#
C#使用Socket实现分布式事件总线,不依赖第三方MQ
`CodeWF.EventBus.Socket` 是一个轻量级的、基于Socket的分布式事件总线系统,旨在简化分布式架构中的事件通信。它允许进程之间通过发布/订阅模式进行通信,无需依赖外部消息队列服务。
C#使用Socket实现分布式事件总线,不依赖第三方MQ
|
存储 机器学习/深度学习 SQL
【Prompt Engineering:自我反思(Reflexion)】
自我反思(Reflexion)是一种通过语言反馈强化基于语言的智能体的新范式,无需微调模型即可提升其在决策、推理和编程等任务中的表现。该框架包括参与者(生成动作)、评估者(评分)和自我反思(生成反馈)三个部分,利用大语言模型生成具体反馈,帮助智能体从错误中快速学习,显著提高了多种任务的性能。
1165 2
【Prompt Engineering:自我反思(Reflexion)】
|
存储 SQL 关系型数据库
深入MySQL锁机制:原理、死锁解决及Java防范技巧
深入MySQL锁机制:原理、死锁解决及Java防范技巧
|
机器学习/深度学习 算法 前端开发
集成学习的力量:Sklearn中的随机森林与梯度提升详解
【7月更文第23天】集成学习,作为机器学习中一种强大而灵活的技术,通过结合多个基础模型的预测来提高整体预测性能。在`scikit-learn`(简称sklearn)这一Python机器学习库中,随机森林(Random Forest)和梯度提升(Gradient Boosting)是两种非常流行的集成学习方法。本文将深入解析这两种方法的工作原理,并通过代码示例展示它们在sklearn中的应用。
566 10
|
机器学习/深度学习 存储 自然语言处理
利用Elasticsearch进行大规模文本分类与聚类
【8月更文第28天】文本数据在现代应用中占据着重要的位置,无论是社交媒体分析、客户反馈管理还是内容推荐系统。Elasticsearch 是一款强大的搜索引擎,非常适合用于处理大量的文本数据。本文将介绍如何利用 Elasticsearch 来实现大规模文本数据的分类与聚类分析,并提供一些具体的代码示例。
522 0
|
SQL Oracle 关系型数据库
技术心得:数据库之笛卡尔积
技术心得:数据库之笛卡尔积
386 0