讲个故事

从身边的好朋友那里听说一个做程序的大哥,主要就是写算法的,然后从上家公司离职的时候,公司要求签订保密协议,并保证如果没有泄密的情况下,每月依然会发工资,持续两年

What?对,你没听错,就是这样,喵。为什么呢?因为他会算法啊,算法是根基,此处省略N多字,我也不会告诉你他年薪接近百万了。看到了吧,这就是我们要不断学习算法的小目标,先挣它一个亿!

算法的世界

Hi,欢迎来到算法的世界,算法无处不在,其实就在我们身边

  • 《社交网络》中扎克伯格在创立Facebook之初靠着数学公式,写出了对比两个女孩照片谁更漂亮的算法
  • 阿尔法狗打败了围棋世界冠军
  • 进入X宝,会推荐你喜欢的商品
  • 点个外卖,下次会推荐你常点的店家
  • 打个农药,会尽量匹配和你选择英雄相同的玩家
  • N多栗子,全举出来可能掘金都容不下这篇文章了

以上这些身边的例子其实都是依靠着算法来实现的

算法之于程序,是质变的过程(其实就是1分钟变1秒钟的差距)

程序 = 算法 + 数据结构

现在火热的AI技术,也并不是突然出现的,也是靠着之前积累的各种算法组合试验去运行的。

算法如同机器人的大脑,告诉机器人如何行动,思考等一系列行为。不然你以为,它们天生丽质难自弃嘛,那是不可能的

说了那么多,那我们为什么要学算法呢?为了挣它一个亿?可以是,也可以不是

没有一身好内功,招式再多都是空

对于我们大自然的搬运工(程序员)来说,算法可以让你的程序更加高效

好了,难道这还不够你臭屁的嘛!

下面开始今天的主题,先从简单的排序和搜索算法说起,因为很多中、高级面试里都会问到。连这些都答不上来还怎么当资深,做专家,挣它一个亿!

排序和搜索算法

排序算法

常用的排序算法有很多,如冒泡排序、选择排序、插入排序、归并排序、快速排序以及堆排序,下面我们就针对这几个排序算法来看看,了解一下思想

冒泡排序

// 创建一个数组列表来处理排序和搜索的数据结构
function ArrayList() {let arr = [];   this.insert = function(item) {  // 将数据插入到数组中arr.push(item);};this.show = function() {    // 展示数组的结构return arr.join(' < ');};// 冒泡排序     眼熟?没错,面试里常考,而且它是排序算法中最简单的this.bubbleSort = function () {let len = arr.length;for (let i = 0; i < len; i++) { // 这里之所以再-i,是因为外层循环已经跑完一轮// 内循环就没有必要再比较一回了for (let j = 0; j < len - 1 - i; j++) {  if (arr[j] > arr[j + 1]) {  // 当前项和下一项做比较,如果大于的话就按照下面交换位置// ES6利用解构赋值的方式轻松实现j和j+1值的交换[arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];}}}};
}// 测试用例,此测试用例在之后的算法中皆可使用
let arr = [5, 4, 3, 2, 1];
let createList = function(arr) {let list = new ArrayList();for (let i = 0; i < arr.length; i++) {list.insert(arr[i]);}return list;
};
let item = createList(arr);
console.log(item.string()); // 排序前 5 < 4 < 3 < 2 < 1
item.bubbleSort();
console.log(item.string()); // 排序后 1 < 2 < 3 < 4 < 5
复制代码

从实现方式来看冒泡排序是最简单的一种,而且从运行时间的角度来看,冒泡排序是最差的一个,因为做了两层循环导致,复杂度为O(n^2)了

下面再给大家看一下冒泡排序的工作流程:

既然是最差的排序,那么我们就继续往下看,看看挖掘技术哪家强?一个一个的来比较一番

选择排序

所谓选择排序算法,其实就是拿数组中的一个值做标杆,和其他的值做比较,如果比标杆还小的就替换标杆的值,以此类推就可以了。不多啰嗦直接上代码

function ArrayList() {// 省略...// 选择排序this.selectSort = function () {let len = arr.length,min;for (let i = 0; i < len - 1; i++) {min = i;    // 我们取第一个值当标杆for (let j = i; j < len; j++) { // 内部循环从i开始到数组结束if (arr[min] > arr[j]) {    // 比标杆的值还小,就替换新值min = j;}}if (i !== min) {    // 上面经过一顿比较替换,如果标杆的值和之前取的第一个值不同了,就交换位置[arr[i], arr[min]] = [arr[min], arr[i]];}}};
}
复制代码

选择排序我们看到其实也是嵌套了两层循环的操作,这样的话,其实复杂度和冒泡排序是一样的了,我们先来看下选择排序的工作流程吧

相比两层循环的冒泡和选择排序,接下来介绍的插入排序性能上会好了很多

插入排序

插入排序算法是假定数组的第一项已经是排好序的了,直接用它和第二项去比较,如果比第二项大就将索引和值都进行交换,以此来推来实现排序

function ArrayList() {// 省略...// 插入排序this.insertSort = function () {let len = arr.length,index, tmp;// 这里默认第一项已经排序了,直接从第二项开始for (let i = 1; i < len; i++) {index = i;        // 用来记录一个索引 tmp = arr[i];   // 储存一个临时变量,方便之后插入位置// 索引必须是大于0,并且数组前一项的值如果大于临时变量的值// 就将前一项的值赋给当期项,并且index--while (index > 0 && arr[index - 1] > tmp) { arr[index] = arr[index - 1];   index--;  }arr[index] = tmp; // 最后在一顿替换后插入到了正确的位置上}};
}// 测试用例写在冒泡排序那里
let arr = [3, 5, 1, 4, 2];
// 按照这个数组,先来分析前两项,剩下的大家自己来推到即可了
/*Tips: -> 符号为最后对应的值进入for循环i从1开始那此时index = i -> 1;  next -> 2tmp = arr[1] -> 5;  next -> arr[2] -> 1while循环条件   1 > 0 && (arr[1-1]-> arr[0] -> 2) > 5不成立继续循环next  2>0 && 5 > 1next  1>0 && 3 > 1arr[2] = arr[1] -> 5arr[1] = arr[0] -> 3index--;  index -> 1  index -> 0最后arr[index] = tmp -> arr[1] = 5;   next  arr[1] = 1  arr[0] = 1就实现了值的交换次时arr为[1, 3, 5, 4, 2]剩下的以此类推即可了
*/
复制代码

还是老样子,依然展示一下插入排序的工作流程:

插入排序显然已经比冒泡和选择排序算法在性能上好很多了,不过有一点小遗憾啊,就是排序的数组不要太大,太大了它就受不鸟啦!

有时候想想同样是排序算法,怎么这三种算法是这也不行,那也不行。试问有没有靠谱的呢,大家时间宝贵不能耽误的。答案当然是肯定的,有,来看

归并排序

这里首先缅怀一位伟大的人物,冯·诺伊曼,‘计算机之父’。之所以要缅怀是因为这个靠谱的排序算法就是由这个开了挂的人提出的。恩,此时此景,我想吟诗一首,不简单啊,不简单!

function ArrayList() {// 省略...// 归并排序this.mergeSort = function () {arr = mergeRecurve(arr);    // 由于需要不停的拆分直到数组只有一项,所以使用递归来做};// 递归function mergeRecurve(arr) {let len = arr.length;// 递归的停止条件,如果数组只有一项,就直接返回了// 这也是我们递归的目的,直到数组只有一项if (len === 1) return arr;      let mid = Math.floor(len / 2);let left = arr.slice(0, mid);let right = arr.slice(mid, len);    // 到这里把数组一分为二// 为了不断对原数组拆分,对left和right数组继续递归,并作为参数传给merge函数// merge函数负责合并和排序小数组为大数组return merge(mergeRecurve(left), mergeRecurve(right));  }function merge(left, right) {   // 接收两个数组,最后合并到一起返回一个大数组let res = [],lLen = left.length,rLen = right.length,l = 0,r = 0;while (l < lLen && r < rLen) {// 如果left数组的项比right数组的项小的话,就将left这里小的项添加到大数组里if (left[l] < right[r]) {   res.push(left[l++]);    // 并继续下一项比较} else {res.push(right[r++]);   // 否则将right里小的项先添加到大数组中}}// 将left和right数组中剩余的项也都添加到大数组中while (l < lLen) {res.push(left[l++]);}while (r < rLen) {res.push(right[r++]);}return res;  // 返回排好序的大数组}
}
复制代码

归并排序采用的是一种分治算法,不明白分治算法?很简单,你就当做是二分法。之后会讲,就是将一个大问题拆成小问题来解决,等每个小问题都解决了,大问题也就搞定了

实现思想:将原数组切分成小数组,直到每个数组只有一项,然后再将小数组合并成大数组,最后得到一个排好序的大数组。可以看下图的执行过程加深理解

如果觉得归并排序算法不好理解,我们准备了简单的测试,挑个重点来分析一下

let arr = [7, 9, 8];    // 就选3个值的数组来简单分析吧
/*arr = mergeRecurve([7, 9, 8])二分拆分后,left = [7], right = [9, 8]merge(mergeRecurve([7]), mergeRecurve([9, 8]))[7]这个已经拆分完毕,先放着不管由于数组并不是只有一项,所以[9, 8]继续递归拆分后得到,left = [9], right = [8]merge([9], [8])res = [], l = 0, r = 0while (0<1 && 0<1) {if (9 < 8) {不走这里 } else {res.push(8)}}while (0<1) {res.push(9);}while(1<1) {不走这里}return [8, 9]再回到上一层处理merge([7], [8, 9]),推到过程同上
*/
复制代码

少年,看到这里,我不得不说你果然骨骼惊奇,接下来放个大招,来介绍一下最常用的排序算法-快速排序,它的性能要比其他复杂度为O(nlog^n)的排序算法都好

快速排序

快速排序算法,需要在数组中找到一个中间项作为标记,然后创建双指针,左边第一项,右边最后一项。移动左指针直到比标记的值大,再移动右指针直到比标记小,然后交换位置,重复此过程,实现一个划分的操作

function ArrayList() {// 省略...// 快速排序this.quickSort = function () {quick(arr, 0, arr.length - 1);  // 递归}function quick(arr, left, right) {let index;if (arr.length > 1) {index = partition(arr, left, right);  // 划分if (left < index - 1) {quick(arr, left, index - 1)}if (index < right) {quick(arr, index, right);}}}// 划分函数function partition(arr, left, right) {let point = arr[Math.floor((left+right)/2)],i = left,j = right;  // 双指针while (i <= j) {while (arr[i] < point) {i++;}while (arr[j] > point) {j--;}if (i<=j) {[arr[i], arr[j]] = [arr[j], arr[i]];  // 交换位置i++;j--;}}return i;}
}
复制代码

直接看代码可能有点懵,but不用怕,看见蟑螂也不怕不怕了。我们来具体问题具体分析一下就好了,直接看个简单的栗子

/*
let arr = [7, 9, 8];
quickSort// 递归quick([7, 9, 8], 0, 2)  // 传递开头和末尾,两个位置index   // 用来分离小值数组和大值数组,一左一右length > 1对子数组进行划分,第一次传入的是原数组[7, 9, 8]partition(arr, left, right) 用来划分位置point = [7, 9, 8][Math.floor(0+2)/2] -> 9i -> 0, j -> 2    // i 和 j 其实就是双指针,一个从左开始,一个从右开始while (i <= j) { 0 <= 2// 第一次 7 < 9// 下一次 arr[1] -> 9  9 < 9不成立while (arr[i] < 9) { i++;    i -> 1}// arr[2] -> 8 > 9不成立while (arr[j] > 9) {j--;}// 此时i->1, j->2if (i <= j) {   1 <= 2// 交换位置[arr[i], arr[j]] = [arr[2], arr[1]] -> [8, 9]i++;    i -> 2j--;}// 划分重排后的数组为[7, 8, 9]return i; 返回划分的坐标i -> 2}此时计算出了index为2if (left < index - 1) { 0 < 2-1// 继续递归quick([7, 8, 9], 0, 1);// 这步递归完后,index->1,right->-1}if (index < right) {quick([7,8,9], 2, 3);}按照以上步骤继续推到即可理解了,加油加油
*/
复制代码

此时此刻,是不是该来一杯香浓的咖啡来提提神了呢?没有吗?没关系,那就抬起自己的右手慢慢向上抬起,高过头顶,然后......使劲拍一下脑袋,这下大家满足了吧,神清气爽,畅快淋漓了!哈哈,不扯淡了,不扯淡了

上面我们就聊了一下排序算法的几种不同方式,可以说是实现了初步胜利吧,算法的路还很长,路漫漫其修远兮,吾将上下而求索。大家一起共勉,下面再来聊聊搜索算法

请看大屏幕

搜索算法

搜索算法这里着重讲的就是面试中常被问到的二分查找法

我们大家都有用过array.indexOf方法,它的用法和字符串里的indexOf是一样的,都是返回所需元素的位置,没找到就是-1

说白了,这其实也算做一种搜索,只不过它的实现其实很low,不够高效,来看代码

let arr = [1, 9, 8, 2, 3];
arr.indexOf(8);     // 2
arr.indexOf(0);     // -1function ArrayList() {// 省略...// indexOfthis.indexOf = function(item) {for (let i = 0; i < arr.length; i++) {if (item === arr[i]) {return i;}}return -1;};
}
复制代码

