利用 includes api 的特性以及循环弹出顶层的特性,将每个元素 push 进来后下一轮比对是否存在,存在则弹出。
1 2 3 4 5 6 7 8 9 10 11 12
var lengthOfLongestSubstring = function (s) { let arr = []; let max = 0; for (const str of s) { while (arr.includes(str)) { arr.shift(); } arr.push(str); if (max < arr.length) max = arr.length; } return max; };
var merge = function (nums1, m, nums2, n) { let i = m - 1, j = n - 1, len = m + n - 1; while (j >= 0) { nums1[len--] = i >= 0 && nums1[i] >= nums2[j] ? nums1[i--] : nums2[j--]; } };
165. 比较版本号
思路是分割.并遍历最长的 length(因为要同时比较),相等跳过,不等判断返回结果。
注意 前导 0 的情况可以用~~或者 parseInt 去解决,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
var compareVersion = function (version1, version2) { let v1arr = version1.split("."); let v2arr = version2.split("."); let maxLen = Math.max(v1arr.length, v2arr.length); for (let i = 0; i < maxLen; i++) { let v1 = v1arr[i] || 0; let v2 = v2arr[i] || 0; if (v1 === v2) continue; if (~~v1 < ~~v2) { return -1; } elseif (~~v1 > ~~v2) { return1; } } return0; };
415. 字符串相加
思路:从后往前累加到一个数组内,因为是字符串要转数字使用+ 最后从数组还原使用join('') 小技巧,从后往前读可以使用新的 api Array.at(-1) 注意余数和进位数字的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
var addStrings = function (num1, num2) { let last = -1; let maxLen = Math.max(num1.length, num2.length); let newAdd = 0; let res = []; while (maxLen--) { let c1 = num1.at(last) ? +num1.at(last) : 0; let c2 = num2.at(last) ? +num2.at(last) : 0; let count = c1 + c2 + newAdd; res.unshift((count % 10) + ""); newAdd = Math.floor(count / 10); last--; } if (newAdd > 0) { res.unshift("1"); } return res.join(""); };
20. 有效的括号
思路:设置匹配规则,遍历的 s 的时候如果元素满足左括号就把规则中的右括号 push 进数组,如果元素是右括号,说明此时弹出的括号要和右括号匹配 否则返回 false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
var isValid = function (s) { const rule = { "(": ")", "[": "]", "{": "}", }; let arr = []; for (const str of s) { if (str === "(" || str === "[" || str === "{") { arr.push(rule[str]); } else { if (arr.pop() !== str) { returnfalse; } } } return !arr.length; };
1. 两数之和
code
1 2 3 4 5 6 7 8 9 10
var twoSum = function (nums, target) { let map = newMap(); for (let i = 0; i < nums.length; i++) { if (map.has(nums[i])) { let less = map.get(nums[i]); return [less, i]; } map.set(target - nums[i], i); } };
var permute = function (nums) { const res = []; const used = newArray(nums.length).fill(false); const dfs = (path) => { if (path.length === nums.length) { res.push([...path]); return; } for (let i = 0; i < nums.length; i++) { if (used[i]) continue; path.push(nums[i]); used[i] = true; dfs(path); path.pop(); used[i] = false; } }; dfs([]); return res; };
112. 路径总和
dfs 注意第一次进去的是 root 的 val
1 2 3 4 5 6 7 8 9 10 11 12 13
var hasPathSum = function (root, targetSum) { if (!root) returnfalse; let res = false; const dfs = (n, sum) => { if (!n.left && !n.right && sum === targetSum) { res = true; } if (n.left) dfs(n.left, sum + n.left.val); if (n.right) dfs(n.right, sum + n.right.val); }; dfs(root, root.val); return res; };
var levelOrder = function (root) { if (!root) return []; const res = []; const queue = [root]; while (queue.length) { const len = queue.length; const temp = []; for (let i = 0; i < len; i++) { const n = queue.shift(); temp.push(n.val); if (n.left) queue.push(n.left); if (n.right) queue.push(n.right); } res.push(temp); } return res; };
70. 爬楼梯
斐波那契:思路,把每个问题分割成子问题,去逐步处理子问题。
1 2 3 4 5 6 7 8 9
const fun = (n) => { const fn = []; fn[1] = 1; fn[2] = 2; for (let i = 3; i <= n; i++) { fn[i] = fn[i - 1] + fn[i - 2]; } return fn[n]; };
const fn = (nums) => { let sum = nums[0]; let max = nums[0]; for (let i = 1; i < nums.length; i++) { sum = Math.max(nums[i], sum + nums[i]); max = Math.max(sum, max); } return max; };
const fn = (nums) => { nums = nums.sort((a, b) => a - b); let res = []; for (let i = 0; i < nums.length; i++) { if (nums[i] === nums[i - 1]) continue; let mid = i + 1; let last = nums.length - 1; while (mid < last) { let sum = nums[i] + nums[mid] + nums[last]; if (sum === 0) { res.push([nums[i], nums[mid], nums[last]]); mid++; while (nums[mid] === nums[mid - 1]) { mid++; } } elseif (sum < 0) { mid++; } else { last--; } } } return res; };
206.反转链表
反转链表的流程其实就是
稍微理解一下这个图就可以了。并且注意最后 cur 已经走完,但是 pre 构建完了。
1 2 3 4 5 6 7 8
const fn = (head){ let cur = head; let pre = null; while(cur){ [cur.next,pre,cur] = [pre,cur,cur.next] } return pre; }
141.环形链表
快指针每次都比慢指针多走一步,如果有环,必定会重合
1 2 3 4 5 6 7 8 9 10 11 12
const fn = (head) => { let slow = head; let fast = head; while (slow && fast && fast.next) { fast = fast.next.next; slow = slow.next; if (slow === fast) { returntrue; } } returnfalse; };
121. 买卖股票的最佳时机
基本思路是,使用后一次和前一次做差加初始的值进行判断,如果持续收益大于 0 说明本次是正收益,保留,否则置为 0.最后比较得到 max
注意 因为是比较前后,最后一位要省去
1 2 3 4 5 6 7 8 9
const fn = (prices) => { let last = 0; let max = 0; for (let i = 0; i < prices.length - 1; i++) { last = Math.max(0, last + prices[i + 1] - prices[i]); max = Math.max(max, last); } return max; };
215. 数组中的第 K 个最大元素
1
const fn = (nums, k) => {};
5. 最长回文子串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
const fn = (s) => { let len = s.length; let dp = newArray(len); let res = s[0]; for (let i = 0; i < len; i++) { dp[i] = newArray(len).fill(false); } for (let i = len - 1; i >= 0; i--) { for (let j = i; j < len; j++) { if (s.charAt(i) !== s.charAt(j)) continue; dp[i][j] = j - i <= 2 || dp[i + 1][j - 1]; if (dp[i][j] && j - i + 1 > res.length) { res = s.slice(i, j + 1); } } } return res; };
912. 排序数组
手撕快速排序
1 2 3 4
const quickSort = (nums) => { let left = []; let right = []; };
const fn = (matrix) => { const res = []; const dfs = (arr) => { let len = arr.length; //保存length for (let i = 0; i < len; i++) { if (!arr.length) return; if (arr[i] && !arr[i].length) return; //防止只走一圈就结束的情况 if (i === 0) { res.push(...arr.shift()); } elseif (i === len - 1) { res.push(...arr.pop().reverse()); } else { res.push(arr[i - 1].pop()); } } for (let i = arr.length - 1; i >= 0; i--) { if (!arr[i].length) return; res.push(arr[i].shift()); } if (!arr.length) return; dfs(arr); }; dfs(matrix); return res; };
94. 二叉树的中序遍历
code
1 2 3 4 5 6 7 8 9 10 11 12
var inorderTraversal = function (root) { if (!root) return []; const res = []; const dfs = (n) => { if (!n) return; dfs(n.left); res.push(n.val); dfs(n.right); }; dfs(root); return res; };
146. LRU 缓存机制
剑指 Offer 22. 链表中倒数第 k 个节点
注意使用 dummy
1 2 3 4 5 6 7 8 9 10 11 12 13 14
var getKthFromEnd = function (head, k) { let dummy = new ListNode(); dummy.next = head; let slow = dummy; let fast = dummy; while (k--) { fast = fast.next; } while (fast.next) { slow = slow.next; fast = fast.next; } return slow.next; };
const fn = (target, nums) => { let l = (r = sum = 0); let res = Infinity; while (r < nums.length) { sum += nums[r++]; //扩大窗口 while (sum >= target) { res = Math.min(res, r - l); //r-l代表满足条件的窗口长度 sum -= nums[l++]; } } return res === Infinity ? 0 : res; };