数据算法_JS数据结构与算法_排序和搜索算法
写在前面
这是《学习JavaScript数据结构与算法》的最后一篇博客,也是在面试中常常会被问到的一部分内容:排序和搜索。在这篇博客之前,我每每看到排序头就是大的,心里想着类似“冒泡排序,两层遍历啪啪啪“就完事了,然后再也无心去深入研究排序相关的问题了。如果你也有类似的经历,希望下面的内容对你有一定帮助
一、准备
在进入正题之前,先准备几个基础的函数
(1)交换数组两个元素
function swap(arr, sourceIndex, targetIndex) { let temp = arr[sourceIndex]; arr[sourceIndex] = arr[targetIndex]; arr[targetIndex] = temp; }
(2)快速生成0~N的数组 可点击查看更多生成方法
function createArr(length) { return Array.from({length}, (_, i) => i); }
(3)洗牌函数
洗牌函数可快速打乱数组,常见的用法如切换音乐播放顺序
function shuffle(arr) { for (let i = 0; i < arr.length; i += 1) { const rand = Math.floor(Math.random() * (i + 1)); if (rand !== i) { swap(arr, i, rand); } } return arr; }
二、排序
常见排序算法可以分为两大类:
- 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序
- 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序
![](/assets/blank.gif)
在本篇博客中,仅对比较类排序的几种排序方式进行学习介绍
2.1 冒泡排序
冒泡排序是所有排序算法中最简单的,通常也是我们学习排序的入门方法。但是,从运行时间的角度来看,冒泡排序是最差的一种排序方式。
核心:比较任何两个相邻的项,如果第一个比第二个大,则交换它们。元素项向上移动至正确的顺序,就好像气泡升至表面一样,冒泡排序因而得名
![](/assets/blank.gif)
注意:第一层遍历找出剩余元素的最大值,至指定位置【依次冒泡出最大值】
代码:
function bubbleSort(arr) { const len = arr.length; for (let i = 0; i < len; i += 1) { for (let j = 0; j < len - 1 - i; j += 1) { if (arr[j] > arr[j + 1]) { // 比较相邻元素 swap(arr, j, j + 1); } } } return arr; }
2.2 选择排序
选择排序是一种原址比较排序算法。
核心:首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕
![](/assets/blank.gif)
注意:第一层遍历找出剩余元素最小值的索引,然后交换当前位置和最小值索引值【依次找到最小值】
代码:
function selectionSort(arr) { const len = arr.length; let minIndex; for (let i = 0; i < len - 1; i += 1) { minIndex = i; for (let j = i + 1; j < len; j += 1) { if (arr[minIndex] > arr[j]) { minIndex = j; // 寻找最小值对应的索引 } } if (minIndex === i) continue; swap(arr, minIndex, i); } return arr; }
2.3 插入排序
插入排序的比较顺序不同于冒泡排序和选择排序,插入排序的比较顺序是当前项向前比较。
核心:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
![](/assets/blank.gif)
注意:从第二项开始,依次向前比较,保证当前项以前的序列是顺序排列
代码:
function insertionSort(arr) { const len = arr.length; let current, pointer; for (let i = 1; i < len; i += 1) { current = arr[i]; pointer = i; while(pointer >= 0 && current < arr[pointer - 1]) { // 每次向前比较 arr[pointer] = arr[pointer - 1]; // 前一项大于指针项,则向前移动一项 pointer -= 1; } arr[pointer] = current; // 指针项还原成当前项 } return arr; }
2.4 归并排序
归并排序和快速排序相较于上面三种排序算法在实际中更具有可行性(在第四小节我们会通过实践复杂度来比较这几种排序算法)
JavaScript的Array类定义了一个sort函数(Array.prototype.sort)用以排序JavaScript数组。ECMAScript没有定义用哪个排序算法,所以浏览器厂商可以自行去实现算法。例如,Mozilla Firefox使用归并排序作为Array.prototype.sort的实现,而Chrome使用了一个快速排序的变体
归并排序是一种分治算法。其思想是将原始数组切分成较小的数组,直到每个小数组只有一 个位置,接着将小数组归并成较大的数组,直到最后只有一个排序完毕的大数组。因此需要用到递归
核心:归并排序,拆分成左右两块数组,分别排序后合并
![](/assets/blank.gif)
注意:递归中最小的左右数组比较为单个元素的数组,因此在较上层多个元素对比时,左右两个数组一定是顺序的
代码:
function mergeSort(arr) { const len = arr.length; if (len < 2) return arr; // 递归的终止条件 const middle = Math.floor(len / 2); // 拆分左右数组 const left = arr.slice(0, middle); const right = arr.slice(middle); return merge(mergeSort(left), mergeSort(right)); } function merge(left, right) { // 将左右两侧比较后进行合并 const ret = []; while (left.length && right.length) { if (left[0] > right[0]) { ret.push(right.shift()); } else { ret.push(left.shift()); } } while (left.length) { ret.push(left.shift()); } while (right.length) { ret.push(right.shift()); } return ret; }
2.5 快速排序
快速排序也许是最常用的排序算法了。它的复杂度为O(nlogn),且它的性能通常比其他的复 杂度为O(nlogn)的排序算法要好。和归并排序一样,快速排序也使用分治的方法,将原始数组分为较小的数组
核心:分治算法,以参考值为界限,将比它小的和大的值拆开
![](/assets/blank.gif)
注意:每一次遍历筛选出比基准点小的值
代码:
function quickSort(arr, left = 0, right = arr.length - 1) { // left和right默认为数组首尾 if (left < right) { let partitionpartitionIndex = partition(arr, left, right); quickSort(arr, left, partitionIndex - 1); quickSort(arr, partitionIndex + 1, right); } return arr; } function partition(arr, left, right) { let pivot = left; let index = left + 1; // 满足比较条件的依次放在分割点后 for (let i = index; i <= right; i += 1) { if (arr[i] < arr[pivot]) { swap(arr, i, index); index += 1; } } swap(arr, index - 1, pivot); // 交换顺序时,以最后一位替换分隔项 return index - 1; }
三、搜索算法
3.1 顺序搜索
顺序或线性搜索是最基本的搜索算法。它的机制是,将每一个数据结构中的元素和我们要找的元素做比较。顺序搜索是最低效的一种搜索算法。
function findItem(item, arr) { for (let i = 0; i < arr.length; i += 1) { if (item === arr[i]) { return i; } } return -1; }
3.2 二分搜索
二分搜索要求被搜索的数据结构已排序。以下是该算法遵循的步骤:
- 选择数组的中间值
- 如果选中值是待搜索值,那么算法执行完毕
- 如果待搜索值比选中值要小,则返回步骤1在选中值左边的子数组中寻找
- 如果待搜索值比选中值要大,则返回步骤1在选中值右边的子数组中寻找
function binarySearch(item, arr) { arr = quickSort(arr); // 排序 let low = 0; let high = arr.length - 1; let mid; while (low <= high) { min = Math.floor((low + high) / 2); if (arr[mid] < item) { low = mid + 1; } else if (arr[mid] > item) { high = mid - 1; } else { return mid; } } return -1; }
四、算法复杂度
4.1 理解大O表示法
大O表示法用于描述算法的性能和复杂程度。分析算法时,时常遇到一下几类函数
![](/assets/blank.gif)
(1)O(1)
function increment(num){ return ++num; }
执行时间和参数无关。因此说,上述函数的复杂度是O(1)(常数)
(2)O(n)
以顺序搜索函数为例,查找元素需要遍历整个数组,直到找到该元素停止。函数执行的总开销取决于数组元素的个数(数组大小),而且也和搜索的值有关。但是函数复杂度取决于最坏的情况:如果数组大小是10,开销就是10;如果数组大小是1000,开销就是1000。这种函数的时间复杂度是O(n),n是(输入)数组的大小
(3)O(n2)
以冒泡排序为例,在未优化的情况下,每次排序均需进行n*n次执行。时间复杂度为O(n2)
时间复杂度O(n)的代码只有一层循环,而O(n2)的代码有双层嵌套循环。如 果算法有三层遍历数组的嵌套循环,它的时间复杂度很可能就是O(n3)
4.2 时间复杂度比较
(1)常用数据结构时间复杂度
![](/assets/blank.gif)
(2)排序算法时间复杂度
![](/assets/blank.gif)
天下数据IDC与全球近120多个国家顶级机房直接合作,提供包括香港、美国、韩国、日本、台湾、新加坡、荷兰、法国、英国、德国、埃及、南非、巴西、印度、越南等国家和地区的服务器、云服务器的租用服务.
数据算法_JS数据结构与算法_排序和搜索算法相关推荐
- js计算字典的个数_JS数据结构与算法_集合字典
写在前面 说明:JS数据结构与算法 系列文章的代码和示例均可在此找到 一.集合Set 1.1 集合数据结构 集合set是一种包含不同元素的数据结构.集合中的元素成为成员.集合的两个最重要特性是:集合中 ...
- 程序员内功修炼之学好算法和数据结构(一)排序基础、选择排序、插入排序、希尔排序...
一.排序基础(重要) 1.1 为什么要学习O(n^2)的排序算法? 编码简单,易于实现,是一些简单情景的首选. 在一些特殊情况下,简单的排序算法更有效. 简单的排序算法思想衍生出复杂的排序算法,在这个 ...
- python数据结构与算法13_python 数据结构与算法 (13)
python 数据结构与算法 (13) 选择排序 (Selection sort) 是? 种简单直观的排序算法. 它的? 作原理如 下.? 先在未排序序列中找到最?(?)元素, 存放到排序序列的起始位 ...
- 什么是算法?数据结构与算法概念
算法的概念 算法是计算机处理信息的本质,因为计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务.一般地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写入输出设备或 ...
- 【图解数据结构与算法】数据结构与算法知识点整理 Data Structures and Algorithms
程序=数据结构+算法 数据结构是可以存储和组织数据的命名位置. 算法是用于解决特定问题的一组步骤. 数据结构是指:一种数据组织.管理和存储的格式,它可以帮助我们实现对数据高效的访问和修改. 数据结构 ...
- python算法与数据结构-插入排序算法(34)
阅读目录 一.插入排序的介绍 二.插入排序的原理 三.插入排序的图解 四.插入排序的python代码实现 五.插入排序的C语言代码实现 六.插入排序的时间复杂度 七.插入排序的稳定性 一.插入排序的介 ...
- 算法与数据结构(part1)--算法简介及大O表示法
学习笔记,仅供参考 文章目录 算法与数据结构--基于python 数据结构和算法简介 算法引入 例题A 算法的概念 例题A的优化 算法效率的衡量 时间复杂度与大O记法 例题A的时间复杂度 如何理解大O ...
- 【蛮力算法】数据结构与算法
蛮力算法也称为穷举法或暴力法,它是算法设计中最常见的方法之一.蛮力算法的基本思路是对问题的所有可能状态一一测试,直到找到解或将全部可能状态都测试为止. 蛮力算法的概述 蛮力法是一种简单.直接地解决问题 ...
- 【算法与数据结构】之选择排序
1.直接选择排序 ①定义: 选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序 ...
- js 二叉树图形_js数据结构和算法(三)二叉树
二叉树的概念 二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两棵互不相交的.分别称为根结点的左子树和右子树的二叉树组成. 二叉 ...
最新文章
- 一起谈.NET技术,asp.net控件开发基础(20)
- JSBinding+SharpKit / 更新的原理
- 电路板上的插头怎么拔下来_空调维修排查电路板内外原因
- 骁龙855加持!一加5G原型机将亮相MWC2019:价格却不太友好
- R语言在C#使用DCom中遇到的若干问题
- c mysql linux,linux上C语言连接mysql
- Django SimpleCMDB API
- 对比较器的使用方法的研究
- Cadence PSpice 模型3:从官网或者技术支持得到的PSpice模型与Capture库关联方法图文教程
- Maven基础篇之Maven的实战入门
- 【深度学习+组合优化】深度学习和强化学习在组合优化方面有哪些应用?
- 广东汕尾电信稳定dns服务器,广东电信的DNS是多少?
- 我的前端“先行”之路
- Didn't find class ...l on path: DexPathList
- java中finish什么意思,finish是什么意思(你知道Finish 和 Complete 的区别吗?)
- 移动端h5 下拉框 demo (原生的也太丑了把)
- mysql中ddl和ddm_DDL与DML问题
- 使用Scratch2和ROS进行机器人图形化编程学习
- 最新“3D版”DALL·E爆火,超快速度生成3D点云模型,OpenAI向谷歌新领域发起挑战丨开源...
- windows下编译chromium浏览器的方法
热门文章
- Github大盘点!2021年最惊艳的38篇AI论文
- Light Field 光场以及Matlab光场工具包(LightField ToolBox)的使用说明
- Unity 阴影的制作方式
- [BZOJ1085][SCOI2005]骑士精神
- springMVC_08文件上传
- 使用 Apache 来限制访问 Confluence 6 的管理员界面
- python 面向对象五 获取对象信息 type isinstance getattr setattr hasattr
- [转载]taking photos with live image preview
- EXT2.0 多选下拉框
- 明天开始放假了[2.5-2.13],春节期间计划