这样的代码实际上是按照顺序一个一个去做对比的,效率上有些低下。还是来看看我们的重点保护对象,二分查找法吧

二分查找法

看过早期李咏的《幸运52》的观众应该记得一个环节,节目会给嘉宾上一款商品,让他在一定时间内来猜价格,根据报价,李咏会说高了或者低了,直至回答正确拿到商品为止

没看过的朋友们,没关系,就是个猜价格的游戏,会说高了还是低了or对了

怀念当时还是个小盆友,无忧无虑的小盆友,哈哈

看代码

function ArrayList() {// 省略...// 二分查找法this.binarySearch = function (item) {this.quickSort();   // 先来排个序,方便查找let low = 0,high = arr.length - 1,mid, ele;while (low <= high) {mid = Math.floor((low + high) / 2); // 一刀切,取中间ele = arr[mid];     // 用中间值去和搜索值item比较if (ele < item) {   // 如果中间值是4,要查的是5// 那就将左侧low的指针升级变成从中间加1项那里查找// 就是在高价位那里查low = mid + 1; } else if (ele > item) { // 中间值还是4,要查的变成3了// 比中间值还小,那就在靠左侧低价位里面了// 将右侧指针变成中间值-1的位置,控制了最大范围high = mid - 1;  } else {    // 中间值是4,你查的正好也是4,那么,bingo,返回return mid;}}return -1;  // 查不到的当然就-1了,这还用说嘛};
}
复制代码

