点击上方“ 源码共读”,选择“置顶公众号”
作者:chenhongdong
https://juejin.cn/post/6844903593732997134;
源码共读整理发布,转载请联系作者获得授权
讲个故事
从身边的好朋友那里听说一个做程序的大哥,主要就是写算法的,然后从上家公司离职的时候,公司要求签订保密协议,并保证如果没有泄密的情况下,每月依然会发工资,持续两年
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 -> 2
tmp = arr[1] -> 5; next -> arr[2] -> 1
while循环条件 1 > 0 && (arr[1-1]-> arr[0] -> 2) > 5
不成立继续循环
next 2>0 && 5 > 1
next 1>0 && 3 > 1
arr[2] = arr[1] -> 5
arr[1] = arr[0] -> 3
index--; index -> 1 index -> 0
最后
arr[index] = tmp -> arr[1] = 5; next arr[1] = 1 arr[0] = 1
就实现了值的交换
次时arr为[1, 3, 5, 4, 2]
剩下的以此类推即可了
*/
还是老样子,依然展示一下插入排序的工作流程: