JavaScript排序大揭秘:手绘图解6大常见排序算法,一网打尽

2024-04-15 23:44

本文主要是介绍JavaScript排序大揭秘:手绘图解6大常见排序算法,一网打尽,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步!

 🍅 个人主页:南木元元

本文用图解总结梳理了6种常见的排序算法 ,如下👇:

  • 冒泡排序 
  • 选择排序
  • 插入排序
  • 快速排序
  • 归并排序
  • 堆排序

目录

 一.冒泡排序

思路及图解

代码实现

二.选择排序

思路及图解

代码实现

三.插入排序

思路及图解

代码实现

四.快速排序

思路及图解

代码实现

五.归并排序

思路及图解

代码实现

六.堆排序

思路及图解

代码实现

结语


一.冒泡排序

思路及图解

  • 基本思路

冒泡排序的基本思路:比较相邻的两个元素,将较大的元素交换到后面。通过多次遍历和两两比较,使得每一轮遍历都能确定一个当前未排序元素的最终位置,直至所有元素有序。

  • 冒泡排序图解

时间复杂度:O(n^2);空间复杂度:O(1)

代码实现

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

function bubbleSort(arr) {//外循环控制比较次数:n-1次for (let i = 0; i < arr.length - 1; i++) {// 内循环控制比较元素for (let j = 0; j < arr.length - 1 - i; j++) {// 如果前一个元素比后一个元素大,则交换它们的位置if (arr[j] > arr[j + 1]) {[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]; }}}return arr;
}// 测试
const arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(bubbleSort(arr));
// 输出:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
  • 优化版

如果在某一轮比较中没有发生元素交换,则说明数组已经有序,可以提前结束排序过程。

function bubbleSort(arr) {let flag; // 标记位for (let i = 0; i < arr.length; i++) {flag = false;for (let j = 0; j < arr.length - 1 - i; j++) {if (arr[j] > arr[j + 1]) {flag = true; // 发生了交换[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];}}if (!flag) break; // 如果一轮比较后没有发生交换,说明已经有序}return arr;
}

二.选择排序

思路及图解

  • 基本思路

选择排序的基本思路:每次从未排序的部分中选择最小(或最大)的元素,然后将其与未排序部分的第一个元素交换位置,直到整个数组排序完成。

  • 选择排序图解

时间复杂度:O(n^2);空间复杂度:O(1) 

代码实现

下面是用 JavaScript 实现的选择排序代码:

function selectSort(arr) {for (let i = 0; i < arr.length - 1; i++) {let minIndex = i; // 假设当前位置是最小值的位置// 在未排序部分找到最小元素的索引 for (let j = i + 1; j < arr.length; j++) {if (arr[j] < arr[minIndex]) {minIndex = j;}}// 如果当前循环的索引不是最小数的索引,交换它们if(i !== minIndex) {[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];}}return arr;
}// 测试
const arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(selectSort(arr));
// 输出:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

三.插入排序

思路及图解

  • 基本思路

基本思想:将待排序的数组分成已排序区间和未排序区间,初始时已排序区间只有一个元素,然后逐步将未排序区间的元素插入到已排序区间的合适位置,直到整个数组排序完成。

整个过程就像打扑克牌,从牌堆顶取一张牌,找到合适的位置插入到已有牌的顺序中。

  • 插入排序图解 

时间复杂度:O(n^2);空间复杂度:O(1) 

代码实现

下面是用 JavaScript 实现的插入排序代码:

function insertionSort(arr) {for (let i = 1; i < arr.length; i++) {// 当前待插入的元素let current = arr[i]; let j = i - 1;// 将大于当前元素的元素都向后移动while (j >= 0 && arr[j] > current) {arr[j + 1] = arr[j];j--;}// 找到合适的位置插入当前元素arr[j + 1] = current; }return arr;
}// 测试
const arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(insertionSort(arr));
// 输出:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

四.快速排序

思路及图解

  • 基本思路

快排采用分治的思想,选择一个基准元素,将数组分割成两个子数组,小于基准元素的放在左边,大于基准元素的放在右边,然后对这两个子数组进行递归排序,直到左右两部分只剩下一个数。

  • 快速排序图解

时间复杂度:O(nlogn);空间复杂度:O(logn)

代码实现

下面是用 JavaScript 实现的快速排序代码:

function quickSort(arr) {//递归出口,如果数组长度小于等于1,直接返回数组,无需排序if (arr.length <= 1) {return arr; }// 选取数组的第一个元素作为基准元素const pivot = arr[0]; // 用于存放小于等于基准元素的部分const left = []; // 用于存放大于基准元素的部分const right = []; // 将小于等于基准元素的数放入 left 数组,大于基准元素的放入 right 数组for (let i = 1; i < arr.length; i++) {if (arr[i] <= pivot) {left.push(arr[i]); } else {right.push(arr[i]);  }}// 递归地对左右两个部分进行排序return [...quickSort(left), pivot, ...quickSort(right)];
}// 测试
const arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(quickSort(arr));
// 输出:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

五.归并排序

思路及图解

  • 基本思路

归并排序是一种经典的分治算法,核心思想是将待排序数组分成若干个子序列,分别进行排序,然后将已排序的子序列合并成更大的有序序列,直到整个数组排序完成。

  • 归并排序图解

 时间复杂度:O(nlogn);空间复杂度:O(n)

代码实现

下面是用 JavaScript 实现的归并排序代码:

function mergeSort(arr) {// 如果数组长度小于 2,不需要排序,直接返回if (arr.length < 2) {return arr;}let mid = Math.floor(len / 2);// 将数组从中间位置分为左右两个子数组let l = arr.slice(0, mid);let r = arr.slice(mid);// 递归调用mergeSort对左右两个子数组进行排序,并将结果合并return merge(mergeSort(l), mergeSort(r));
}function merge(left, right) {let arr = [];// 排序并合并两个数组while (left.length && right.length) {if (left[0] <= right[0]) {arr.push(left.shift());} else {arr.push(right.shift());}}// 合并左右子数组中剩余的元素return arr.concat(left, right); 
}// 测试
const arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(mergeSort(arr));
// 输出:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

六.堆排序

思路及图解

  • 基本思路

堆排序的基本思路:先将待排序的数组构建成一个最大堆(或最小堆),然后依次将堆顶元素与堆尾元素交换并重新调整堆,重复这个过程直到整个数组排序完成。

1.构建最大堆。将待排序的数组视为完全二叉树,从最后一个非叶子节点开始,依次向前调整节点,使得每个节点都满足父节点的值大于或等于子节点的值。

2.交换并调整堆。将堆顶元素(即当前最大的元素)与堆尾元素交换,然后重新调整剩余的元素,使得剩余元素重新构成一个最大堆。重复这个过程,直到所有元素都被排序完成。

  • 堆排序图解

 时间复杂度:O(nlogn);空间复杂度:O(1)

代码实现

下面是用 JavaScript 实现的堆排序代码:

// 调整堆,使得剩余元素重新构成最大堆
function heapify(arr, n, i) {let l = i * 2 + 1;let r = i * 2 + 2;let max = i;// 找出左右子节点和当前节点中的最大值if (l < n && arr[l] > arr[max]) {max = l;}if (r < n && arr[r] > arr[max]) {max = r;}// 如果最大值不是当前节点,交换节点并继续调整堆if (max !== i) {[arr[max], arr[i]] = [arr[i], arr[max]];heapify(arr, n, max);}
}
// 堆排序
function heapSort(arr) {let len = arr.length;// 建立大根堆,从最后一个非叶子节点开始let last = Math.floor((len - 2) / 2);for (let i = last; i >= 0; i--) {heapify(arr, len, i);}// 每次把堆顶的数与最后一个数交换,并重新调整堆for (let i = 0; i < len; i++) {[arr[0], arr[len - 1 - i]] = [arr[len - 1 - i], arr[0]];heapify(arr, len - 1 - i, 0);}return arr;
}// 测试
const arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(heapSort(arr));
// 输出:[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

结语

🔥如果此文对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下博主~ 

这篇关于JavaScript排序大揭秘:手绘图解6大常见排序算法,一网打尽的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/907251

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory