把这些根底算法题把握好,根底不牢地动山摇,前面中级题很多都是在这些根底题的根底上的。
二叉树(DFS)
二叉树前中后遍历套路详解
前序遍历题目如下:
root 节点是 A 节点(下图的 A 节点),而后让你依照下图数字的程序顺次打印出节点。
咱们能够看到这其中的法则,就是深度优先遍历,先遍历左子树,再遍历右子树,这里咱们不必递归,因为一些大厂严格要求二叉树遍历不必递归,递归太简略了。
重点思路就是:深度优先遍历,先遍历左子树,再遍历右子树,
所以,咱们须要一套如何遍历一颗二叉树,并且是先左子树,再右子树的通用模板,如下
var Traversal = function(root) {const stack = [];
while (root || stack.length){while(root){stack.push(root);
root = root.left;
}
root = stack.pop();
root = root.right;
}
return res;
};
咱们联合图片发现这个遍历产生的整体压栈的程序是
- A、B、D 入栈,
- D 出栈
- B 出栈
- E 入栈
- E 出栈
- A 出栈
- C 入栈
- C 出栈
- F 入栈
- F 出栈
咱们把下面入栈的元素按顺序排列一下就是,A、B、D、E、C、F,而这就是前序遍历的程序!解答结束!
是不是很有意思,上面的中序遍历,咱们看看出栈程序是不是中序遍历的要求:D、B、E、A、C、F(这就是中序遍历的要求,好了,两个题解决)
放具体前序遍历代码:
var preorderTraversal = function(root) {
// 初始化数据
const res =[];
const stack = [];
while (root || stack.length){while(root){res.push(root.val);
stack.push(root);
root = root.left;
}
root = stack.pop();
root = root.right;
}
return res;
};
中序遍历是一个意思,在前序遍历的根底上革新一下
var preorderTraversal = function(root) {
// 初始化数据
const res =[];
const stack = [];
while (root || stack.length){while(root){stack.push(root);
root = root.left;
}
root = stack.pop();
res.push(root.val);
root = root.right;
}
return res;
};
后序遍历有点不太一样,然而套路是一样的,咱们须要先遍历右子树,再遍历左子树,反着来,就能够了,代码如下:
var postorderTraversal = function(root) {
// 初始化数据
const res =[];
const stack = [];
while (root || stack.length){while(root){stack.push(root);
res.unshift(root.val);
root = root.right;
}
root = stack.pop();
root = root.left;
}
return res;
};
对称二叉树
这个题简而言之就是判断一个二叉树是对称的,比如说:
二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
然而上面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
思路:
递归解决:
- 判断两个指针以后节点值是否相等
- 判断 A 的右子树与 B 的左子树是否对称
-
判断 A 的左子树与 B 的右子树是否对称
function isSame(leftNode, rightNode){if(leftNode === null && rightNode === null) return true; if(leftNode === null || rightNode === null) return false; return leftNode.val === rightNode.val && isSame(leftNode.left, rightNode.right) && isSame(leftNode.right, rightNode.left) } var isSymmetric = function(root) {if(!root) return root; return isSame(root.left, root.right); };
二叉树的最大深度
这个题在面试滴滴的时候遇到过,次要是把握二叉树遍历的套路
- 只有遍历到这个节点既没有左子树,又没有右子树的时候
- 阐明就到底部了,这个时候如果之前记录了深度,就能够比拟是否比之前记录的深度大,大就更新深度
-
而后以此类推,始终比拟到深度最大的
var maxDepth = function(root) {if(!root) return root; let ret = 1; function dfs(root, depth){if(!root.left && !root.right) ret = Math.max(ret, depth); if(root.left) dfs(root.left, depth+1); if(root.right) dfs(root.right, depth+1); } dfs(root, ret); return ret };
将有序数组转化为二叉搜寻树
咱们先看题:
给你一个整数数组 nums,其中元素曾经按 升序 排列,请你将其转换为一棵 高度均衡 二叉搜寻树。
高度均衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1」的二叉树。
示例 1:输出:nums = [-10,-3,0,5,9] 输入:[0,-3,9,-10,null,5] 解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:
示例 2:
输出:nums = [1,3] 输入:[3,1] 解释:[1,3] 和 [3,1] 都是高度均衡二叉搜寻树。提醒:
-
<= nums.length <= 104
-104 <= nums[i] <= 104
nums 按 严格递增 顺序排列 - 构建一颗树包含:构建 root、构建 root.left 和 root.right
- 题目要求 ” 高度均衡 ” — 构建 root 时候,抉择数组的两头元素作为 root 节点值,即可保持平衡。
-
递归函数能够传递数组,也能够传递指针,抉择传递指针的时候:l r 别离代表参加构建 BST 的数组的首尾索引。
var sortedArrayToBST = function(nums) {return toBST(nums, 0, nums.length - 1) }; const toBST = function(nums, l, r){if( l > r){return null;} const mid = l + r >> 1; const root = new TreeNode(nums[mid]); root.left = toBST(nums, l, mid - 1); root.right = toBST(nums, mid + 1, r); return root; }
栈
栈是一种先进先出的数据结构,所以波及到你须要先进先出这个想法后,就能够应用栈。
其次我感觉栈跟递归很类似,递归是不是先压栈,而后先进来的先进来,就跟函数调用栈一样。
无效的括号
这是一道很典型的用栈解决的问题,给定一个只包含 ‘(‘,’)’,'{‘,’}’,'[‘,’]’ 的字符串 s,判断字符串是否无效。
无效字符串需满足:
左括号必须用雷同类型的右括号闭合。左括号必须以正确的程序闭合。
示例 1:输出:s = "()"
输入:true
示例 2:输出:s = "()[]{}"
输入:true
示例 3:输出:s = "(]"
输入:false
示例 4:输出:s = "([)]"
输入:false
思路:这道题有一法则:
1. 右括号后面,必须是绝对应的左括号,能力对消!
2. 右括号后面,不是对应的左括号,那么该字符串,肯定不是无效的括号!
也就是说左括号咱们间接放入栈中即可,发现是右括号就要比照是否跟栈顶元素相匹配,不匹配就返回 false
var isValid = function(s) {const map = { '{': '}', '(': ')', '[': ']' };
const stack = [];
for(let i of s){if(map[i]){stack.push(i);
} else {if(map[stack[stack.length - 1]] === i){stack.pop()
}else{return false;}
}
}
return stack.length === 0;
};
### 最小栈
先看题目:
设计一个反对 push,pop,top 操作,并能在常数工夫内检索到最小元素的栈。
- push(x) —— 将元素 x 推入栈中。
- pop() —— 删除栈顶的元素。
- top() —— 获取栈顶元素。
-
getMin() —— 检索栈中的最小元素。
示例: MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> 返回 -3. minStack.pop(); minStack.top(); --> 返回 0. minStack.getMin(); --> 返回 -2. 提醒:pop、top 和 getMin 操作总是在 非空栈 上调用。
咱们先不写 getMin 办法,满足其余办法实现就非常简单,咱们来看一下:
var MinStack = function() {this.stack = []; }; MinStack.prototype.push = function(x) {this.stack.push(x); }; MinStack.prototype.pop = function() {this.stack.pop(); }; MinStack.prototype.top = function() {return this.stack[this.stack.length - 1]; };
如何保障每次取最小呢,咱们举一个例子:
如上图,咱们须要一个辅助栈来记录最小值, - 开始咱们向 stack push -2
- 此时辅助栈 minStack,因为此时 stack 最小的是 -2,也 push -2
stack push 0 - 此时辅助站 minStack 会用 0 跟 - 2 比照,- 2 更小,minstack 会 push -2
- stack push -3
- 此时辅助站 minStack 会用 -3 跟 - 2 比照,- 3 更小,minstack 会 push -3
所以咱们取最小的时候,总能在 minStack 中取到最小值,所以解法就进去了:
var MinStack = function() {this.stack = [];
// 辅助栈
this.minStack = [];};
MinStack.prototype.push = function(x) {this.stack.push(x);
// 如果是第一次或者以后 x 比最小栈里的最小值还小才 push x
if(this.minStack.length === 0 || x < this.minStack[this.minStack.length - 1]){this.minStack.push(x)
} else {this.minStack.push( this.minStack[this.minStack.length - 1])
}
};
MinStack.prototype.pop = function() {this.stack.pop();
this.minStack.pop();};
MinStack.prototype.top = function() {return this.stack[this.stack.length - 1];
};
MinStack.prototype.getMin = function() {return this.minStack[this.stack.length - 1];
};
动静布局
动静布局,肯定要晓得动静转移方程,有了这个,就相当于解题的钥匙,咱们从题目中领会一下
最大子序和
题目如下:
给定一个整数数组 nums,找到一个具备最大和的间断子数组(子数组起码蕴含一个元素),返回其最大和。
示例 1:输出:nums = [-2,1,-3,4,-1,2,1,-5,4]
输入:6
解释:间断子数组 [4,-1,2,1] 的和最大,为 6。示例 2:输出:nums = [1]
输入:1
示例 3:输出:nums = [0]
输入:0
思路:
- 这道题能够用动静布局来解决,要害是找动静转移方程
- 咱们动静转移方程中,dp 示意每一个 nums 下标的最大自序和,所以 dp[i]的意思为:包含下标 i 之前的最大间断子序列和为 dp[i]。
确定本义方程的公示:
dp[i]只有两个方向能够推出来:
- 1、如果 dp[i – 1] < 0,也就是以后遍历到 nums 的 i,之前的最大子序和是正数,那么咱们就没必要持续加它了,因为 dp[i] = dp[i – 1] + nums[i] 会比 nums[i]更小,所以此时还不如 dp[i] = nums[i],就是目前遍历到 i 的最大子序和呢
- 2、同理 dp[i – 1] > 0,阐明 nums[i]值得去加 dp[i – 1],此时回避 nums[i]更大
这样代码就进去了,其实更多的就是求 dp,遍历 nums 每一个下标都会产生最大子序和,咱们记录下来即可
var maxSubArray = function(nums) {let res = nums[0];
const dp = [nums[0]];
for(let i=1;i < nums.length;i++){if(dp[i-1]>0){dp[i]=nums[i]+dp[i-1]
}else{dp[i]=nums[i]
}
res=Math.max(dp[i],res)
}
return res
};
爬楼梯
先看题目:
假如你正在爬楼梯。须要 n 阶你能力达到楼顶。
每次你能够爬 1 或 2 个台阶。你有多少种不同的办法能够爬到楼顶呢?
留神:给定 n 是一个正整数。
示例 1:输出:2
输入:2
解释:有两种办法能够爬到楼顶。1. 1 阶 + 1 阶
2. 2 阶
示例 2:输出:3
输入:3
解释:有三种办法能够爬到楼顶。1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
波及到动静布局,肯定要晓得动静转移方程,有了这个,就相当于解题的钥匙,
这道题咱们假如 dp[10]示意爬到是你爬到 10 阶就达到楼顶的办法数,
那么,dp[10] 是不是就是你爬到 8 阶,而后再走 2 步就到了,还有你走到 9 阶,再走 1 步就到了,
所以 dp[10] 是不是等于 dp[9]+dp[8]
延长一下 dp[n] 是不是等于 dp[n – 1] + dp[n – 2]
代码如下:
var climbStairs = function(n) {const dp = {};
dp[1] = 1;
dp[2] = 2;
for(let i = 3; i <= n; i++){dp[i] = dp[i-1] + dp[i-2]
}
return dp[n]
};
数学问题
加一
题目如下:
给定一个由 整数 组成的 非空 数组所示意的非负整数,在该数的根底上加一。
最高位数字寄存在数组的首位,数组中每个元素只存储单个数字。
你能够假如除了整数 0 之外,这个整数不会以零结尾。
示例 1:输出:digits = [1,2,3]
输入:[1,2,4]
解释:输出数组示意数字 123。示例 2:输出:digits = [4,3,2,1]
输入:[4,3,2,2]
解释:输出数组示意数字 4321。示例 3:输出:digits = [0]
输入:[1]
这个题的要害有两点:
- 须要有一个进位的变量 carry 记录到底进位是几
-
还须要一个每次迭代都重置和的变量 sum 来帮咱们算是否进位,以及进位后的数字
记住这个题,这是两数字相加的套路,这次是 +1,其实就是两数相加的题(腾讯面试遇到过两数相加)var plusOne = function(digits) { let carry = 1; // 进位(因为咱们确定 +1,初始化进位就是 1)for(let i = digits.length - 1; i >= 0; i--){let sum = 0; // 这个变量是用来每次循环计算进位和 digits[i]的值的 sum = digits[i] + carry; digits[i] = sum % 10; // 模运算取个位数 carry = (sum / 10) | 0; // 除以 10 是取百位数,并且|0 示意舍弃小数位 } if(digits[0] === 0) digits.unshift(carry); return digits };
x 的平方根
题目如下:实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
因为返回类型是整数,后果只保留整数的局部,小数局部将被舍去。
示例 1:输出: 4 输入: 2
示例 2:
输出: 8 输入: 2 阐明: 8 的平方根是 2.82842..., 因为返回类型是整数,小数局部将被舍去。
这道题是典型的二分法解题,所以咱们须要相熟二分法的通用模板,咱们出一个题:
在 [1, 2, 3, 4, 5, 6] 中找到 4,若存在则返回下标,不存在返回 -1const arr = [1, 2, 3, 4, 5, 6]; function getIndex1(arr, key) { let low = 0; const high = arr.length - 1; while (low <= high) {const mid = Math.floor((low + high) / 2); if (key === arr[mid]) {return mid;} if (key > arr[mid]) {low = mid + 1;} else {height = mid - 1;} } return -1; } console.log(getIndex1(arr, 5)); // 4
所以这道题的意思就是,咱们找一个数平方跟 x 最相近的数,二分法的用法中也有找相近数的性能
所以代码如下:
var mySqrt = function(x) {let [l , r] = [0, x];
let ans = -1;
while(l <= r) {const mid = (l + r) >> 1;
if(mid * mid > x){r = mid - 1} else if(mid * mid < x){
ans = mid; // 避免越界
l = mid + 1;
} else {
ans = mid;
return ans;
}
}
return ans;
};
};
Excel 表序列号
这个题比拟重要,也比拟根底,简而言之就是进制转换,必须牢牢把握
题目如下:
给你一个整数 columnNumber,返回它在 Excel 表中绝对应的列名称。
例如:
A -> 1
B -> 2
C -> 3
...
Z -> 26
AA -> 27
AB -> 28
...
示例 1:输出:columnNumber = 1
输入:"A"
示例 2:输出:columnNumber = 28
输入:"AB"
示例 3:输出:columnNumber = 701
输入:"ZY"
示例 4:输出:columnNumber = 2147483647
输入:"FXSHRXW"
说白了,这就是一道 26 进制的问题,以前咱们晓得 10 进制转 2 进制就是不停的除 2,把余数加起来,26 进制也是一样,不停的除 26
思路:
- 初始化后果 ans = 0,遍历时将每个字母与 A 做减法,因为 A 示意 1,所以减法后须要每个数加 1,计算其代表的数值 num = 字母 –‘A’+ 1
- 因为有 26 个字母,所以相当于 26 进制,每 26 个数则向后退一位
- 所以每遍历一位则 ans = ans * 26 + num
-
以 ZY 为例,Z 的值为 26,Y 的值为 25,则后果为 26 * 26 + 25=701
var titleToNumber = function(columnTitle) { let ans = 0; for(let i = 0; i < columnTitle.length; i++){ans = ans * 26 + (columnTitle[i].charCodeAt() - 'A'.charCodeAt() + 1) } return ans; };
阶乘中的零
题目:
给定一个整数 n,返回 n! 后果尾数中零的数量。示例 1: 输出: 3 输入: 0 解释: 3! = 6, 尾数中没有零。示例 2: 输出: 5 输入: 1 解释: 5! = 120, 尾数中有 1 个零.
这道题很简略,有多少个 5 就有多少个 0,为什么这么说呢,咱们剖析一下题目
比如说 5!,
- 也就是 5 4 3 2 1 = 120,咱们发现只有 1 个 0,怎么产生的呢,次要造成者就是 2 * 5 结构了一个 0
- 再看看 10!
10! = 10 9 8 7 6 5 4 3 2 1 其中,除了 10 = 2 5 和自身有一对 2 * 5,所以有两个 0,这样这道题的法则就进去了,咱们再精进一步
如上图,每四个数字都会呈现一个或者多个 2 的因子,然而只有每 5 个数字能力找到一个或多个 5 的因子。所以总体上看来,2 的因子是远远多于 5 的因子的,所以咱们只须要找 5 的倍数就能够了。
咱们再进一步,依照下面的说法,咱们须要计算比方 10 的阶乘有多少个 0,要把 10 的阶乘算进去,其实咱们只须要算 10 有几个 5 就好了,为什么呢
咱们再进一步,依照下面的说法,咱们须要计算比方 10 的阶乘有多少个 0,要把 10 的阶乘算进去,其实咱们只须要算 10 有几个 5 就好了,为什么呢
咱们发现只有 5 的倍数的阶乘,才会产生 5, 所以咱们须要看看阶层数有多少个 5,代码如下:
var trailingZeroes = function (n) {
let r = 0;
while (n > 1) {n = Math.floor(n / 5);
r += n;
}
return r;
};
颠倒二进制位
题目如下:
颠倒给定的 32 位无符号整数的二进制位。
示例 1:
输出: 00000010100101000001111010011100
输入: 00111001011110000010100101000000
解释: 输出的二进制串 00000010100101000001111010011100 示意无符号整数 43261596,因而返回 964176192,其二进制示意模式为 00111001011110000010100101000000。
示例 2:
输出:11111111111111111111111111111101
输入:10111111111111111111111111111111
解释:输出的二进制串 11111111111111111111111111111101 示意无符号整数 4294967293,因而返回 3221225471 其二进制示意模式为 10111111111111111111111111111111。
这类题,就是翻转字符串,咱们能够把其转为字符串,再转成数组,再 reverse 一下,这里咱们选用数学的形式去解答,不必这种转字符串的形式。
解答这道题之前,咱们须要理解的前置常识:
1. 与估算 &
1 & 1 // 1 的 2 进制最初一位是 1,失去 1
2 & 0 // 2 的 2 进制最初一位是 0,失去 0
3 & 1 // 3 的 2 进制最初一位是 1,失去 1
4 & 0 // 4 的 2 进制最初一位是 0,失去 0
所以咱们晓得了怎么取 10 进制最初 1 位的 2 进制是几。
2.JavaScript 应用 32 位按位运算数(意思是咱们的按位运算都会转成 32 位,你的数字不能超过 32 位,会出问题)
- JavaScript 将数字存储为 64 位浮点数,但所有按位运算都以 32 位二进制数执行。
- 在执行位运算之前,JavaScript 将数字转换为 32 位有符号整数。
- 执行按位操作后,后果将转换回 64 位 JavaScript 数。
3.'<< 1′ 运算
这个运算实际上就是把 10 进制乘以 2,这个乘 2 在 2 进制上体现出左边填了一个 0,咱们距举例来说,
- 2 的 2 进制是 10,2 << 1 失去 4,4 的 2 进制是 100,所以比 10 多了个 0
- 3 的 2 进制是 11,3 << 1 失去 6。6 的 2 进制是 110,所以比 11 多了个 0
以上就是法则
思路:循环取最初一位拼接起来即可
var reverseBits = function (n) {
let result = 0
for (let i = 0; i < 32; i++) {result = (result << 1) + (n & 1)
n = n >> 1
}
// 为什么要 >>> 0 呢,一位 javascript 没有无符号整数,全是有符号的
// 不 >>>0 的话,得进去的值是正数,然而无符号整数是没有符号的
// javascript 有符号转化为无符号的办法就是 >>>0
return result >>> 0
}
失落的数字
给定一个蕴含 [0, n] 中 n 个数的数组 nums,找出 [0, n] 这个范畴内没有呈现在数组中的那个数。
进阶:
你是否实现线性工夫复杂度、仅应用额定常数空间的算法解决此问题?
示例 1:输出:nums = [3,0,1]
输入:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范畴 [0,3] 内。2 是失落的数字,因为它没有呈现在 nums 中。示例 2:输出:nums = [0,1]
输入:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范畴 [0,2] 内。2 是失落的数字,因为它没有呈现在 nums 中。
这题很简略,就是用 0 - n 的总和减去数组总和
-
0 – n 的总和用等差数列:(首数 + 尾数)* 项数 / 2 来求
var missingNumber = function(nums) { const len = nums.length let sum = ((1 + len) * len) / 2 for (let i = 0; i < len; i++) {sum -= nums[i] } return sum }
的幂
题目如下:
给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true;否则,返回 false。
整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3 的 x 次方示例 1:输出:n = 27 输入:true 示例 2:输出:n = 0 输入:false 示例 3:输出:n = 9 输入:true
思路
- 咱们拿 27 来说:27 = 3 3 3,所以 27 是 3 的幂次方
- 咱们拿 29 来说:29 = 3 3 3 点几
也就是说,如果是 3 的幂次方,始终除以 3,除到最初就等于 1 比方 27/3/3/ 3 等于 1 如果不是 3 的幂次方,除到最初就是 3 点几 /3 等于 1 点几
代码就进去了判断是不是等于 1 即可
var isPowerOfThree = function(n) {while(n >= 3){n /= 3;}
return n === 1;
};
412. Fizz Buzz
这个题没啥好说的,就依照题目说的写代码就行,先看题目:
写一个程序,输入从 1 到 n 数字的字符串示意。
1. 如果 n 是 3 的倍数,输入“Fizz”;
2. 如果 n 是 5 的倍数,输入“Buzz”;
3. 如果 n 同时是 3 和 5 的倍数,输入“FizzBuzz”。
示例:n = 15,
返回:
[
"1",
"2",
"Fizz",
"4",
"Buzz",
"Fizz",
"7",
"8",
"Fizz",
"Buzz",
"11",
"Fizz",
"13",
"14",
"FizzBuzz"
]
var fizzBuzz = function (n) {const list = [];
for (let i = 1; i <= n; i++) {
const is3Times = i % 3 === 0; // 是否是 3 的倍数
const is5Times = i % 5 === 0; // 是否是 5 的倍数
const is15Times = is3Times && is5Times; // 是否是 15 的倍数
if (is15Times) {list.push('FizzBuzz');
continue;
}
if (is3Times) {list.push('Fizz');
continue;
}
if (is5Times) {list.push('Buzz');
continue;
}
list.push(`${i}`);
}
return list;
};
环问题
这类问题的特点就是,你要循环寻找,到底怎么循环寻找,看题便知。
环形链表
题目如下:
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,能够通过间断跟踪 next 指针再次达到,则链表中存在环。为了示意给定链表中的环,咱们应用整数 pos 来示意链表尾连贯到链表中的地位(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。留神:pos 不作为参数进行传递,仅仅是为了标识链表的理论状况。
如果链表中存在环,则返回 true。否则,返回 false。
输出:head = [3,2,0,-4], pos = 1
输入:true
解释:链表中有一个环,其尾部连贯到第二个节点。
示例 2:
输出:head = [1,2], pos = 0
输入:true
解释:链表中有一个环,其尾部连贯到第一个节点。
咱们采纳标记法:
给遍历过的节点打记号,如果遍历过程中遇到有记号的阐明已环
var hasCycle = function(head) {
let traversingNode = head;
while(traversingNode){if(traversingNode.isVistitd) return true
traversingNode.isVistitd = true
traversingNode = traversingNode.next
}
return false;
};
202. 高兴数
题目如下:编写一个算法来判断一个数 n 是不是高兴数。
「高兴数」定义为:
- 对于一个正整数,每一次将该数替换为它每个地位上的数字的平方和。
- 而后反复这个过程直到这个数变为 1,也可能是 有限循环 但始终变不到 1。
- 如果 能够变为 1,那么这个数就是高兴数。
- 如果 n 是高兴数就返回 true;不是,则返回 false。
高兴数怎么剖析呢?
咱们来看一个表,就会得出结论,一个数依照高兴数定义的形式别离每个数字平方,会有两种状况
1. 失去 1
2. 有限循环
有限循环参照下图 - 有人会说会不会始终变大,答案是不会:咱们看上面列表,
能够看到如果你是 13 位,你的下一次高兴数算法会变为 4 位 1053, - 如果你是 9999, 4 位,下一个高兴数是 324
所以代码只有判断这两种就行了,代码如下:
// 封装获取高兴数的办法
function getNext(n){n = String(n);
let sum = 0;
for(let num of n){sum = sum + Math.pow(+num, 2);
}
return sum;
}
var isHappy = function(n) {
// 哈希表来看是否循环
const map = {};
while(n !== 1){map[n] = true;
n = getNext(n)
if(map[n]) return false
}
return true
};
我还整顿了一份前端面试题,包含人事、我的项目、小程序、HTML5/CSS3、JS、HTTP、ES6、Vue、REACT 等面试题,咱们先一起看看题目。
人事 + 我的项目
这都是主观问题,列举进去的是能够参考下,人事面试也不能漫不经心,有的公司人事是有一票否决权
小程序
- 数据申请怎么封装
- 参数传值的办法
- 进步小程序的利用速度的办法
- 小程序的长处
- 小程序的毛病
- 简述小程序原理
- 怎么解决异步申请问题
- 小程序和 Vue 写法的区别
- 小程序的双向绑定和 vue 哪里不一样
- 几种跳转,小程序内的页面跳转
HTML5\CSS3
- Doctype 作用? 严格模式与混淆模式 - 如何触发这两种模式,辨别它们有何意义?
- 行内元素有哪些?块级元素有哪些?空 (void) 元素有那些?
- CSS 的盒子模型有几种?各有什么特点?
- link 和 @import 的区别是?
- CSS 选择符有哪些?哪些属性能够继承?优先级算法如何计算?CSS3 新增伪类有那些?
- 如何居中 div, 如何居中一个浮动元素?
- 浏览器的内核别离是什么? 常常遇到的浏览器的兼容性有哪些?起因,解决办法是什么,罕用 hack 的技
- css 属性那些有继承性?哪些没有?
- 如果盒子都为浮动,父类会没有高度,如何解决
- isibility 和 display 的暗藏有什么区别?
JS
- 原型 / 原型链 / 构造函数 / 实例 / 继承
- 如何实现 new 运算符
- 有几种形式能够实现继承
- arguments
- 数据类型判断
- 作用域链、闭包、作用域
- Ajax 的原生写法
- 对象深拷贝、浅拷贝
- 图片懒加载、预加载
- 实现页面加载进度条
React
- 辨别 Real DOM 和 Virtual DOM
- React 有什么特点?
- 列出 React 的一些次要长处。
- React 有哪些限度?
- 什么是 JSX?
- 你理解 Virtual DOM 吗?解释一下它的工作原理。
- 为什么浏览器无奈读取 JSX?
- 与 ES5 相比,React 的 ES6 语法有何不同?
- 怎么解释 React 中 render() 的目标。
完整版的 2021 前端面试题精编 PDF 文档点击这里获取,还有面试题没有列举进去,小伙伴们到时能够好好看面试题。