@@ -199,24 +199,207 @@ function maxProfit(prices) {
199199
200200``` javascript
201201function longestPalindrome (s ) {
202+ // 初始化左右边界,表示当前找到的最长回文子串的起始和结束位置
202203 let l = 0 ;
203204 let r = 0 ;
204-
205+ // 遍历字符串,以每个字符为中心尝试扩展回文
205206 for (let i = 0 ; i < s .length ; i++ ) {
206- helper (i, i);
207- helper (i, i + 1 );
207+ helper (i, i); // 情况 1:回文子串长度为奇数(以 i 为中心)
208+ helper (i, i + 1 ); // 情况 2:回文子串长度为偶数(以 i 和 i+1 为中心)
208209 }
209-
210+ // 辅助函数,用于扩展回文子串的左右边界
210211 function helper (m , n ) {
211- while (m >= 0 && n < s .length && s[m] == s[n]) {
212- m-- ;
213- n++ ;
212+ // 当左右指针范围内的字符相等时,继续向外扩展
213+ while (m >= 0 && n < s .length && s[m] === s[n]) {
214+ m-- ; // 左指针向左移动
215+ n++ ; // 右指针向右移动
214216 }
217+ // 如果当前回文子串的长度大于之前记录的最长回文子串长度
215218 if (n - m > r - l) {
216- r = n;
217- l = m;
219+ r = n; // 更新右边界
220+ l = m; // 更新左边界
218221 }
219222 }
223+ // 返回最长回文子串,通过 slice 提取范围 [l+1, r)
220224 return s .slice (l + 1 , r);
221225}
222226```
227+
228+ ## 11. 爬楼梯
229+
230+ #### 1. 正常递归求解,但是时间空间复杂度很差
231+
232+ ``` javascript
233+ function climbStairs (n ) {
234+ if (n <= 2 ) return n;
235+ return climbStairs (n - 1 ) + climbStairs (n - 2 );
236+ }
237+ ```
238+
239+ #### 2. 利用数组缓存值
240+
241+ ``` javascript
242+ function climbStairs (n ) {
243+ let result = [1 , 2 ];
244+ for (let i = 2 ; i < n; i++ ) {
245+ result[i] = result[i - 1 ] + result[i - 2 ];
246+ }
247+ return result .pop ();
248+ }
249+ ```
250+
251+ #### 3. 动态规划求解
252+
253+ ``` javascript
254+ function climbStairs (n ) {
255+ if (n <= 2 ) return n;
256+ let a = 1 ;
257+ let b = 2 ;
258+ let sum = 0 ;
259+ for (let i = 2 ; i < n; i++ ) {
260+ sum = a + b;
261+ a = b;
262+ b = sum;
263+ }
264+ return sum;
265+ }
266+ ```
267+
268+ ## 12. 三数之和
269+
270+ ``` javascript
271+ function threeSum (nums ) {
272+ // 如果数组为空或长度小于等于2,不可能组成三元组,直接返回
273+ if (! nums || nums .length <= 2 ) return nums;
274+ // 将数组排序,便于后续双指针查找
275+ nums .sort ((a , b ) => a - b);
276+ // 定义结果数组,用于存放满足条件的三元组
277+ let result = [];
278+ // 遍历数组,固定第一个数 nums[i]
279+ for (let i = 0 ; i < nums .length ; i++ ) {
280+ // 如果 nums[i] > 0,后面的数都大于 0,三数之和不可能为 0,直接退出循环
281+ if (nums[i] > 0 ) break ;
282+ // 如果当前数和前一个数相同,跳过,避免重复三元组
283+ if (i > 0 && nums[i] === nums[i - 1 ]) continue ;
284+ // 定义左右指针
285+ let L = i + 1 ; // 左指针初始化为当前数之后的第一个数
286+ let R = nums .length - 1 ; // 右指针初始化为数组最后一个数
287+ // 开始双指针查找
288+ while (L < R ) {
289+ // 计算三数之和
290+ const sum = nums[i] + nums[L ] + nums[R ];
291+ if (sum === 0 ) {
292+ // 如果三数之和为 0,将三元组加入结果数组
293+ result .push ([nums[i], nums[L ], nums[R ]]);
294+ // 跳过重复的右指针值,避免重复三元组
295+ while (L < R && nums[R ] === nums[R - 1 ]) {
296+ R -- ;
297+ }
298+ // 跳过重复的左指针值,避免重复三元组
299+ while (L < R && nums[L ] === nums[L + 1 ]) {
300+ L ++ ;
301+ }
302+ // 左指针右移,右指针左移,继续检查其他可能的三元组
303+ L ++ ;
304+ R -- ;
305+ } else if (sum > 0 ) {
306+ // 如果三数之和大于 0,说明右指针的值过大,右指针左移
307+ R -- ;
308+ } else {
309+ // 如果三数之和小于 0,说明左指针的值过小,左指针右移
310+ L ++ ;
311+ }
312+ }
313+ }
314+ // 返回结果数组
315+ return result;
316+ }
317+ ```
318+
319+ ## 13. 环形链表
320+
321+ ``` javascript
322+ function hasCycle (head ) {
323+ // 定义两个指针,slow 和 fast,均初始化为链表的头节点
324+ let slow = head;
325+ let fast = head;
326+ // 当快指针和快指针的下一个节点不为空时,继续循环
327+ while (fast && fast .next ) {
328+ // 慢指针每次移动一步
329+ slow = slow .next ;
330+ // 快指针每次移动两步
331+ fast = fast .next .next ;
332+ // 如果快慢指针相遇,说明链表中存在环
333+ if (slow === fast) return true ;
334+ }
335+ // 如果循环结束,说明快指针到达链表末尾,没有环
336+ return false ;
337+ }
338+ ```
339+
340+ ## 14. 二叉树的层序遍历
341+
342+ ``` javascript
343+ function levelOrder (root ) {
344+ // 如果根节点为空,返回空数组,表示没有任何层级
345+ if (! root) return [];
346+ // 初始化结果数组,用于存储每一层的节点值
347+ let result = [];
348+ // 使用队列实现层序遍历,初始队列包含根节点
349+ let queue = [root];
350+ // 当队列不为空时,持续处理队列中的节点
351+ while (queue .length ) {
352+ // 用于存储当前层的节点值
353+ let levelNodes = [];
354+ // 获取当前层的节点数量
355+ let levelNodeSize = queue .length ;
356+ // 遍历当前层的所有节点
357+ for (let i = 0 ; i < levelNodeSize; i++ ) {
358+ // 从队列中取出一个节点
359+ const node = queue .shift ();
360+ // 将该节点的值加入当前层的结果中
361+ levelNodes .push (node .val );
362+ // 如果左子节点存在,将其加入队列
363+ if (node .left ) queue .push (node .left );
364+ // 如果右子节点存在,将其加入队列
365+ if (node .right ) queue .push (node .right );
366+ }
367+ // 将当前层的节点值加入最终结果中
368+ result .push (levelNodes);
369+ }
370+ // 返回结果数组,包含所有层的节点值
371+ return result;
372+ }
373+ ```
374+
375+ ## 15. 路径总和
376+
377+ #### 1. 通过递归
378+
379+ ``` javascript
380+ function hasPathSum (root , targetSum ) {
381+ if (! root) return false ;
382+ if (! root .left && ! root .right && root .val === targetSum) return true ;
383+ const nextTargetSum = targetSum - root .val ;
384+ return (
385+ hasPathSum (root .left , nextTargetSum) ||
386+ hasPathSum (root .right , nextTargetSum)
387+ );
388+ }
389+ ```
390+
391+ #### 2. 通过 ` BFS `
392+
393+ ``` javascript
394+ function hasPathSum (root , targetSum ) {
395+ if (! root) return false ;
396+ let queue = [[root, root .val ]];
397+ while (queue .length ) {
398+ const [node , currentSum ] = queue .shift ();
399+ if (! node .left && ! node .right && currentSum === targetSum) return true ;
400+ if (node .left ) queue .push ([node .left , node .left .val + currentSum]);
401+ if (node .right ) queue .push ([node .right , node .right .val + currentSum]);
402+ }
403+ return false ;
404+ }
405+ ```
0 commit comments