本人的话工作时间不是特别长,一直从事的就是前端行业,现在依然还是前端的小白,入行到现在时不时都能听到各种各样的人在那儿说什么“世界上只有两种程序员,一种懂算法,一种不懂”,感觉好像特别高大上牛逼的样子,各种大佬们更是一个比一个思路清晰,没啥说的,只能是膜拜再膜拜。
写这个系列的文章,是因为我也曾经迷茫,不知道算法对于前端来说重要么。也找过很多的东西,但是很多都不是那种能够针对新人,或者是没头没尾的一段,弄的很难受。而且很多人都说前端就使用好框架就行,这个不重要。对于这些我不想评价,我只想说,你有犹豫的时间为啥不去学一学试一试,你自己就会告诉你自己答案。
1、最开始的时候问过很多公司的老人,算法对于前端来说是不是必须要学的?
仅仅从我自己的角度和理解来说,如果你虽然是咸鱼但是也想着某天就算不能翻身但是至少能活动活动筋骨,那么算法你就必须学习,很现实多数公司或多或少都会问算法的问题,如果说你本身喜欢安逸,钱和工作对于你来说只是消遣或者是有点儿就行,那么你可以不学,因为我也看到过很多前端工作者对于算法知识很是粗浅或者不知道,但是依然过的很好,挣的也不少。
但是如果你有想要学习算法的心思,然后不知道该不该学习的话,我仅代表我自己,我会建议你学习,其他都不用说,首先面试高薪点儿的都会问算法,越高越深,没有一家公司会是傻子,其次你比别人的优势在哪儿呢?为什么公司要选择你?还是你就准备从年轻就开始养老?
2、如何学习算法
(1)必须亲自动手实践操作。我一直信奉的就是身为一个程序员,那么你就必须拿起你的电脑,打开你的编辑器,开始写程序编辑思考,只是单单的去看别人如何去做,走马观花可能会一直是两秒钟记忆。
(2)方法很重要。对于很多的前端来说都是其他行业转或者半路出家的,对于一些基础的东西其实欠缺,能够去弥补这些的就是看书,仅仅看书还是不够的,因为你看书有可能会看不懂,我自己推荐的是去看一些讲的好的视频,先看懂视频的东西,然后在书上找到对应的东西看一遍,然后在letcode上刷类似的题。
(3)勤动手多思考。在刷题的时候,不要觉得自己想法很差然后就直接去看别人的解题思路和答案,我感觉不太好,因为没有自己的思考的过程,就算是自己暴力法一直循环解答出来的,也需要有自己的思路和解答的过程,不要形成依赖的习惯。
(4)多写多记。多写这个一般都知道,就是经常刷题并且在自己的日常工作项目中有意的去使用和避免一些不好的东西,多记就是多看别人的东西,一知半解的话那就先记下来,印在脑子里,突然某个时刻你就会明白,原来这么简单。
首先你需要有javascript基础,如果基础很差,请转去红宝书去啃书,其次学习算法需要了解数据结构,接下来是我的常见算法的学习和总结和大家一起学习分享
时间复杂度和空间复杂度
算法的入门应该先学习如何去计算时间和空间复杂度,以及了解他们是干嘛的,顾名思义就是衡量一个算法的执行时间和所需空间的一个标准,从而来评判算法是否高效。具体的评判标准我就不详细解释啦,网上讲的很详细。
栈
栈是一种遵从后进先出原则的有序集合。新添加或待删除的元素都保存在栈的同一端,称作栈顶,另一段就叫栈底。在栈里,新元素都靠近栈顶,旧元素都接近栈底。其实很好理解,就像你往你的储物箱里面要放衣服,你最先放的在最下面,最后放的在最上面,你要想拿下面的衣服,你就必须把上面的衣服一层层拿开。
重点就是先进后出和有序集合。
javascrpt中没有栈,但是能用数组进行模拟和实现。
letcode的20题:给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。
示例 1: 输入: "()" 输出: true 示例 2: 输入: "()[]{}" 输出: true 示例 3: 输入: "(]" 输出: false 示例 4: 输入: "([)]" 输出: false 示例 5: 输入: "{[]}" 输出: true 。
var isValid = function(s) {
let stack = []
for(i = 0; i<s.length; i++) {
if(s.length % 2 === 1) {return false}
const c = s[i]
if(c === '(' || c === '{' || c === '[') {
stack.push(c)
} else {
const p = stack[stack.length - 1]
if(
(c === ')' && p === '(') ||
(c === '}' && p === '{') ||
(c === ']' && p === '[')
) {
stack.pop()
} else {
return false
}
}
}
return stack.length === 0
};
解题思路:
1、首先符号需要是偶数个,否则直接就返回false
2、从外到内有序的对应的关系,两两组成一对,从中间开始抽,抽走一对后,又是一对,循环下去
3、采用栈的思路:一直循环遍历,遇到左边的符号,就放入栈中,直到第一个右半部分的出现,对应的就是刚放入栈中的栈顶元素,如何能符号闭合那么就把栈顶元素抛出,然后下一个匹配新的栈顶,这样一一对应,最后应该是整个栈抛空就说明是true,没有抛空或者中间对应错误那么就是false。
对于栈来说需要清楚栈里面常用的一些东西:
push():入栈 pop():出栈 stack[stack.length-1]:栈顶元素 stack[0]:栈底元素
队列
队列是一种遵从先进先出原则的有序集合。可以和栈进行比较记忆,一个是后进先出,一个是先进先出,都是一种线性结构。这个场景就像是食堂排队打饭,来了新人后排在最后面,打上饭后第一个人先出去吃饭去啦。
letcode****933. 最近的请求次数:
写一个 RecentCounter 类来计算特定时间范围内最近的请求。请你实现 RecentCounter 类:
RecentCounter() 初始化计数器,请求数为 0 。
int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。 保证每次对 ping 的调用都使用比之前更大的 t 值。 ** **
输入: ["RecentCounter", "ping", "ping", "ping", "ping"] [[], [1], [100], [3001], [3002]]
输出: [null, 1, 2, 3, 3]
var RecentCounter = function() {
this.q = []
};
/** * @param {number} t * @return {number} */
RecentCounter.prototype.ping = function(t) {
this.q.push(t);
while(this.q[0] < t - 3000) {
this.q.shift()
}
return this.q.length
};
/** * Your RecentCounter object will be instantiated and called as such: * var obj = new RecentCounter() * var param_1 = obj.ping(t) */
解题思路:
1、这道题比较简单,其实是在判断当前出现的值与数组中原有的值是否有相距3000的,如果有那么就将前面的踢出去
2、可以模拟从第一个开始依次往进进,当进入的时候与最开始的进行比较,引文最开始的就是最先进去的,如果超过3000,就将第一个踢出去,然后依次进入,这样在数组中的永远都是符合的
3、其实就是队列的先进先出的思想
对于队列的常用的一些东西:
shift():出队列 push():进队列 a[0]:队列头 a[a.length - 1]:队列尾
前端没有栈和队列,但是都能通过数组进行模拟,这块儿需要熟悉数组的常用操作,并配合栈和队列的思想进行解答。
本文章主要用来本人的学习和记录,以及与大家分享学习,会持续更新,也请大佬能指点,能少走弯路,谢谢。
所有的题均在letcode上找到的,具体可以参照官网。