不会算法的前端不是好前端
用js刷leetcode,坚持每日一题
题目:8. String to Integer (atoi) 字符串转换整数
解法1: 用现成的parseInt()呀,来对比一下需求
题目要求为: | parseInt() |
---|---|
无视开头空格 | 符合 |
含有符号 | 符合 |
无视整数部分后的字符 | 符合 |
范围在32位内,超出返回 INT_MAX (2^31− 1) 或 INT_MIN (−2^31) | 不符合(需要手动判断一下) |
不能有效转换时返回 0 | 不能转化时返回NaN |
/**
* @param {string} str
* @return {number}
*/
var myAtoi = function(str) {
const number = parseInt(str, 10);//10进制
if(isNaN(number)) return 0;//不能有效的转换
return number < Math.pow(-2, 31) ? Math.pow(-2, 31):(number > Math.pow(2, 31) - 1?Math.pow(2, 31) - 1:number);
}
再来看一下官方的方法:确定有限状态机
题目:11. Container With Most Water 盛最多水的容器
贪心,左右指针
var maxArea = function(height) {
var l = 0, r = height.length-1; // 左右指针
var temp = 0, ans = 0;
while(l < r){
temp = (r-l)*(Math.min(height[l],height[r])); //计算此时容积
ans<temp && (ans = temp); //记录最优解
height[l] < height[r] ? l++ : r--; //哪边短哪边往里移动
}
return ans;
};
题目:22. Generate Parentheses 括号生成
一开始的写法比较蠢… 循环加(),set去重
() --> (()) --> (()()), ((())), (())()
() --> ()() --> (())(), ()()(), ()(())
/**
* @param {number} n
* @return {string[]}
*/
var fun1 = function(s){
let ans = new Set();
for(let temp of s){
for(var j=0;j<temp.length;j++){
ans.add(temp.slice(0,j-1)+"()"+temp.slice(j-1));
}
}
return ans;
}
var generateParenthesis = function(n) {
if(n==0) return [];
let s = new Set();s.add("()");
while(--n){s = fun1(s);}
return [...s];
};
回溯
var generateParenthesis = function(n) {
let res = [];
let dfs = (s, left, right)=>{
if(left ==n &&right==n) return res.push(s);
if(left < n) dfs(s+'(', left+1, right);
if(right < left) dfs(s+')', left, right+1);
}
dfs('', 0, 0);
return res;
};
题目:55. Jump Game 跳跃游戏
暴力 遍历一遍记可到达的最远位置
var canJump = function(nums) {
var maxIndex = nums[0];
for(var i=0;i<nums.length;i++){
if(i>maxIndex) return false;
maxIndex = Math.max(maxIndex,i+nums[i]);
}
return true;
};
题目 151. Reverse Words in a String 翻转字符串里的单词
var reverseWords = function(s) {
return s.trim().split(" ").filter(item => item).reverse().join(' ');
};
题目:199. Binary Tree Right Side View 二叉树的右视图
深搜 右枝先遍历(后放入栈),找到每个深度第一次遍历到的元素值
var rightSideView = function(root) {
if(root==null) return [];
var _s=[],ans=[],t;
root.h=0; _s.push(root);
while(_s.length>0){
t = _s.pop();
if(t.left!=null){
t.left.h = t.h+1; _s.push(t.left);
}
if(t.right!=null){
t.right.h = t.h+1; _s.push(t.right);
}
ans[t.h]==undefined &&(ans[t.h] = t.val) ;
}
return ans;
};
题目 289. Game of Life 生命游戏
方法1是复制一个原矩阵出来
/**
* @param {number[][]} board
* @return {void} Do not return anything, modify board in-place instead.
*/
var gameOfLife = function(board) {
if(!board.length) return null;
var m=board.length,n=board[0].length, t;
var _board = JSON.parse(JSON.stringify(board));
for(var i=0;i<m;i++){
for(var j=0;j<n;j++){
t=0;
if(i>0){
t += _board[i-1][j] + (j>0?_board[i-1][j-1]+_board[i][j-1]:0)+(j<n-1?_board[i-1][j+1] + _board[i][j+1]:0);
}else{
t += (j>0?_board[i][j-1]:0)+(j<n-1?_board[i][j+1]:0);
}
if(i<m-1){
t += _board[i+1][j] + (j>0?_board[i+1][j-1]:0)+(j<n-1?_board[i+1][j+1]:0);
}
if( _board[i][j]==1 && (t<2 || t>3))board[i][j]=0;
else if(_board[i][j]==0 && t==3)board[i][j]=1;
}
}
return board;
};
方法2 不使用额外内存,而是用状态值
2: 原本死了的活了;-1:原本活的死了
var gameOfLife = function (board) {
if(!board.length) return null;
let rows = board.length; let cols = board[0].length;
let neighbors = [0, -1, 1];
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
let liveBox = 0;
// 遍历每个格子的周围的八个格子
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (!i && !j) continue
let r = row + neighbors[i];
let c = col + neighbors[j];
if ((r >= 0 && r < rows) && (c >= 0 && c < cols) && Math.abs(board[r][c]) === 1) {
liveBox++;
}
}
}
if ((board[row][col] === 1) && (liveBox < 2 || liveBox > 3)) {
board[row][col] = -1;//-1:原本活的死了
}
if (board[row][col] === 0 && liveBox === 3) {
board[row][col] = 2;//2: 原本死了的活了
}
}
}
// 转换状态,2 -> 1,-1 -> 0
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
board[row][col] = board[row][col] > 0 ? 1 : 0;
}
}
};
01.07. Rotate Matrix LCCI 旋转矩阵
var rotate = function(matrix) {
var _matrix = JSON.parse(JSON.stringify(matrix)), len = matrix.length;
for(var i=0;i<len;i++){
for(var j=0;j<len;j++){
matrix[j][len-1-i]=_matrix[i][j];
}
}
};
let rotate = (matrix) =>{
for(let i = 0; i < matrix.length; i++){
for (let j = i; j < matrix[i].length; j++){
[matrix[i][j],matrix[j][i]] = [matrix[j][i],matrix[i][j]]
}
}
matrix.forEach(row=> row.reverse())
};
硬币,给定数量不限的硬币,币值为25分、10分、5分和1分,编写代码计算n分有几种表示法。(结果可能会很大,你需要将结果模上1000000007)
动态规划,
/**
* @param {number} n
* @return {number}
*/
let waysToChange = (n)=> {
let dp = new Array(n+1).fill(1)
let coins = [1,5,10,25]
for(let i=1; i<4; i++){
for(let j=1; j<=n; j++){
if(j-coins[i]>=0){
dp[j] = (dp[j]+dp[j-coins[i]]) % (1e9+7)
}
}
}
return dp[n];
};
46 Permutations 全排列
回溯法 深度优先搜索
var permute = function(nums) {
if(nums==0) return [];
var len=nums.length,used=[],path=[],ans=[];
var dfs = function(d){
if(d==len){ans.push([...path]);//拷贝}
for(var i=0;i<len;i++){
if(!used[nums[i]]){//该数字还没被加入path
path.push(nums[i]);
used[nums[i]] = true;
dfs(d+1);
path.pop();
used[nums[i]] = false;
}
}
}
dfs(0);
return ans;
};
题目 23. Merge k Sorted Lists 合并K个排序链表
先来看一下21. Merge Two Sorted Lists 合并两个排序链表
var mergeTwoLists = function(l1, l2) {
var ans= new ListNode(-1),cur=ans;
while(l1&&l2){
if(l1.val <= l2.val){
cur.next = l1; l1=l1.next;
}else{
cur.next = l2; l2=l2.next;
}
cur=cur.next;
}
cur.next = l1?l1:l2;
return ans.next;
};
这道题可以两个两个来合并
var mergeKLists = function(lists) {
let mergeTwoLists = (l1,l2) => {
let pre = new ListNode(-1)
let cur = pre;
while(l1 && l2){
if(l1.val <= l2.val){
cur.next = l1 l1 = l1.next
}else{
cur.next = l2 l2 = l2.next
}
cur = cur.next;
}
cur.next = l1 ? l1 : l2;
return pre.next;
}
if(lists.length == 0) return null;
let res = lists[0]
for(let i = 1;i < lists.length;i++){
if(lists[i]){
res = mergeTwoLists(res,lists[i]);
}
}
return res;
};
题目33. Search in Rotated Sorted Array 搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )
输入: nums = [4,5,6,7,0,1,2], target = 0 输出: 4
var search = function(nums, target) {
let l = 0, r = nums.length-1;
if(r==0) return nums[0]==target?0:-1;
m = parseInt((l+r)/2);
while(l<=r){
m = parseInt((l+r)/2);
if(nums[m]==target) return m;
if(nums[l]<=nums[m]){
if(target <= nums[m] && target >= nums[l]){
r = m-1;
}else{
l = m+1;
}
}else if(nums[m]<nums[r]){
if(target >= nums[m] && target <= nums[r]){
l = m+1;
}else{
r = m-1;
}
}
}
return -1;
};
面试题56 - I. 数组中数字出现的次数 LCOF
用额外O(n)空间
var singleNumbers = function(nums) {
let obj = {}
for(let i = 0; i < nums.length; i++){
obj[nums[i]] = obj[nums[i]] ? obj[nums[i]] + 1 : 1
}
let list = []
for(let prop in obj){
if(obj[prop] === 1){
list.push(prop)
}
}
return list
};
异或法
先看一下如果是要找到只出现一次的一个数字,只要全员异或就可以了
异或:对于两个操作数的每一位,相同结果为 0,不同结果为 1。成对出现的数字的所有位会两两抵消为 0,最终得到的结果就是那个出现了一次的数字。
var singleNumbers = function(nums) {
let res = 0;
for(let n in nums){
res ^= n;
}
return res;
}
如果我们可以把所有数字分成两组,使得:
那么对两个组分别进行异或操作,即可得到答案的两个数字
记这两个只出现了一次的数字为 a 和 b,那么所有数字异或的结果就等于 a 和 b异或的结果x
xi==1表示ai!=bi
var singleNumbers = function(nums) {
var ret = 0;
for(let n of nums){ret ^= n;}
let i = 1;
while((i & ret)==0){i <<= 1;} //找到第一个为1的位
let a = 0, b = 0;
for(let n of nums){
if(i & n) a ^= n;
else b ^= n;
}
return [a,b];
};
题目 542. 01 Matrix 01 矩阵
bfs 从所有0往四个方向遍历,
var updateMatrix = function(matrix) {
if(matrix.length==0) return ;
var st = [],cur;
var f = [[0,-1],[-1,0],[0,1],[1,0]];
for(var i=0;i<matrix.length;i++){
for(var j=0;j<matrix[0].length;j++){
if(matrix[i][j]==1) matrix[i][j]=-1;
else st.push([i,j]);
}
}
while(st.length){
cur = st.pop();
for(var i = 0; i < 4; i++){
var r = cur[0] + f[i][0]; var c = cur[1] + f[i][1];
if ((r >= 0 && r < matrix.length) && (c >= 0 && c < matrix[0].length)
&& (matrix[r][c]== -1 || matrix[r][c] > matrix[cur[0]][cur[1]]+1)) {
matrix[r][c] = matrix[cur[0]][cur[1]]+1;
st.push([r,c]);
}
}
}
return matrix;
}
动态规划
var updateMatrix = function(matrix) {
if(!matrix.length || !matrix[0].length) return null;
let n = matrix.length;
let m = matrix[0].length;
let ans = new Array(n);
for(let i = 0; i < n; i++) ans[i] = new Array(m).fill(n+m);
for(let i = 0; i < n; i++)
for(let j = 0; j < m; j++)
if(matrix[i][j] === 0) ans[i][j] = 0;
for(let i = 0; i < n; i++)
for(let j = 0; j < m; j++) {
if(i-1 >= 0) ans[i][j] = Math.min(ans[i][j], ans[i-1][j]+1);
if(j-1 >= 0) ans[i][j] = Math.min(ans[i][j], ans[i][j-1]+1);
}
for(let i = n-1; i >= 0; i--)
for(let j = 0; j < m; j++){
if(i+1 < n) ans[i][j] = Math.min(ans[i][j], ans[i+1][j]+1);
if(j-1 >= 0) ans[i][j] = Math.min(ans[i][j], ans[i][j-1]+1);
}
for(let i = n-1; i >= 0; i--)
for(let j = m -1 ; j >= 0; j--){
if(i+1 < n) ans[i][j] = Math.min(ans[i][j], ans[i+1][j]+1);
if(j+1 < m) ans[i][j] = Math.min(ans[i][j], ans[i][j+1]+1);
}
for(let i = 0; i < n; i++)
for(let j = m -1 ; j >= 0; j--){
if(i-1 >= 0) ans[i][j] = Math.min(ans[i][j], ans[i-1][j]+1);
if(j+1 < m) ans[i][j] = Math.min(ans[i][j], ans[i][j+1]+1);
}
return ans;
};
题目 1095. Find in Mountain Array 山脉数组中查找目标值
三次二分:先二分查找找到峰值,再左右两次二分
/**
* @param {number} target
* @param {MountainArray} mountainArr
* @return {number}
*/
var findInMountainArray = function(target, mountainArr) {
let l = 0, r = mountainArr.length() - 1;
// 寻找山峰
while (l < r) {
const mid = (l + r) / 2 | 0;
if (mountainArr.get(mid) >= mountainArr.get(mid + 1)) {
r = mid
} else {
l = mid + 1
}
}
// 标记山峰所在的位置
const peak = l;
// 在山峰左边查找,即在升序序列中查找
const index = binarySearch(mountainArr, target, 0, peak, v => v)
// 若存在,则直接返回下标
if (index !== -1) {
return index
}
// 否则在山峰右边查找,即在降序序列中查找
return binarySearch(mountainArr, target, peak + 1, mountainArr.length() - 1, v => -v)
// 二分法查找
// 其中 fn 是用来对升序还是降序的特殊处理
function binarySearch (list, target, l, r, fn) {
target = fn(target)
while (l <= r) {
const mid = (l + r) / 2 | 0
const cur = fn(list.get(mid))
if (cur === target) {
return mid
} else if (cur < target) {
l = mid + 1
} else {
r = mid - 1
}
}
return -1
}
};
题目 202. Happy Number 快乐数字
第一个方法:记住所有访问过的数字,来判断是否循环
时间复杂度 O(logn) ,空间复杂度 O(logn)
var isHappy = function(n) {
var _map={}, t;
while(true){
t=n; n=0;
while(t>0){
n += (t%10)*(t%10);
t = (t-t%10)/10;
}
if(n==1) return true;
if(_map[n] == 1) return false;
_map[n] = 1;
}
};
方法2:快慢指针,用快慢指针来找是否有循环
var isHappy = function(n) {
var getNextNumber = (num)=>{
let t=num; num=0;
while(t>0){
num += (t%10)*(t%10);
t = (t-t%10)/10;
}
return num;
}
var k = getNextNumber(n), m = n;
while(true){
if(k == 1 || m == 1) return true;
if(k == m) return false;
k = getNextNumber(getNextNumber(k));
m = getNextNumber(m);
}
};
方法3:数学法,只有出现数字1或者4会循环
var isHappy = function(n) {
var t=0;
while (n != 1 && n != 4) {
t=n; n = 0;
while(t>0){
n += (t%10)*(t%10);
t = (t-t%10)/10;
}
}
return n == 1;
};