二分查找法也聊完了,节目不早,时间刚好了。让我们一起来梳理一下上面的内容吧

梳理一下

排序算法

  • 冒泡排序 -> 嵌套两层循环,和下面的选择排序一样性能不好
  • 选择排序 -> 楼上的没脑子
  • 插入排序 -> 相比楼上两位,它的性能会好很多,不过只适用于数据量少的数组
  • 归并排序 -> 很有来头,被认可的排序算法
  • 快速排序 -> 很常用的排序算法,性能也很好

搜索算法

  • 二分查找法 -> 猜猜猜,押大押小,小了就在小的一侧找,大了就去大的一侧找

好了,以上内容在面试中、高级前端的时候也会被问到,在这里也希望看过此文章的同学,面试顺利,找到满意的工作

算法很有用,提高工作效率,重构的时候也很有效果

所以之后我也会继续研究学习算法的内容,好继续和大家分享,感谢各位了,骚年们!

少年,我看你骨骼精奇,见与你有缘,这套算法赠你相关推荐

  1. 少年,我看你骨骼精奇,送你这几本书看看吧

    今年已经过半了,作为要改变世界的 IT 人,不读书怎么行呢? 清华大学出版社,送书活动来了.为了给大家补补血,以便更好的改变世界,本号联合了 9 名大佬一起给大家送书啦!!! 书怎么送? 1. 关注下 ...

  2. 少年你骨骼精奇?AI现在可以帮你看一看了

    本文来自AI新媒体量子位(QbitAI) "少年,我看你骨骼精奇,是个练武奇才,这里有一本<如来神掌>--" 现在,AI就能判断你是否骨骼精奇了. 而且这还不是概念或实 ...

  3. 百度大脑人脸识别深度验证与思考(七)之骨骼精奇

    环境 win7 32位 VisualStudio2017 python3.6.3 opencv3.3.1 pyQt5.9 baidu-AIP 1.6.9.0 特别声明 所有图片均来自网络公开,无意冒犯 ...

  4. 少年,我看你骨骼惊奇,必是练武奇才,将来维护宇宙正义

    亲,我看你骨骼惊奇,必是练武奇才, 将来维护宇宙正义与和平的重任就交给你了! 我这有失传已久的武林秘籍,看在咱们有缘, 只卖你五百块钱一本,买两本的话给你打99折, 一次性购买五本以上的话包邮喔亲! ...

  5. 识骨寻踪:少年,我看你骨骼清奇,不如来看看这本书。

    前两年大热的网剧<法医秦明>让法医这个角色出现在大众的视野里,同时也揭开了他们工作背后神秘的面纱.不少人对他们的工作内容极其感兴趣. 电视上的法医通过分析尸体为警察提供破案依据,但你知道最 ...

  6. 看你骨骼清奇,老夫决定赐你这份“2018软件测试发光秘籍”

    当你因为读书而思考得越多的时候,你就会越清楚地看到你知道得还很少. --伏尔泰 都听说过一万小时定律吧?要成为某个领域的专家,需要10000个小时.但在持续下功夫之前,得先找准方向,确定好目标.如果你 ...

  7. 少年, 我看你骨骼惊奇, 送你一套精选 Java 面试题

    临近过年, 疫情再次来袭, 大家注意保护好自己和家人, 而各大公司也在年前进行 2020 年最后的人才储备工作, 这里帅帅送大家一套精选的 Java 考试题目希望对大家有所帮助. 如果学不动了, 就想 ...

  8. 深入浅出CChart 每日一课——第二课 上一课代码分析,骨骼精奇之一点即通 .

    在第一课中,笨笨编写了少量的代码.对CChart不熟悉的人对部分代码可能不能理解,下面给同学们解释一下. 第一段代码如下. #include "Chart.h" #ifdef _D ...

  9. 年轻人,看你骨骼惊奇,我这有一份来自阿里的Android开发学习指南,不仅能让你月入5w,度过中年危机都不是问题!

    摘要 很简单,我这有一份来自阿里程序员佛系月薪5w指南,看你骨骼惊奇,印堂光亮,一看就是将要大富大贵.走向人生巅峰之人,就不收你钱了,一个点赞就送给你怎么样? 缘起 为什么写下这篇文章? 疫情自爆发以 ...

  10. kengoro机器人哪儿有卖_日本东京大学创造出“骨格精奇”的机器人Kengoro 会流汗能做掌上压...

    Kengoro(图)的骨架有如海绵(右下),能让水流过散热,同时能支撑身体. 水分会流至表层散热蒸发掉. Kengoro身上有多个电机及活动关节. (神秘的地球uux.cn报道)人类可以透过流汗散热, ...

