From 71cfcf7abc730995e8ee1535d2cf813e0195b866 Mon Sep 17 00:00:00 2001 From: "pengpeng.chen" Date: Sat, 9 Mar 2024 12:40:16 +0800 Subject: [PATCH] feat: add 0308 --- .../experiences/13202307handwriting.md | 32 ++- .../experiences/14202308handwriting.md | 70 +++++ .../experiences/15202309handwriting.md | 20 +- docs/interview/experiences/20React.md | 139 +++++++++- docs/interview/experiences/21leetcodeA.md | 65 +++-- docs/interview/experiences/27handwritingTs.md | 2 +- .../experiences/practise/202403/0306.js | 23 ++ .../experiences/practise/202403/0307.js | 44 ++- .../experiences/practise/202403/0308.js | 255 ++++++++++++++++++ .../experiences/practise/202403/0309.ts | 63 +++++ .../experiences/practise/202403/0310.js | 151 +++++++++++ docs/interview/logic/tree.md | 24 -- 12 files changed, 821 insertions(+), 67 deletions(-) create mode 100644 docs/interview/experiences/practise/202403/0309.ts create mode 100644 docs/interview/experiences/practise/202403/0310.js diff --git a/docs/interview/experiences/13202307handwriting.md b/docs/interview/experiences/13202307handwriting.md index bb90646..60c5590 100644 --- a/docs/interview/experiences/13202307handwriting.md +++ b/docs/interview/experiences/13202307handwriting.md @@ -27,7 +27,7 @@ nav: * 7.bigIntSum 大数相加 * 8.deepClone 深浅拷贝 * 9.16进制转 rgb or rgb 转 16 进制 - * 10.mockMap/mockFilter 数组方法重写 + * 10.mockMap/mockFilter/push 数组方法重写 * 11.myReduce 重写 * 12.flatter 数组和对象扁平化 * 13.手写发布订阅模式 @@ -319,6 +319,18 @@ Array.prototype.mockFilter = function (fn, context = window) { var filter = [1, 2, 3].filter((e) => e > 1); var filter2 = [1, 2, 3].mockFilter((e) => e > 1); console.log(filter, filter2); + +Array.prototype.mockPush = function () { + const len = arguments.length; + const arr = this; + for (let i = 0; i < len; i++) { + // 调用这个方法的数组 获取数组的长度 将当前元素放入最后一个 + arr[arr.length] = arguments[i]; + } + return arr.length; +}; +let test = [1, 2, 3]; +test.mockPush(4, 5, 6); ``` ## 11.myReduce 重写!! @@ -328,9 +340,10 @@ var sum = [1, 2, 3].reduce((pre, cur) => cur + pre, 10); console.log(sum); Array.prototype.mockReduce = function (fn, init) { - var arr = this; - var res = init ? init : arr[0]; - for (let i = init ? 0 : 1; i < arr.length; i++) { + const arr = this; + let res = init ? init : arr[0]; + const startIndex = init ? 0 : 1; // 老是混淆 + for (let i = startIndex; i < arr.length; i++) { res = fn.call(null, res, arr[i], i, arr); } return res; @@ -364,11 +377,11 @@ const list2 = flatten(array, 2); console.log(list1); // [1, 2, 3, 4, 5, 3, -4] console.log(list2); // [1, 2, 3, 4, [5], 3, -4] function myFlatten1(arr) { - return arr.reduce((pre, cur) => { - return pre.concat(Array.isArray(cur) ? myFlatten(cur) : cur); - }, []); + while (arr.some((item) => Array.isArray(item))) { + arr = [].concat(...arr); + } + return arr; } - function myFlatten2(arr) { return arr.reduce((pre, cur) => { if (Array.isArray(cur)) { @@ -1206,7 +1219,8 @@ parseUrlParams('?name=cpp&age=31&hobby=writing'); function removeDup(arr) { return arr.filter((item, index) => arr.indexOf(item) === index); } -removeDup([1, 33, 44, 99, 11]); +const uniueArr = [...new Set(arr)]; +removeDup([1, 1, 33, 44, 99, 11]); ``` ## 35.useEvent diff --git a/docs/interview/experiences/14202308handwriting.md b/docs/interview/experiences/14202308handwriting.md index 586bc70..84f48fc 100644 --- a/docs/interview/experiences/14202308handwriting.md +++ b/docs/interview/experiences/14202308handwriting.md @@ -481,6 +481,52 @@ function myAssign(target, ...source) { Object.myAssign = myAssign; ``` +## 22.二叉树的最大深度和最小深度 + +```js +// 最大深度 +function maxDepth(root) { + if (root == null) return 0; + let queue = [root]; + let depth = 0; + while (queue.length) { + let len = queue.length; + depth++; + for (let i = 0; i < len; i++) { + let cur = queue.shift(); + if (cur.left) { + queue.push(cur.left); + } + if (cur.right) { + queue.push(cur.right); + } + } + } + return depth; +} +// 最小深度 +function minDepth(root) { + if (root == null) return 0; + let queue = [root]; + let ans = 1; + while (queue.length) { + let len = queue.length; + for (let i = 0; i < len; i++) { + let cur = queue.shift(); + if (cur.left == null && cur.right == null) return ans; + if (cur.left) { + queue.push(cur.left); + } + if (cur.right) { + queue.push(cur.right); + } + } + ans++; + } + return ans; +} +``` + ## 23 TS 手写 IndexToUnion 索引转联合类型 ```ts @@ -507,6 +553,30 @@ type NN3 = IndexToUnion; const tt3: NN3 = { age: '22' }; ``` +## 24.TS 手写 Unique 去重 + +```ts +type ZONE = [0, 1, 1, 2, 2]; +type Equal = ((arg: A) => T extends A ? 1 : 2) extends ( + arg: B, +) => T extends B ? 1 : 2 + ? true + : false; +type EE1 = Equal; +type HasInclude = T extends [infer F, ...infer R] + ? Equal extends true + ? true + : HasInclude + : false; +type TP = HasInclude<[1, 2, 3], 11>; +type Unique = T extends [infer F, ...infer R] + ? HasInclude extends true + ? Unique + : Unique + : N; +type ZONE1 = Unique; +``` + ## 25 axios 以及拦截器 ```js diff --git a/docs/interview/experiences/15202309handwriting.md b/docs/interview/experiences/15202309handwriting.md index e48ff16..b92ab28 100644 --- a/docs/interview/experiences/15202309handwriting.md +++ b/docs/interview/experiences/15202309handwriting.md @@ -38,7 +38,7 @@ nav: * 18.请求超时重试? * 19.校验html是否合法 * 20.实现数组的旋转 - * 21.三数之和 + * 21.用setTimeout实现setInterval * 22.连续正整数之和 * 23.手写ts版方法调用的注解 * 24.自定义迭代器遍历斐波那契数列 @@ -887,12 +887,22 @@ var rotate2 = (nums, k) => { rotate2([1, 2, 3, 4, 5, 6], 2); ``` -## 21.三数之和 - -> 贼难的一道题 +## 21.用 setTimeout 实现 setInterval ```js - +function mockSetInterval(fn, delay) { + let timerId; + function interval() { + fn(); + timerId = setTimeout(interval, delay); + } + setTimeout(interval, delay); + return { + clear() { + clearTimeout(timerId); + }, + }; +} ``` ## 22.连续正整数之和? diff --git a/docs/interview/experiences/20React.md b/docs/interview/experiences/20React.md index 5eadc9a..94774f4 100644 --- a/docs/interview/experiences/20React.md +++ b/docs/interview/experiences/20React.md @@ -14,6 +14,7 @@ react 在面试中举足轻重,列举几个有代表性的问题,背一背. - [前端面试通关宝典:解析 44 道 React 测试题(上)](https://mp.weixin.qq.com/s/0EtJvTTZJFYZJOPDxLiAbw) - [前端面试通关宝典:解析 44 道 React 测试题(下)](https://mp.weixin.qq.com/s/H8TrhzBQpjJbz6A2oJ14UA) +- [2023 面试真题之框架篇](https://juejin.cn/post/7204307381689532474?searchId=202403081641040D4CF9DBB2742B236EEA) 列表如下 @@ -24,9 +25,10 @@ react 在面试中举足轻重,列举几个有代表性的问题,背一背. - 4.基于类的 React 组件,与函数式 React 组件之间有何区别? - 5.类组件生命周期的各个阶段 - 6.Redux 中的 reducer 是什么,它会用到哪些参数? -- 7. Redux 实现的是哪种模式?以及 Mobx 实现的是哪种模式。两者之间有什么区别? +- 7.Redux 实现的是哪种模式?以及 Mobx 实现的是哪种模式。两者之间有什么区别? - 8.什么是 JSX? - 9.userMemo 的用途是什么?它是如何起效的? +- diff 算法相关 ## 0.fiber @@ -395,7 +397,7 @@ export const getStaticProps = async ({ params: { slug } }) => { ``` 2. getServerSideProps: 此方法用于根据每个请求获取数据,并在服务器端预渲染页面。如果需要获取经常更改、或者仅供特定用户使用的数据,则可以使用此方法。(SSR) -3. getStaticPaths: 此方法用于在动态路由当中,指定需要在构建时预渲染的路径列表,常用于获取带有参数的动态路由数据。 +3. getStaticPaths: 此方法用于在**动态路由**当中,指定需要在构建时**预渲染的路径列表**,常用于获取带有参数的**动态路由数据**。 ```js export const getStaticPaths: GetStaticPaths = async () => { @@ -449,3 +451,136 @@ function useRerender() { return ref.current; } ``` + +## 17.react 中的 diff + +在 React 中,diff 算法需要与虚拟 DOM 配合才能发挥出真正的威力。React 会使用 diff 算法计算出虚拟 DOM 中真正发生变化的部分,并且只会针对该部分进行 dom 操作,从而避免了对页面进行大面积的更新渲染,减小性能的开销。在传统的 diff 算法中复杂度会达到 O(n^3)。React 中定义了三种策略,在对比时,根据策略只需遍历一次树就可以完成对比,将复杂度降到了 O(n): + +- tree diff:在两个树对比时,只会比较同一层级的节点,会忽略掉跨层级的操作 + +## 18.setState 同步异步问题 + +### v18 之前版本 + +> setState 并不是单纯的异步或同步,这其实与调用时的环境相关 + +1.在**合成事件** 和 **生命周期钩子**(除 componentDidUpdate) 中,setState 是"异步"的; + +2.在 原生事件 和 setTimeout 中,setState 是同步的,可以马上获取更新后的值; + +### 批量更新 + +多个顺序的 setState 不是同步地一个一个执行滴,会一个一个加入队列,然后最后一起执行。在 合成事件 和 生命周期钩子 中,setState 更新队列时,存储的是 合并状态(Object.assign)。因此前面设置的 key 值会被后面所覆盖,最终只会执行一次更新。 + +### 异步现象原因 + +setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是**合成事件和生命钩子函数的调用顺序在更新之前**,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback)中的 callback 拿到更新后的结果。 + +### setState 调用流程 + +- 调用 this.setState(newState) +- 将新状态 newState 存入 pending 队列 +- 判断是否处于 batch Update(isBatchingUpdates 是否为 true) + +### 为什么直接修改 this.state 无效 + +setState 本质是通过一个**队列机制**实现 state 更新的。 执行 setState 时,会将需要更新的 state 合并后放入状态队列,而不会立刻更新 state,队列机制可以批量更新 state。 + +### 自动批处理 + +在 v18 之前只在事件处理函数中实现了批处理,在 v18 中所有更新都将自动批处理,包括 promise 链、setTimeout 等异步代码以及原生事件处理函数 + +### V18 新特性 + +React 中 Fiber 树的更新流程分为两个阶段 **render 阶段**和 **commit 阶段**。 + +- 组件的 render 函数执行时称为 render(本次更新需要做哪些变更),纯 js 计算; +- 而将 render 的结果渲染到页面的过程称为 commit (变更到真实的宿主环境中,在浏览器中就是操作 DOM)。 + +在 V16 Async 异步 模式下,render 阶段是一次性执行完成;而在 Concurrent 模式下 render 阶段可以被拆解,每个时间片内执行一部分,直到执行完毕。由于 commit 阶段有 DOM 的更新,不可能让 DOM 更新到一半中断,必须一次性执行完毕。 + +### React 并发新特性 + +> 并发渲染机制 concurrent rendering 的目的:根据用户的设备性能和网速对渲染过程进行适当的调整, 保证 React 应用在长时间的渲染过程中依旧保持可交互性,避免页面出现卡顿或无响应的情况,从而提升用户体验。 + +## 19.ref 能否拿到函数组件的实例? + +### 使用 forwardRef + +将组件单独封装成组件 TextInput + +```js +const TextInput = React.forwardRef((props, ref) => { + return ; +}); +const TextPar = () => { + const inputEl = useRef(null); + const onClick = () => { + inputEl.current.focus(); + }; + return ( +
+ + +
+ ); +}; +``` + +### useImperativeHandle + +有时候,我们可能不想将整个子组件暴露给父组件,而只是暴露出父组件需要的值或者方法,这样可以让代码更加明确。而**useImperativeHandle** Api 就是帮助我们做这件事的。 + +```js +const TextInput = React.forwardRef((props, ref) => { + const inputRef = useRef(); + useImperativeHandle(ref, () => { + getName: () => { + inputRef.current.focus(); + }; + }); + return ; +}); +const TextPar = () => { + const inputEl = useRef(null); + const onClick = () => { + inputEl.current.getName(); + }; + return ( +
+ + +
+ ); +}; +``` + +## 20.React.memo + +memo:结合了 pureComponent 纯组件和 componentShouldUpdate()功能,会对传入的 props 进行一次对比,然后根据第二个函数返回值来进一步判断哪些 props 需要更新 + +> 要注意 memo 是一个高阶组件,函数式组件和类组件都可以使用。 + +```js +function MyComponent(props) {} +// 返回布尔值 true:不更新 false: 需要更新 +function areEqual(prevProps, nextProps) {} +export default React.memo(MyComponent, areEqual); +``` + +### memo 的注意事项 + +React.memo 与 PureComponent 的区别: + +#### 服务对象不同: + +- PureComponent 服务于类组件, +- React.memo 既可以服务于类组件,也可以服务与函数式组件, +- useMemo 服务于函数式组件 + +#### 针对的对象不同: + +- PureComponent 针对的是 props 和 state +- React.memo 只能针对 props 来决定是否渲染 + +> React.memo 的第二个参数的返回值与 shouldComponentUpdate 的返回值是相反的 React.memo:返回  true  组件不渲染 , 返回 false 组件重新渲染。 shouldComponentUpdate: 返回  true  组件渲染 , 返回  false  组件不渲染 diff --git a/docs/interview/experiences/21leetcodeA.md b/docs/interview/experiences/21leetcodeA.md index c5a9c70..05c52cd 100644 --- a/docs/interview/experiences/21leetcodeA.md +++ b/docs/interview/experiences/21leetcodeA.md @@ -767,10 +767,30 @@ function deletNode(node, k) {} ## [32.二叉树中的最大路径和](https://leetcode.cn/problems/binary-tree-maximum-path-sum/) -> hard +> middle ```js - +/** + * @param {TreeNode} root + * @return {number} + */ +var maxPathSum = function (root) { + if (root == null) return 0; + let max = Number.MAX_SAFE_INTEGER; + let dfs = (node) => { + if (node == null) return 0; + let left = dfs(node.left); + let right = dfs(node.right); + // 当前子树内部的最大路径和 + let innerMax = left + right + val; + max = Math.max(innerMax, max); + // 当前子树外部的最大路径和 + let outSideMax = root.val + Math.max(0, left, right); + return outSideMax > 0 ? outSideMax : 0; + }; + dfs(root); + return max; +}; ``` ## [33.合并区间](https://leetcode.cn/problems/merge-intervals/) @@ -815,28 +835,26 @@ mergeArr([ rose -> ros (删除 'e') */ function editPaths(word1, word2) { - let [m,n] = [word1.length, word2.length] - let dp = new Array(m+1).fill().map(() => new Array(n+1).fill(0)) - for(let i=1;i<=m;i++) { - dp[i][0]=i - } - for(let j=1;j<=n;j++) { - dp[0][j]=j - } - for(let i=1;i<=m;i++) { - for(let j=1;j<=n;j++) { - if(word1[i-1]==word2[j-1]) { - dp[i][j] = dp[i-1][j-1] + let [m, n] = [word1.length, word2.length]; + let dp = new Array(m + 1).fill().map(() => new Array(n + 1).fill(0)); + // 首列 + for (let i = 1; i <= m; i++) { + dp[i][0] = i; + } + // 首行 + for (let j = 1; j <= n; j++) { + dp[0][j] = j; + } + for (let i = 1; i <= m; i++) { + for (let j = 1; j <= n; j++) { + if (word1[i - 1] == word2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1]; } else { - dp[i][j] = Math.min( - dp[i-1][j]+1 - dp[i][j-1]+1 - dp[i-1][j-1]+1 - ) + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1; } } } - return dp[m][n] + return dp[m][n]; } ``` @@ -943,19 +961,22 @@ function findLongest(nums1, nums2) { } ``` -## [46.括号生成](https://leetcode.cn/problems/generate-parentheses/) +## [41.复原 IP 地址](https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE) + +## [46.括号生成](https://leetcode.cn/problems/generate-parentheses/solutions/597236/sui-ran-bu-shi-zui-xiu-de-dan-zhi-shao-n-0yt3/) ```js function generate(n) { if (n <= 0) return ''; let res = []; + // 保存的结果 左括号 右括号 var dfs = (paths, left, right) => { if (left < right || left > n) return; if (paths.length == 2 * n) { res.push(paths.slice()); return; } - dfs(paths + '(', left + 1, right); + dfs(paths + '(', left + 1, right); // 生产一个 加一个 dfs(paths + ')', left, right); }; dfs('', 0, 0); diff --git a/docs/interview/experiences/27handwritingTs.md b/docs/interview/experiences/27handwritingTs.md index 319db7b..dd71edf 100644 --- a/docs/interview/experiences/27handwritingTs.md +++ b/docs/interview/experiences/27handwritingTs.md @@ -70,7 +70,7 @@ type MockAwaited> = T extends Promise : T; ``` -## Parital +## 5.Parital ```ts /** diff --git a/docs/interview/experiences/practise/202403/0306.js b/docs/interview/experiences/practise/202403/0306.js index 93f71a4..b1d8df2 100644 --- a/docs/interview/experiences/practise/202403/0306.js +++ b/docs/interview/experiences/practise/202403/0306.js @@ -13,6 +13,28 @@ * 12.数组转树 */ +/** + * @param {TreeNode} root + * @return {number} + */ +var maxPathSum = function (root) { + if (root == null) return 0; + let max = Number.MAX_SAFE_INTEGER; + let dfs = (node) => { + if (node == null) return 0; + let left = dfs(node.left); + let right = dfs(node.right); + // 当前子树内部的最大路径和 + let innerMax = left + right + val; + max = Math.max(innerMax, max); + // 当前子树外部的最大路径和 + let outSideMax = root.val + Math.max(0, left, right); + return outSideMax > 0 ? outSideMax : 0; + }; + dfs(root); + return max; +}; + /** * @param {TreeNode} root * @return {TreeNode} @@ -26,6 +48,7 @@ var invertTree = function (root) { }; /** + * 是否是对称二叉树 * @param {TreeNode} root * @return {boolean} * 外侧 diff --git a/docs/interview/experiences/practise/202403/0307.js b/docs/interview/experiences/practise/202403/0307.js index 12c4207..669d206 100644 --- a/docs/interview/experiences/practise/202403/0307.js +++ b/docs/interview/experiences/practise/202403/0307.js @@ -13,6 +13,37 @@ * 12.重排链表 */ +var isBalanced = function (root) { + if (root == null) return true; + let left = maxDepth(root.left); + let right = maxDepth(root.right); + return Math.abs(left - right) <= 1 && isBalanced(root.left) && isBalanced(root.right); +}; +var maxDepth = (root) => { + if (root == null) return 0; + let left = maxDepth(root.left); + let right = maxDepth(root.right); + return Math.max(left, right) + 1; +}; + +var removeDuplicates = function (nums) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] == nums[i + 1]) { + nums.splice(i, 1); + i--; + } + } + return nums.length; +}; + +// 最大深度 +var maxDepth = function (root) { + if (root == null) return 0; + let left = maxDepth(root.left); + let right = maxDepth(root.right); + return 1 + Math.max(left, right); +}; + /** * @param {number} n * @return {string[]} @@ -21,11 +52,16 @@ */ var generateParenthesis = function (n) { let ans = []; - let dfs = (n, left, right) => { - if (left.length > right.length || right.length > n) return; - dfs(n); + let dfs = (paths, left, right) => { + if (left > right || right > n) return; + if (2 * n == paths.length) { + ans.push(paths); + return; + } + dfs(paths + '(', left + 1, right); + dfs(paths + ')', left, right + 1); }; - dfs(n, '(', ')'); + dfs('', 0, 0); return ans; }; diff --git a/docs/interview/experiences/practise/202403/0308.js b/docs/interview/experiences/practise/202403/0308.js index ccc8bf0..15b439c 100644 --- a/docs/interview/experiences/practise/202403/0308.js +++ b/docs/interview/experiences/practise/202403/0308.js @@ -1,5 +1,6 @@ /** * 1.每天都在写,到底记住了多少?离03月20日还剩两周,今天要做的就是反思回想 + * 心态放开一点,不要给自己太大的压力,一切都要向前看 * 2.不同路径 * 3.从中序和后序遍历构造二叉树 * 4.青蛙跳台阶问题 @@ -7,4 +8,258 @@ * 6.寻找重复数 * 7.复原IP地址 * 8.检测循环依赖 + * 9.括号生成 + * 10.最大深度和最小深度 + * 11.判断子序列 + * 12.不同的子序列 + * 111.两个字符串删除操作 + * 13.编辑距离 */ + +var minDistance = function (word1, word2) { + let [a, b] = [word1.length, word2.length]; + let dp = new Array(a + 1).fill().map(() => new Array(b + 1).fill(0)); + for (let i = 1; i <= a; i++) { + dp[i][0] = i; + } + for (let j = 0; j <= b; j++) { + dp[0][j] = j; + } + for (let i = 1; i <= a; i++) { + for (let j = 1; j <= b; j++) { + if (word1[i - 1] == word2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1] + 2); + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1; + } + } + } + return dp[a][b]; +}; + +/** + * 中序和后序遍历构造二叉树 +输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3] +输出:[3,9,20,null,null,15,7] + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ +var buildTree = function (inorder, postorder) { + if (!inorder.length || !postorder.length) return null; + let last = postorder.pop(); + let index = inorder.indexOf(last); + var root = { + val: last, + }; + root.left = buildTree(inorder.slice(0, index), postorder.slice(0, index)); + root.right = buildTree(inorder.slice(index + 1), postorder.slice(index)); + return root; +}; + +/** + * 不同的子序列 + * @param {*} s babgbag + * @param {*} t bag + */ +function diffChild(s, t) { + let [a, b] = [s.length, t.length]; + let dp = new Array(a + 1).fill().map(() => new Array(b + 1).fill(0)); + for (let i = 0; i <= b; i++) { + dp[0][i] = 1; + } + for (let i = 1; i <= a; i++) { + for (let j = 1; j <= b; j++) { + if (s[i - 1] == t[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + return dp[a][b]; +} + +/** +给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 +你可以按 任何顺序 返回答案。 +输入:n = 4, k = 2 +输出: +[ + [2,4], + [3,4], + [2,3], + [1,2], + [1,3], + [1,4], +] + */ +var combine = function (n, k) { + let ans = []; + var backTrack = (path, start) => { + if (path.length == k) { + ans.push(path.slice()); + return; + } + for (let i = start; i <= n; i++) { + if (!path.includes(i)) { + path.push(i); + backTrack(path, i); + path.pop(); + } + } + }; + backTrack([], 1); + return ans; +}; +combine(4, 2); + +/** + * +有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 '.' 分隔。 +例如:"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址,但是 "0.011.255.245"、"192.168.1.312" 和 "192.168@1.1" 是 无效 IP 地址。 +给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 '.' 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。 +示例 1: +输入:s = "25525511135" +输出:["255.255.11.135","255.255.111.35"]} + */ +/** + * @param {string} s + * @return {string[]} + */ +var restoreIpAddresses = function (s) { + let ans = []; + var backTrack = (arr, path, pos) => { + if (pos === arr.length - 1) { + ans.push(path.slice().join('.')); + return; + } + // 剪枝 + for (let i = 0; i < arr.length; i++) { + let cur = arr[i]; + } + }; + backTrack(s, [], 0); + return ans; +}; + +// 给定字符串 s 和 t ,判断 s 是否为 t 的子序列 +// 判断子序列 +// 输入:s = "abc", t = "ahbgdc" +// 输出:true +// 示例 2: +// 输入:s = "axc", t = "ahbgdc" +// 输出:false +function isChild(s, t) { + let [a, b] = [s.length, t.length]; + let dp = new Array(a + 1).fill().map(() => new Array(b + 1).fill(0)); + for (let i = 1; i <= a; i++) { + for (let j = 1; j <= b; j++) { + if (s[i - 1] == t[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = dp[i][j - 1]; + } + } + } + return dp[a][b] == a ? true : false; +} + +function isChild(s, t) { + if (s.length == 0) return true; + let j = 0; + for (let i = 0; i < t.length; i++) { + if (t[i] == s[j]) { + j++; + if (j == s.length) { + return true; + } + } + } + return false; +} +isChild('abc', 'ahbdc'); +// 背包问题 +function weightBag(values, weights, size) { + // 先遍历物品在遍历背包 + let len = weights.length; + let dp = new Array(len).fill().map(() => new Array(size + 1).fill(0)); + // 首行进行初始化 + for (let i = 0; i <= size; i++) { + dp[0][i] = values[0]; + } + for (let i = 1; i < len; i++) { + for (let j = 0; j <= size; j++) { + if (weights[i] > j) { + dp[i][j] = dp[i - 1][j]; + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]); + } + } + } + return dp[len - 1][size]; +} + +function reverseNode(root) { + let pre = null; + let cur = root; + while (cur) { + let next = cur.next; + cur.next = pre; + pre = cur; + cur = next; + } + return pre; +} + +function maxDepth(tree) { + if (tree == null) return 0; + let left = maxDepth(tree.left); + let right = maxDepth(tree.right); + return Math.max(left, right) + 1; +} + +function minDepth(tree) { + if (tree == null) return 0; + let left = minDepth(tree.left); + let right = minDepth(tree.right); + if (left == null && right != null) return right + 1; + if (left != null && right == null) return left + 1; + return 1 + Math.min(left, right); +} + +function generate(n) { + if (n == 0) return ''; + let ans = []; + let dfs = (paths, left, right) => { + if (left < right || left > n) return; + if (2 * n == paths.length) { + ans.push(paths); + return; + } + dfs(paths + '(', left + 1, right); + dfs(paths + ')', left, right + 1); + }; + dfs('', 0, 0); + return ans; +} + +function removeDup(arr) { + arr.sort((a, b) => a - b); + for (let i = 0; i < arr.length; i++) { + if (arr[i] == arr[i + 1]) { + arr.splice(i, 1); + i--; + } + } + return arr; +} +removeDup([1, 1, 2, 2, 3, 3, 3, 4, 5, 6, 6]); +function scrollTop() { + let top = window.document.scrollTop; + if (top > 0) { + window.requestAnimationFrame(scrollTop); + window.scrollTo(0, top - top / 8); + } +} diff --git a/docs/interview/experiences/practise/202403/0309.ts b/docs/interview/experiences/practise/202403/0309.ts new file mode 100644 index 0000000..d21b3e2 --- /dev/null +++ b/docs/interview/experiences/practise/202403/0309.ts @@ -0,0 +1,63 @@ +/** + * 1.pick + * 2.Awaited + * 3.Record + * 4.Exclude/Extract + */ + +type MockRecord

= { + [K in P]: T; +}; + +type MockAwaited

> = P extends Promise + ? T extends Promise + ? MockAwaited + : T + : P; +type AA1 = Awaited]>>; + +type TTU = { + name: string; + age: number; + hobby: () => void; +}; + +type MockPick = { + [P in K]: T[K]; +}; +type EE2 = MockPick; +// type MockParameters<> + +type ZONE = [0, 1, 1, 2, 2]; +type Equal = ((arg: A) => T extends A ? 1 : 2) extends ( + arg: B, +) => T extends B ? 1 : 2 + ? true + : false; +type EE1 = Equal; +type HasInclude = T extends [infer F, ...infer R] + ? Equal extends true + ? true + : HasInclude + : false; +type TP = HasInclude<[1, 2, 3], 11>; +type Unique = T extends [infer F, ...infer R] + ? HasInclude extends true + ? Unique + : Unique + : N; +type ZONE1 = Unique; + +type Include = A extends [infer F, ...infer R] + ? Equal extends true + ? true + : Include + : false; +type Q01 = Include<[1, 2, 3], 11>; + +// type Unique = A extends [infer F, ...infer R] +// ? Include extends true +// ? Unique +// : Unique +// : C; +// type Q02 = Unique<[1, 2, 3, 4, 4]>; diff --git a/docs/interview/experiences/practise/202403/0310.js b/docs/interview/experiences/practise/202403/0310.js new file mode 100644 index 0000000..74f4ced --- /dev/null +++ b/docs/interview/experiences/practise/202403/0310.js @@ -0,0 +1,151 @@ +/** + * 1.永远不要放弃,我可能离成功就差一步之遥 + * 2.前端架构和框架,前面准备了那么久,时刻厚积薄发一下了 + * 3.不定长二维数组全排列 + */ + +function thousand(str) { + return str.replace(/(?!^)(?=(\d{3})+$)/g, ','); +} +thousand('123456789'); + +var inputArray = [ + { id: 1, parentId: null, name: 'Node 1' }, + { id: 2, parentId: 1, name: 'Node 1.1' }, + { id: 3, parentId: 1, name: 'Node 1.2' }, + { id: 4, parentId: 2, name: 'Node 1.1.1' }, + { id: 5, parentId: null, name: 'Node 2' }, + { id: 6, parentId: 5, name: 'Node 2.1' }, +]; +// 数组转树 +function arrToTree(arr) { + let res = []; + let dfs = (arr, res, parentId) => { + for (let item of arr) { + if (parentId == item.parentId) { + var newItem = { + ...item, + children: [], + }; + res.push(newItem); + dfs(arr, newItem.children, item.id); + } + } + }; + dfs(arr, res, null); + return res; +} +arrToTree(inputArray); +function treeToArr(arr) { + let res = []; + var dfs = (arr, res) => { + for (let item of arr) { + res.push(item); + if (item.children && item.children.length) { + dfs(item.children, res); + } + delete item.children; + } + }; + dfs(arr, res); + return res; +} + +function treeToArrBfs(arr) { + let queue = [...arr]; + let res = []; + while (queue.length) { + let cur = queue.shift(); + res.push(cur); + if (cur.children && cur.children.length) { + queue.push(...cur.children); + } + delete cur.children; + } + return res; +} + +var listTree = [ + { + id: 1, + name: '部门1', + pid: 0, + children: [ + { + id: 2, + name: '部门1-1', + pid: 1, + children: [ + { + id: 4, + name: '部门1-1-1', + pid: 2, + children: [], + }, + ], + }, + { + id: 3, + name: '部门1-2', + pid: 1, + children: [ + { + id: 5, + name: '部门1-2-1', + pid: 3, + children: [], + }, + ], + }, + ], + }, + { + id: 6, + name: '部门2', + pid: 0, + children: [ + { + id: 7, + name: '部门2-1', + pid: 6, + children: [], + }, + ], + }, + { + id: 8, + name: '部门3', + pid: 0, + children: [], + }, +]; + +treeToArr(listTree); + +function per(arr) { + let ans = []; + let backTrack = (arr, path, row) => { + if (path.length == arr.length) { + ans.push(path.slice()); + return; + } + for (let i = 0; i < arr[row].length; i++) { + path.push(arr[row][i]); + backTrack(arr, path, row + 1); + path.pop(); + } + }; + backTrack(arr, [], 0); + return ans; +} +per([ + ['A', 'B'], + [1, 2, 3], + ['a', 'b'], +]); +A1a; +A1b; +A2a; +A2b; +B1a; +B1b; diff --git a/docs/interview/logic/tree.md b/docs/interview/logic/tree.md index f65a4d8..394f3fe 100644 --- a/docs/interview/logic/tree.md +++ b/docs/interview/logic/tree.md @@ -131,30 +131,6 @@ function maxDepth(root) { } ``` -### [平衡二叉树](https://leetcode-cn.com/problems/balanced-binary-tree/) - -一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 - -```js -/** - * @param {TreeNode} root - * @return {boolean} - */ -var isBalanced = function (root) { - if (!root) return true; - let left = maxDepth(root.left); - let right = maxDepth(root.right); - return Math.abs(left - right) <= 1 && isBalanced(root.left) && isBalanced(root.right); -}; -var maxDepth = function (root) { - if (root === null) { - return 0; - } else { - return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); - } -}; -``` - ### [对称二叉树](https://leetcode-cn.com/problems/symmetric-tree/) ```js