最新文章

  1. 字节跳动算法工程师总结:java接口实验报告总结
  2. python使用for循环打印99乘法表-python3:使用for循环打印九九乘法表
  3. php.ini文件可以复制吗,php安装完成以后要复制php.ini文件
  4. 【小米校招笔试】假如已知有n个人和m对好友关系(存于数字r)。如果两个人是直接或间接的好友(好友的好友的好友...),则认为他们属于同一个朋友圈,请写程序求出这n个人里一共有多少个朋友圈。
  5. editor修改样式 vue_手摸手Electron + Vue实战教程(三)
  6. Eclipse快捷键大全,导包快捷键:ctrl+Shift+/【转】
  7. 解决CentOS 中部署JasperReport时出错的问题。错误:net.sf.jasperreports.engine.util.JRFontNotFoundException: Font '宋体
  8. 软件测试工作都干些什么?
  9. C#之Xml去掉前面的空格
  10. Git的使用--如何将本地项目上传到Github(两种简单、方便的方法)
  11. c语言中保存的文件时怎么换行,关于文件操作,碰到空格就换行
  12. vmware服务器虚拟机重新安装系统教程,在VMware虚拟机装系统教程_vmware装系统_U盘工具_装系统教程_课课家...
  13. 《非暴力沟通》-马歇尔·卢森堡
  14. kk每日一句:第一句
  15. 不值一提?开源CRM是否值得应用
  16. day 83 Vue学习三之vue组件
  17. Java字符和数字列对齐_字符串(包含中英文、数字、符号)的对齐
  18. Github-谷歌插件gitzip(加速器-不用再忍受几十kb/s的煎熬了)
  19. Android微信九宫格图片展示控件
  20. 给定平面上任意三个点的坐标(x1​,y1​)、(x2​,y2​)、(x3​,y3​),检验它们能否构成三角形。

热门文章

  1. Java开发需要学什么!中原银行java面试题目
  2. 大学生了没-20120709迅雷下载
  3. 720p、1080i、1080p,三种格式(片源),谁更清晰?
  4. 全国计算机技术与软件专业技术资格(水平)考试2019年上半年考试公告(湖北省)
  5. The kernel appears to have died. It will restart automatically.
  6. mysql查询财两个人信息_MYSQL学习与数据库综合实验—数据查询实验
  7. 小程序根据国外经纬度显示地图
  8. css3效果隔两秒旋转然后停两秒再继续旋转,无限循环
  9. IceWarp邮件监控功能
  10. 【C#进阶3-7】C# Socket通讯