数组 二分法 二分(左闭右闭) 循环判断条件
while(left <= right)
因为左闭右闭的情况下,left是可以等于right的
区间的处理
left = mid + 1
right = mid -1
因为当mid不符合条件时,不应该再将mid放入闭区间里
最后结果
right 等于 left -1
代码
1 2 3 4 5 6 while (left <= right) { mid = left + ((right - left) << 1 ) if (nums[mid] > target) right = mid - 1 ; else if (num[mid] < target) left = mid + 1 ; else return mid; }
二分(左闭右开) 判断条件
while(left<right)
因为左闭右开的情况下,left不可以等于right
最后结果
left等于right
区间的处理
left = mid + 1
right = mid
因为此时右边是开区间 把mid赋值到右边不会影响
代码
1 2 3 4 5 6 while (left < right) { mid = left + ((right - left) << 1 ) if (nums[mid] > target) right = mid; else if (num[mid] < target) left = mid + 1 ; else return mid; }
35 搜索插入位置 给定排序数组nums和一个目标值target
找到目标值并返回索引
如果目标值不存在数组中,返回其按顺序插入的位置
思路
二分查找,找到目标值返回索引很简单
重点 在于不在数组中返回其 应该插入位置的情况
左闭右闭 时 未找到的情况
最后一次查找,开始时 left等于right
mid等于left等于right
,结束时left等于right + 1
如果target > nums[mid]
,target应该在mid右端一位,即target的索引应该是mid + 1
,此时left = mid + 1
,所以返回left
或者right+1
如果target < nums[mid]
, target应该在mid左端一位,即target的索引应该是mid
,此时right = mid - 1
,所以返回 left
或者right + 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var searchInsert = function (nums, target ) { let left = 0 , right = nums.length - 1 ; while (left <= right) { let mid = left + ((right - left) >> 1 ); if (nums[mid] > target) { right = mid - 1 ; } else if (nums[mid] < target) { left = mid + 1 ; } else return mid; } return right + 1 ; };
左闭右开 时 未找到的情况
最后一次查找,开始时right等于left+1
mid等于left等于right-1
,结束时left等于right
如果target > nums[mid]
,target应该在mid右端一位,即target的索引应该是mid + 1
,此时left = mid + 1
,所以返回left
或者right
如果target < nums[mid]
, target应该在mid左端一位,即target的索引应该是mid
,此时right = mid - 1
,所以返回 left
或者right
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var searchInsert = function (nums, target ) { let left = 0 , right = nums.length ; while (left < right) { let mid = left + ((right - left) >> 1 ); if (nums[mid] > target) { right = mid; } else if (nums[mid] < target) { left = mid + 1 ; } else return mid; } return right; };
34 在排序数组中查找元素的第一个和最后一个位置 给一个非递减的数组nums和目标值target
给定目标值在数组中的开始位置和结束位置
如果数组中不存在目标值,返回[-1,-1]
思路
将查找元素的第一个和最后一个位置问题转换成 查找元素的左边界(不含该元素)和右边界问题(不含该元素)
重点 如何寻找左右边界呢?改写二分法。
如果要寻找左边界,那就是要找尽可能小于目标值的地方,即使找到了target也不返回,而是继续向左查找,直到最后一个target为mid时 right就是左边界
1 2 3 4 5 6 7 8 9 10 11 12 13 var searchLeftBorder = function (nums, target ) { let left = 0 , right = nums.length - 1 , leftBorder = -2 ; while (left <= right) { let mid = left + ((right - left) >> 1 ); if (nums[mid] >= target) { right = mid - 1 ; leftBorder = right; } else { left = mid + 1 ; } } return leftBorder; }
如果要寻找右边界,那就是要找尽可能小于目标值的地方,即使找到了target也不返回,而是继续向右查找,直到最后一个target为mid时 left就是左边界
1 2 3 4 5 6 7 8 9 10 11 12 13 var searchRightBorder = function (nums, target ) { let left = 0 , right = nums.length - 1 , rightBorder = -2 ; while (left <= right) { let mid = left + ((right - left) >> 1 ); if (nums[mid] > target) { right = mid - 1 ; } else { left = mid + 1 ; rightBorder = left; } } return rightBorder; }
现在讨论leftBorder和rightBorder各种取值的情况
初始时 leftBorder和rightBorder都为 -2
如果数组中所有元素都大于 target
那么rightBorder
一直都不被赋值,试图在最左边找leftBorder
最后 leftBorder
为-1
如果数组中所有元素都小于target
那么leftBorder
一直都不被赋值,试图在最右边找rightBorder
最后rightBorder
为-1
如果target在数组中元素的范围内,但不在数组内。那么leftBorder + 1等于rightBorder
target在数组中元素的范围内 ,那么rightBorder > leftBorder + 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var searchRange = function (nums, target ) { let leftBorder = searchLeftBorder (nums, target); let rightBorder = searchRightBorder (nums, target); if (leftBorder === -2 || rightBorder === -2 ) { return [-1 , -1 ]; } if (rightBorder - leftBorder > 1 ) return [leftBorder + 1 , rightBorder - 1 ]; return [-1 , -1 ]; };
删除数组元素(原地修改) leetcode27题
题干 :给定一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
原地:不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地 修改输入数组。
暴力解法 两重循环,第一次循环遍历元素,当元素值为val时,进入第二重循环,将所有数右移
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var removeElement = function (nums, val ) { let len = nums.length ; for (let i = 0 ; i < len; i++){ if (nums[i] == val) { for (let j = i + 1 ; j < len; j++){ nums[j-1 ] = nums[j]; } i--; len--; } } console .log (len); return len; };
双指针法 fast指针遍历数组,slow指针指向新数组
当fast指向的值为val时,fast指针右移动
当fast指向的值不为val时,将fast的值赋给slow指针,slow指针向右移动
1 2 3 4 5 6 7 8 9 10 11 12 13 var removeElement = function (nums, val ) { let slow = 0 , fast = 0 ; for (fast = 0 ; fast < nums.length ; fast++){ if (nums[fast] != val) nums[slow++] = nums[fast]; } return slow };
26 删除有序数组中的重复项 给定非降序数组nums
删除所有重复的元素,并将剩下的元素排序 ,并返回新数组的长度
nums
唯一元素的数量为k,其余元素与大小并不重要
思路
双指针, slow指针和fast指针初始都指向0
fast指针遍历数组
当fast指针和slow指针指的元素不一样的时候,++nums[slow] = nums[fast]
删除后新数组的长度为slow+1
1 2 3 4 5 6 7 8 9 10 11 12 var removeDuplicates = function (nums ) { let slow = 0 , fast = 0 ; for (; fast < nums.length ; fast++){ if (nums[fast] != nums[slow]) nums[++slow] = nums[fast]; } return slow + 1 ; };
283 移动零 给定数组nums
要求将所有0移动到末尾,并保持非零元素的相对顺序
要求 对原数组进行操作 空间复杂度o(1)
暴力解法
一遇到0就往后找不为0的数 并交换
时间复杂度O(n2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var moveZeroes = function (nums ) { for (let i = 0 ; i < nums.length ; i++){ if (nums[i] === 0 ) { for (let j = i + 1 ; j < nums.length ; j++) { if (nums[j] !== 0 ) { nums[i] = nums[j]; nums[j] = 0 ; break ; } } } } };
双指针法
定义指针slow,fast,fast遍历数组
fast指向的是0 fast向右移动
fast指向的是非0 fast的值给slow fast和slow都向右移动
最后数组的fill方法填充元素0
有序数组的平方 leetcode 997题
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
暴力解法 先平方再排序(略)
双指针 每次都选最大的元素放到新数组里,因为原先是非递减排序的数组,所以平方后最大的值只可能在两端(绝对值最大)
左侧i指针,右侧j指针
当指针i的数值平方小于指针j的数值平方时,将指针j的值放到新数组末尾,将j左移
反之 将指针i的值放到数组末尾,将i右移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var sortedSquares = function (nums ) { let i = 0 , j = nums.length -1 , k = j; let results = []; while (i<=j){ if (nums[i] * nums[i] < nums[j] * nums[j]) { nums[k--] = nums[j] * nums[j]; j--; } else { nums[k--] = nums[i] * nums[i]; i++; } } return results; };
长度最小的子数组 leetcode 209
给定数组和特定值,找出数组中满足其和>=特定值的长度最小的 连续 子数组,并返回其长度,如果不存在满足条件的子数组,返回0
滑动窗口 思路是,定义数组开头指针start 末尾指针end。
【end一直后移动,当子数组满足题目的条件时,start后移直到不满足条件,再让end后移动到满足条件】,如此循环,记录满足条件的最小长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var minSubArrayLen = function (target, nums ) { let start = 0 , end = 0 , result = Infinity , sum = 0 ; for (; end < nums.length ; end++) { sum += nums[end]; while (sum >= target) { const subLen = end - start + 1 result = result < subLen? result : subLen; sum -= nums[start++]; } } return result === Infinity ? 0 : result; }
904 水果成篮 给定数组fruits,fruits[i]表示水果的种类。
两个篮子,每个篮子只能装一种水果,每个篮子无限装。
只能从前往后按顺序装。
思路
滑动窗口,end,start两个指针。
【end右移。当当前无法继续采摘时,start指针指向end指针前方的,最前的,与end指针同种的水果。然后继续end右移】
如此循环,每次end右移,记下最大长度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 var totalFruit = function (fruits ) { let start = 0 , end = 0 , result = 0 ,basket = []; for (;end < fruits.length ; end++){ if (!basket.includes (fruits[end]) && basket.length < 2 ) { basket.push (fruits[end]); } else if (basket.includes (fruits[end])) { } else { start = end - 1 while (fruits[start] === fruits[start-1 ]) { start--; } basket = [fruits[end], fruits[start]] } const subLen = end - start + 1 ; result = result > subLen? result : subLen; console .log (`从${start} 到${end} ,篮子里是${basket} ` ); } return result; }
76 最小覆盖子串 leetcode 76
给定字符串s,t
要求在s中找到涵盖字符串t的最小字串
可以乱序 但要求t的所有字母都在s子串里出现过 重复的字母,次数也不能小于s子串的次数
思路
滑动窗口,指针start和end,当s子串涵盖了t时回退到不涵盖的情况
关键是如何表示涵盖 了
为s子串创建cntS数组,t子串创建cntT数组,统计每个字母出现的次数,当s子串中每个字母出现的字数都大于t时 即为涵盖 了
方法一 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 var isCovered = function (cntS, cntT ){ for (let i = 'a' .charCodeAt (0 ); i <= 'z' .charCodeAt (0 ); i++) { if (cntS[i] < cntT[i]) return false ; } for (let i = 'A' .charCodeAt (0 ); i <= 'Z' .charCodeAt (0 ); i++) { if (cntS[i] < cntT[i]) return false ; } return true ; } var minWindow = function (s, t ) { let start = 0 , end = 0 , cntS = Array (128 ).fill (0 ), cntT = Array (128 ).fill (0 ), ansLeft = -1 , ansRight = s.length ; for (let char of t) { cntT[char.charCodeAt (0 )]++; } for (; end < s.length ; end++){ cntS[s[end].charCodeAt (0 )]++; while (isCovered (cntS, cntT)) { if (end - start < ansRight - ansLeft) { ansLeft = start; ansRight = end; } cntS[s[start].charCodeAt (0 )]--; start++; } } return ansLeft === -1 ? "" : s.substring (ansLeft, ansRight + 1 ) }
方法二:
用type表示t的字母种类,每当t有一个字母被s涵盖全,就将type–
直到type为0时即为 涵盖 了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 var minWindow = function (s, t ) { let start = 0 , end = 0 , cntS = Array (128 ).fill (0 ), cntT = Array (128 ).fill (0 ), ansLeft = -1 , ansRight = s.length , type = 0 ; for (let char of t) { if (cntT[char.charCodeAt (0 )]++ === 0 ) { type++; } } for (; end < s.length ; end++){ let num = s[end].charCodeAt (0 ); if (++cntS[num] === cntT[num]) { type--; } while (type === 0 ) { if (end - start < ansRight - ansLeft) { ansLeft = start; ansRight = end; } const x = s[start++].charCodeAt (0 ); if (cntS[x]-- === cntT[x]){ type++; } } } return ansLeft === -1 ? "" : s.substring (ansLeft, ansRight + 1 ) }
螺旋矩阵 59 螺旋矩阵II 给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
思路
循环不变量
填充分为4部分
例如输入为3时,从左到右填1,2;从上到下填3,4;从右到左填5,6;从下到上填7,8
输入n,循环n/2(向下取整)
,如果n为奇数,最后在矩阵中间填一次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 var generateMatrix = function (n ) { let mid = Math .floor (n / 2 ) let loop = Math .floor (n / 2 ) let matrix = new Array (n).fill (0 ).map (() => new Array (n).fill (0 )) let offset = 1 , startx = 0 , starty = 0 let count = 1 while (loop--) { let col = starty, row = startx for (; col < n - offset; col++) matrix[row][col] = count++ for (; row < n - offset; row++) matrix[row][col] = count++ for (; col > starty; col--) matrix[row][col] = count++ for (; row > startx; row--) matrix[row][col] = count++ startx++,starty++ offset++ } if (n % 2 === 1 ) { matrix[mid][mid] = count } return matrix };
54 螺旋矩阵 给定一个 m
行 n
列的矩阵 matrix
,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
思路:
循环loop次,loop = m > n ? Math.floor(m / 2) : Math.floor(n / 2)
循环过程中,当res里已经有了所有元素时,返回
特殊情况,m,n相等且都为奇数时,matrix中间元素填充到res
前缀和 前缀和概念 :
一个长度为n的数组a[1] ~ a[n],前缀和sum[i]等于a[1] ~ a[i]的和:
sum[i] = a[1] + a[2] + … + a[i]
递推公式:sum[i]=sum[i-1]+a[i]
差分概念 :
差分是前缀和的逆运算,一个长度为n的数组a[1]~a[n],差分d[i]等于a[i]-a[i-1]
公式 :a[i]=a[i-1]+d[i]
区间和 题目链接
给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const readline = require ('readline' )const rl = readline.createInterface ({ input : process.stdin , output : process.stdout }) let nlet num = []let s = []let inputStep = 0 let ranges = []rl.on ('line' , (line ) => { if (inputStep === 0 ) { n = parseInt (line.trim ()) inputStep++ } else if (inputStep <=n) { inputStep++ num.push (parseInt (line.trim ())) } else { range = line.trim ().split (' ' ).map (Number ) ranges.push (range) } }) rl.on ('close' , () => { s = new Array (num.length ).fill (0 ) s[0 ] = num[0 ] for (let i = 1 ; i < num.length ; i++) { s[i] = s[i-1 ] + num[i] } ranges.forEach (range => { const [start, end] = range let sum = s[end] if (start > 0 ) sum -= s[start - 1 ] console .log (sum) }) })
开发商购买土地 题目链接
本题也是用前缀和优化时间复杂度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 const readline = require ('readline' )const rl = readline.createInterface ({ input : process.stdin , output : process.stdout }) let n,mlet inputStep = 0 let num = []let prefixColSum = []let prefixRowSum = []rl.on ('line' , (line ) => { if (inputStep === 0 ) { [n, m] = line.trim ().split (' ' ).map (Number ) inputStep++ num = new Array (n).fill (0 ).map (() => new Array (m).fill (0 )) } else if (inputStep <= n) { num[inputStep - 1 ] = line.trim ().split (' ' ).map (Number ) inputStep++ } else { } }) rl.on ('close' , () => { prefixColSum = new Array (m).fill (0 ) let colSum = new Array (m).fill (0 ) prefixRowSum = new Array (n).fill (0 ) let rowSum = new Array (n).fill (0 ) for (let i = 0 ; i < m; i++) { for (let j = 0 ; j < n; j++) { colSum[i] += num[j][i] } if (i === 0 ) prefixColSum[i] = colSum[i] else prefixColSum[i] = prefixColSum[i-1 ] + colSum[i] } for (let i = 0 ; i < n; i++) { for (let j = 0 ; j < m; j++) { rowSum[i] += num[i][j] } if (i === 0 ) prefixRowSum[i] = rowSum[i] else prefixRowSum[i] = prefixRowSum[i-1 ] + rowSum[i] } let sum = Infinity for (let i = 1 ; i < m; i++) { let res = Math .abs (prefixColSum[m - 1 ] - 2 * prefixColSum[i - 1 ]) sum = sum < res ? sum : res } for (let i = 1 ; i < n; i++) { let res = Math .abs (prefixRowSum[n - 1 ] - 2 * prefixRowSum[i - 1 ]) sum = sum < res ? sum : res } console .log (sum) })
链表 移除链表元素 203 移除链表元素 题意 :删除链表中等于给定值 val 的所有节点。
两种方法 :
对原链表进行操作,需要分类,因为移除头结点和移除其他结点操作不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class ListNode { val next = null constructor (value) { this .val = value this .next = null } } var removeElements = function (head, val ) { while (head && head.val === val) { head = head.next } if (head === null ) return head let cur = head while (cur.next ) { if (cur.next .val === val) cur.next = cur.next .next else cur = cur.next } return head };
使用虚拟头结点,统一操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class ListNode { val next = null constructor (value) { this .val = value this .next = null } } var removeElements = function (head, val ) { let dummyHead = new ListNode (0 ) dummyHead.next = head let cur = dummyHead while (cur.next ) { if (cur.next .val === val) cur.next = cur.next .next else cur = cur.next } return dummyHead.next };
链表常用操作 707 设计链表
MyLinkedList()
初始化 MyLinkedList
对象。
int get(int index)
获取链表中下标为 index
的节点的值。如果下标无效,则返回 -1
。
void addAtHead(int val)
将一个值为 val
的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
void addAtTail(int val)
将一个值为 val
的节点追加到链表中作为链表的最后一个元素。
void addAtIndex(int index, int val)
将一个值为 val
的节点插入到链表中下标为 index
的节点之前。如果 index
等于链表的长度,那么该节点会被追加到链表的末尾。如果 index
比长度更大,该节点将 不会插入 到链表中。
void deleteAtIndex(int index)
如果下标有效,则删除链表中下标为 index
的节点。
注意链表的自身属性size和head指针的变化,同时要注意不要对空指针赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 class LinkNode { val next = null constructor (value) { this .val = value this .next = null } } var MyLinkedList = function ( ) { this ._size = 0 this ._head = null }; MyLinkedList .prototype .get = function (index ) { if (index < 0 || index >= this ._size ) return -1 let cur = this ._head while (index) { cur = cur.next index-- } return cur.val }; MyLinkedList .prototype .addAtHead = function (val ) { let newNode = new LinkNode (val) newNode.next = this ._head this ._head = newNode this ._size ++ }; MyLinkedList .prototype .addAtTail = function (val ) { let dummyHead = new LinkNode (0 ) dummyHead.next = this ._head let newNode = new LinkNode (val) if (!dummyHead.next ) { dummyHead.next = newNode } else { let cur = dummyHead.next while (cur.next ) { cur = cur.next } newNode.next = null cur.next = newNode } this ._size ++ this ._head = dummyHead.next }; MyLinkedList .prototype .addAtIndex = function (index, val ) { if (index > this ._size ) return let dummyHead = new LinkNode (0 ) dummyHead.next = this ._head let cur = dummyHead while (index--) { cur = cur.next } let newNode = new LinkNode (val) newNode.next = cur.next cur.next = newNode this ._head = dummyHead.next this ._size ++ }; MyLinkedList .prototype .deleteAtIndex = function (index ) { if (index < 0 || index >= this ._size ) return let dummyHead = new LinkNode (0 ) dummyHead.next = this ._head let cur = dummyHead while (index) { cur = cur.next index-- } cur.next = cur.next .next this ._head = dummyHead.next this ._size -- }; let myLinkedList = new MyLinkedList ();myLinkedList.addAtHead (1 ); myLinkedList.addAtTail (3 ); myLinkedList.addAtIndex (1 , 2 ); myLinkedList.get (1 ); myLinkedList.deleteAtIndex (1 ); console .log (myLinkedList); myLinkedList.get (0 ); console .log (myLinkedList);
翻转链表 206 翻转链表 反转一个单链表
每次只操作两个节点
pre指向前一个节点,cur指向后一个节点
链表为空或只有一个节点时,返回head
1 2 3 4 5 6 7 8 9 10 11 12 var reverseList = function (head ) { if (!head || !head.next ) return head let cur = head let pre = null while (cur) { let temp = cur.next cur.next = pre pre = cur cur = temp } return pre };
两两交换链表的节点 24 两两交换链表中的节点 使用虚拟头节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var swapPairs = function (head ) { let dummyHead = new ListNode (0 , head) let cur = dummyHead while (cur.next && cur.next .next ) { let temp = cur.next let temp1 = cur.next .next let temp2 = cur.next .next .next cur.next = temp1 temp1.next = temp temp.next = temp2 cur = temp } return dummyHead.next };
删除链表的第n个节点 19 删除链表的倒数第n个结点 重点在于使用双指针,一次实现找到倒数第n个
使用虚拟头节点
先让fast移动n+1次
再同时移动slow和fast直到fast为null
这样slow就移动了 size - (n+1)次,刚好指向倒数第n个结点的前一个节点 可以执行删除操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var removeNthFromEnd = function (head, n ) { let dummyHead = new ListNode (0 , head) let fast = dummyHead let slow = dummyHead n++ while (n--) { fast = fast.next } while (fast) { fast = fast.next slow = slow.next } slow.next = slow.next .next return dummyHead.next }
链表相交 160 链表相交 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 var getListLength = function (head ) { let len = 0 , cur = head while (cur) { len++ cur = cur.next } return len } var getIntersectionNode = function (headA, headB ) { let lenA = getListLength (headA) let lenB = getListLength (headB) let curA = headA let curB = headB if (lenA < lenB) { let temp = curA curA = curB curB = temp temp = lenA lenA = lenB lenB = temp } let i = lenA - lenB while (i--) { curA = curA.next } while (curA && curA !==curB) { curA = curA.next curB = curB.next } return curA };
环形链表 142 环形链表 题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
判断环 ?
遍历链表过程中,如果指到null,就是有环,反之就是没有
找出环的入口
假设链表非环部分长度为a 环长度为b
设置快慢指针,快指针每次都比慢多移动一次,当二者相遇时,若慢指针移动次数为s
,快指针fast
移动次数为s+nb
,由于快指针移动距离为慢指针2倍,所以s等于nb
,慢指针移动了nb
由于环的入口为a + nb
,所以只需让慢指针移动a即可,只需让fast再指向a,当fast和slow相遇时,即为所求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var detectCycle = function (head ) { let fast = head let slow = head while (fast && fast.next ) { fast = fast.next .next slow = slow.next if (slow === fast) { fast = head while (fast !== slow) { slow = slow.next fast = fast.next } return slow } } return null };
哈希表 有效的字母异位词 242 有效的字母异位词 数组也是一种哈希表
用空间存储换时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var isAnagram = function (s, t ) { let checkArray = new Array (26 ).fill (0 ) for (let char of s) { const index = char.charCodeAt (0 ) - 'a' .charCodeAt (0 ) checkArray[index]++ } for (let char of t) { const index = char.charCodeAt (0 ) - 'a' .charCodeAt (0 ) checkArray[index]-- } for (let index in checkArray) { if (checkArray[index] !== 0 ) return false } return true };
383 赎金信 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var canConstruct = function (ransomNote, magazine ) { let checkArray = new Array (128 ).fill (0 ) for (char of ransomNote) { const index = char.charCodeAt (0 ) checkArray[index]++ } for (char of magazine) { const index = char.charCodeAt (0 ) checkArray[index]-- } for (index in checkArray) { if (checkArray[index] > 0 ) return false } return true };
49 字母异位词分组 map的键能够是任何类型
这里将经过排序后的字母异位词分组,起到了索引作用
题中用到了几个方法
Array.from(map.values)
将哈希表的值转成数组
map.get(key)
已知键,获得哈希表的值
map.set(key, value)
设置哈希表的键值
map.values()
获得哈希表值的迭代器,迭代器
效果就和let str of strs
里的 strs一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var groupAnagrams = function (strs ) { const map = new Map () for (let str of strs) { let array = str.split ('' ) let key = array.sort ().toString () console .log (key) let value = map.get (key) ? map.get (key) : new Array () value.push (str) map.set (key, value) } return Array .from (map.values ()) };
438 找到字符串中所有字母异位词 滑动窗口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 let checkStr = function (sArray, pArray ) { for (let i = 0 ; i < 26 ; i++) { if (sArray[i] !== pArray[i]) return false } return true } var findAnagrams = function (s, p ) { if (s.length < p.length ) return [] let sArray = new Array (26 ).fill (0 ) let pArray = new Array (26 ).fill (0 ) let num = [] let start = 0 , end = 0 for (let char of p) { const index = char.charCodeAt (0 ) - 'a' .charCodeAt (0 ) pArray[index]++ } while (start <= end && end < s.length ) { while (end + 1 - start < p.length ) { const index = s[end].charCodeAt (0 ) - 'a' .charCodeAt (0 ) sArray[index]++ end++ } if (end + 1 - start === p.length ) { let index = s[end].charCodeAt (0 ) - 'a' .charCodeAt (0 ) sArray[index]++ if (checkStr (sArray, pArray)) { num.push (start) } index = s[start].charCodeAt (0 ) - 'a' .charCodeAt (0 ) sArray[index]-- start++ end++ } } return num };
两个数组的交集 349 两个数组的交集 给定两个数组,返回交集
1 2 3 4 5 6 7 8 9 10 11 12 13 var intersection = function (nums1, nums2 ) { let nums1Map = new Set (nums1) let res = new Set () for (let num of nums2) { if (nums1Map.has (num)) res.add (num) } return Array .from (res) };
350 两个数组的交集 II 给定两个数组,返回两个数组中都出现的数,次数是俩数组中较少的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var intersect = function (nums1, nums2 ) { let res = new Map () let ans = [] for (let num1 of nums1) { if (res.has (num1)) { let value = res.get (num1) value++ res.set (num1, value) } else { res.set (num1, 1 ) } } for (let num2 of nums2) { let value = res.get (num2) if (res.has (num2)) { if (value > 0 ) { ans.push (num2) value-- res.set (num2, value) } } } return ans };
快乐数 202 快乐数 如果在过程中,得到了之前变过的数,就不是快乐数,用set存放变过的数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let trans = function (n ) { let sum = 0 while (n > 0 ) { let i = n % 10 sum += i * i n = Math .floor (n / 10 ) } return sum } var isHappy = function (n ) { let res = new Set ([n]) while (n !== 1 ){ n = trans (n) console .log (n) if (res.has (n)) return false else res.add (n) } return true };
两数之和 1 两数之和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var twoSum = function (nums, target ) { let map = new Map () for (let i = 0 ; i < nums.length ; i++) { let index = nums[i] let value = i let key = target - index if (map.has (key)) return [map.get (key), value] else map.set (index, value) } };
四数相加II 454 四数相加 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var fourSumCount = function (nums1, nums2, nums3, nums4 ) { let map = new Map () let res = 0 for (let a of nums1) { for (let b of nums2) { let value = a + b if (map.has (value)) map.set (value, map.get (value) + 1 ) else map.set (value, 1 ) } } for (let c of nums3) { for (let d of nums4) { let value = 0 - c - d if (map.has (value)) res += map.get (value) } } return res };
三数之和 15 三数之和 双指针,注意去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 var threeSum = function (nums ) { nums.sort ((a, b ) => a - b) let res = [] for (let i = 0 ; i < nums.length ; i++) { if (nums[i] > 0 ) return res if (i > 0 && nums[i] === nums[i - 1 ]) continue let left = i + 1 let right = nums.length - 1 while (left < right) { let sum = nums[left] + nums[right] let target = 0 - nums[i] if (sum > target) right-- else if (sum < target) left++ else { res.push ([nums[i], nums[left], nums[right]]) left++ while (left < right && nums[left] === nums[left - 1 ]) { left++ } right-- while (left < right && nums[right] === nums[right + 1 ]) { right-- } } } } return res };
四树之和 18 四数之和 同理,注意剪枝和去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 var fourSum = function (nums, target ) { nums.sort ((a, b ) => a - b) let res = [] for (let k = 0 ; k < nums.length ; k++) { if (nums[k] > target && target > 0 ) break if (k > 0 && nums[k] === nums[k - 1 ]) continue for (let i = k + 1 ; i < nums.length ; i++) { if (nums[i] + nums[k] > target && target > 0 ) break if (i > k + 1 && nums[i] === nums[i - 1 ]) continue let left = i + 1 let right = nums.length - 1 while (left < right) { let sum1 = nums[left] + nums[right] let sum2 = target - nums[k] - nums[i] if (sum1 > sum2) right-- else if (sum1 < sum2) left++ else { res.push ([nums[k], nums[i], nums[left], nums[right]]) left++ while (left < right && nums[left] === nums[left - 1 ]) { left++ } right-- while (left < right && nums[right] === nums[right + 1 ]) { right-- } } } } } return res };
字符串 反转字符串 344 反转字符串 双指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var reverseString = function (s ) { let start = 0 let end = s.length - 1 while (start < end) { let temp = s[start] s[start] = s[end] s[end] = temp start++ end-- } };
反转字符串II 541 反转字符串 只反转每2*k的k部分
最后如果要反转的小于k,就反转剩余
最后如果要反转的大于k小于2*k,就反转前k部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 let reverse = function (s, start, end ) { s = Array .from (s) while (end > start) { let temp = s[end] s[end] = s[start] s[start] = temp start++ end-- } s = s.join ('' ) return s } var reverseStr = function (s, k ) { for (let i = 0 ; i < s.length ; i += 2 * k) { if (i + k - 1 <= s.length - 1 ) { s = reverse (s, i , i + k - 1 ) continue } s = reverse (s, i, s.length - 1 ) } return s };
替换数字 不开额外数组,在原数组上进行改动
题目链接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const readline = require ('readline' )const rl = readline.createInterface ({ input : process.stdin , output : process.stdout }) function main () { const isAz = str => str >= 'a' && str <= 'z' const isNumber = str => str >= '0' && str <= '9' rl.on ('line' , line => { let n = 0 for (let i = 0 ; i < line.length ; i++) { let str = line[i] if (isAz (str)) n++ if (isNumber (str)) n += 6 } line = Array .from (line) let index = line.length - 1 line.length = n for (let i = n - 1 ; i >= 0 ; i--) { let str = line[index] if (isAz (str)) line[i] = str if (isNumber (str)) { line[i] = 'r' line[i - 1 ] = 'e' line[i - 2 ] = 'b' line[i - 3 ] = 'm' line[i - 4 ] = 'u' line[i - 5 ] = 'n' i -= 5 } index-- } console .log (line.join ('' )) }) } main ()
151 反转字符串的单词 思路
翻转整个字符串
去除多余空格
翻转字符串中的每个单词
利用双指针,打到时间复杂度O(n),空间复杂度O(1)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 const reverse = (s, start, end ) => { s = Array .from (s) end = end - 1 while (start < end) { let temp = s[start] s[start] = s[end] s[end]= temp start++ end-- } return s.join ('' ) } const removeSpace = s => { s = Array .from (s) let slow = 0 , fast = 0 while (fast < s.length ) { if (s[fast] === ' ' && (slow === 0 || s[slow - 1 ] === ' ' )) { fast++ } else { s[slow++] = s[fast++] } } s.length = s[slow - 1 ] === ' ' ? slow - 1 : slow return s.join ('' ) } var reverseWords = function (s ) { s = reverse (s, 0 , s.length ) s = removeSpace (s) let slow = 0 , fast = 0 while (fast < s.length ) { if (s[fast] === ' ' ) { s = reverse (s, slow, fast) slow = fast + 1 } fast++ } if (fast === s.length ) s = reverse (s, slow, fast) return s };
右旋转字符串 题目链接
思路:
先反转字符串,再反转前部分和后部分即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const readline = require ('readline' )const rl = readline.createInterface ({ input : process.stdin , output : process.stdout }) const reverse = (s, start, end ) => { s = Array .from (s) end-- while (start < end) { let temp = s[start] s[start] = s[end] s[end] = temp start++ end-- } return s.join ('' ) } const main = ( ) => { let input = [] rl.on ('line' , line => { input.push (line) }) rl.on ('close' , () => { let k = input[0 ] - '0' let s = input[1 ] s = reverse (s, 0 , s.length ) s = reverse (s, 0 , k) s = reverse (s, k, s.length ) console .log (s) }) } main ()
kmp子串匹配 28 找出字符串中第一个匹配的下标 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 const getNext = (s ) => { let next = new Array (s.length ).fill (0 ) let j = 0 , i = 1 next[j] = 0 for (let i = 1 ; i < s.length ; i++) { while (s[i] !== s[j] && j > 0 ) { j = next[j - 1 ] } if (s[i] === s[j]) { j++ } next[i] = j } return next } var strStr = function (haystack, needle ) { let next = getNext (needle) let i = 0 , j = 0 for (i = 0 ; i < haystack.length ; i++) { while (haystack[i] !== needle[j] && j > 0 ) { j = next[j - 1 ] } if (haystack[i] === needle[j]) { j++ } if (j === needle.length ) return i - needle.length + 1 } return -1 };
重复的子字符串 451 重复的子字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 const getNext = (s ) => { let next = Array (s.length ).fill (0 ) let j = 0 , i = 1 next[0 ] = 0 for (i = 1 ; i < s.length ; i++) { while (j > 0 && s[i] !== s[j]) { j = next[j - 1 ] } if (s[i] === s[j]) j++ next[i] = j } return next } const find = (s, son ) => { let next = getNext (son) let i = 0 , j = 0 for (i = 0 ; i < s.length ; i++) { while (s[i] !== son[j] && j > 0 ) { j = next[j - 1 ] } if (s[i] === son[j]) j++ if (j === son.length ) return true } return false } var repeatedSubstringPattern = function (s ) { let newS = s + s newS = newS.substring (1 , newS.length - 1 ) return find (newS, s) };
栈与队列 用栈模拟队列 232 用栈实现队列 思路
用两个栈模拟队列,一个进栈,一个出栈
执行入队列操作时,只从进栈入
执行出队列操作时,先将进栈的元素全部出到出栈,再对出栈进行pop
操作
执行看队头操作时,执行出队列操作,再将队头元素压入出栈,并返回队头元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 var MyQueue = function ( ) { this .stackIn = [] this .stackOut = [] }; MyQueue .prototype .push = function (x ) { this .stackIn .push (x) }; MyQueue .prototype .pop = function ( ) { let size = this .stackOut .length if (size) { return this .stackOut .pop () } else { while (this .stackIn .length ) { let x = this .stackIn .pop () this .stackOut .push (x) } return this .stackOut .pop () } }; MyQueue .prototype .peek = function ( ) { let x = this .pop () this .stackOut .push (x) return x }; MyQueue .prototype .empty = function ( ) { return !this .stackIn .length && !this .stackOut .length };
用队列模拟栈 225 用队列实现栈 思路
用一个队列模拟栈
执行出栈操作时,将前面 n - 1
个元素出队列,再进对列
将队首元素出队列即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 var MyStack = function ( ) { this .queue = [] }; MyStack .prototype .push = function (x ) { this .queue .push (x) }; MyStack .prototype .pop = function ( ) { let size = this .queue .length size-- while (size--) { let x = this .queue .shift () this .queue .push (x) } return this .queue .shift () }; MyStack .prototype .top = function ( ) { let x = this .pop () this .push (x) return x }; MyStack .prototype .empty = function ( ) { return !this .queue .length };
有效的括号 20 有效的括号 思路
遍历时,左括号全部进栈
遍历右括号时,匹配则出栈,不匹配或者栈为空则无效
最后栈为空有效,不为空无效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 const stackTop = (stack ) => { let x = stack.pop () stack.push (x) return x } const stackEmpty = (stack ) => !stack.length var isValid = function (s ) { if (s.length % 2 !== 0 ) return false let stack = [] for (let i = 0 ; i < s.length ; i++) { if (s[i] === '(' ) { stack.push (')' ) } else if (s[i] === '[' ) { stack.push (']' ) } else if (s[i] === '{' ) { stack.push ('}' ) } else { if (!stackEmpty (stack) && stackTop (stack) === s[i]) { console .log (stack) stack.pop () console .log (stack) } else { return false } } } return stackEmpty (stack) };
逆波兰表达式求值 150 逆波兰表达式求值 也是栈的一个经典应用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var evalRPN = function (tokens ) { let stack = [] for (let i = 0 ; i < tokens.length ; i++) { if (isNaN (tokens[i] - '0' )) { let num1 = stack.pop () let num2 = stack.pop () if (tokens[i] === '+' ) { stack.push (num2 + num1) } if (tokens[i] === '-' ) { stack.push (num2 - num1) } if (tokens[i] === '/' ) { stack.push (Math .trunc (num2 / num1)) } if (tokens[i] === '*' ) { stack.push (num2 * num1) } } else { stack.push (tokens[i] - '0' ) } } return stack.pop () };
滑动窗口的最大值 239 滑动窗口最大值 单调队列求解
思路
构建一个单调队列,队列有如下特点
队首到队尾单调递减
元素入队时如果队列中有小于自己的元素,直接剔除
元素出队时,只出队首的最大元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class MonoQueue { queue constructor () { this .queue = [] } Empty = () => !this .queue .length Back = () => this .queue [this .queue .length - 1 ] enqueue = (value ) => { while (!this .Empty () && value > this .Back ()) { this .queue .pop () } this .queue .push (value) } dequeue = (value ) => { if (this .Front () === value) this .queue .shift () } Front = () => this .queue [0 ] } var maxSlidingWindow = function (nums, k ) { let start = 0 let end = 0 let queue = new MonoQueue () let ans = [] while (end < k) { queue.enqueue (nums[end]) end++ } ans.push (queue.Front ()) while (end < nums.length ) { queue.enqueue (nums[end]) queue.dequeue (nums[start]) ans.push (queue.Front ()) start++ end++ } return ans };
前k个高频元素 347 前k个高频元素 解法1
利用哈希表map和排序
间复杂度O(nlogn) 空间复杂度O(n)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var topKFrequent = function (nums, k ) { let map = new Map () for (let i =0 ; i < nums.length ; i++) { let key = nums[i] map.set (key, (map.get (key) || 0 ) + 1 ) } let arr = [...map] arr.sort ((a,b ) => b[1 ] - a[1 ]) let ans = [] for (let i = 0 ; i < k; i++) { ans.push (arr[i][0 ]) } return ans };
解法2
小顶堆: 二叉树的父节点小于其子节点
大顶堆: 二叉树的父节点大于其子节点
使用优先级队列 ,手写堆实现优先级队列
先用哈希表map存储元素key和元素出现次数value
再利用小顶堆,将元素进k次,并将多余元素排除
最后全部将优先级队列剩下的k个元素全部出队即可得到结果
写堆时需要注意的地方
构造器的compare函数,使用传的参数compareFn
决定了是大顶堆还是小顶堆,大于0时才交换,小于0时不交换
当执行enqueue
操作时,将数据推入队列末尾,同时进行上浮 操作
上浮:二叉树里,当前元素和父元素对比,如果小(对于小顶堆而言)则进行上浮,否则则找到了对应的位置
当执行dequeue
操作时,保留队首元素并之后返回,并将队尾元素放到队首,进行下沉 操作,
下沉:二叉树里,当前元素和较小(小顶堆而言)子元素对比,如果大(对于小顶堆而言),则进行下沉,否则则找到了对应的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class Heap { constructor (compareFn) { this .compareFn = compareFn this .queue = [] } size = () => this .queue .length compare = (index1, index2 ) => { if (this .queue [index1] === undefined ) return -1 if (this .queue [index2] === undefined ) return -1 return this .compareFn (this .queue [index1], this .queue [index2]) } enqueue = (item ) => { this .queue .push (item) let index = this .size () - 1 let parent = Math .floor ((index - 1 ) / 2 ) while (parent >= 0 && this .compare (parent, index) > 0 ) { [this .queue [index], this .queue [parent]] = [this .queue [parent], this .queue [index]] index = parent parent = Math .floor ((index - 1 ) / 2 ) } } dequeue = () => { if (this .size () <= 1 ) { return this .queue .pop () } const outer = this .queue [0 ] this .queue [0 ] = this .queue .pop () let index = 0 let left = 2 * index + 1 let searchChild = this .compare (left, left + 1 ) > 0 ? left + 1 : left while (this .compare (index, searchChild) > 0 ) { [this .queue [index], this .queue [searchChild]] = [this .queue [searchChild], this .queue [index]] index = searchChild left = 2 * index + 1 searchChild = this .compare (left, left + 1 ) > 0 ? left + 1 : left } return outer } } var topKFrequent = function (nums, k ) { const map = new Map (); const res = []; for (const num of nums) { map.set (num, (map.get (num) || 0 ) + 1 ); } const heap = new Heap ((a, b ) => a.value - b.value ) for (const [key, value] of map) { heap.enqueue ({ key, value }); if (heap.size () > k) heap.dequeue (); } while (heap.size ()) res.push (heap.dequeue ().key ); return res; };
二叉树 二叉树的递归遍历 144 145 94 二叉树的前序、后序、中序遍历 1 2 3 4 5 6 7 8 9 10 11 const traversal = (node, nums ) => { if (node === null ) return nums.push (node.val ) traversal (node.left , nums) traversal (node.right , nums) } var preorderTraversal = function (root ) { let res = [] traversal (root, res) return res };
1 2 3 4 5 6 7 8 9 10 11 const traversal = (node, nums ) => { if (node === null ) return traversal (node.left , nums) nums.push (node.val ) traversal (node.right , nums) } var inorderTraversal = function (root ) { let res = [] traversal (root, res) return res };
1 2 3 4 5 6 7 8 9 10 11 const traversal = (node, nums ) => { if (node === null ) return traversal (node.left , nums) traversal (node.right , nums) nums.push (node.val ) } var inorderTraversal = function (root ) { let res = [] traversal (root, res) return res };
二叉树的迭代遍历 144 145 94先序遍历 中序遍历 后序遍历 先序遍历的思路
根节点结点进栈
栈不为空,则出栈,出栈元素录入结果
进出栈元素的右结点,再进左结点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var preorderTraversal = function (root ) { let res = [] if (!root) return res let stack = [root] const stackEmpty = stack => !stack.length while (!stackEmpty (stack)) { const cur = stack.pop () res.push (cur.val ) cur.right && stack.push (cur.right ) cur.left && stack.push (cur.left ) } return res };
后序遍历的思路
在先序遍历的基础上 变为左结点入栈后 再右节点入栈,然后再倒序数组即可
即 中左右 -> 中右左 -> 左右中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 const reverse = (res ) =>{ let start = 0 , end = res.length - 1 while (start < end) { [res[start], res[end]] = [res[end], res[start]] start++ end-- } return res } var postorderTraversal = function (root ) { let res = [] if (!root) return res let stack = [root] const stackEmpty = stack => !stack.length while (!stackEmpty (stack)) { const cur = stack.pop () res.push (cur.val ) cur.left && stack.push (cur.left ) cur.right && stack.push (cur.right ) } res = reverse (res) return res };
中序遍历的思路
根节点进栈,一路向左进栈
遇到为空结点,指针指向栈顶,栈顶元素进数组
指针指向栈顶元素右节点,返回步骤1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 var inorderTraversal = function (root ) { let res = [] let st = [] let cur = root while (cur !== null || st.length ) { if (cur !== null ) { st.push (cur) cur = cur.left } else { cur = st.pop () res.push (cur.val ) cur = cur.right } } return res };
二叉树的统一迭代法 144 145 94先序遍历 中序遍历 后序遍历 思路: 标记法
将访问的节点放入栈中,对要处理的节点放入栈中但是做标记
做标记的方法:先放一个空指针进栈,再放元素进栈
中序代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var inorderTraversal = function (root ) { const top = stack => stack[stack.length - 1 ] let res = [] let st = [] if (root) {st.push (root)} while (st.length ) { let node = top (st) if (node !== null ) { st.pop () if (node.right ) {st.push (node.right )} st.push (node) st.push (null ) if (node.left ) {st.push (node.left )} } else { st.pop () res.push (st.pop ().val ) } } return res };
前序代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var preorderTraversal = function (root ) { const top = stack => stack[stack.length - 1 ] let res = [] let st = [] if (root) {st.push (root)} while (st.length ) { let node = top (st) if (node !== null ) { st.pop () if (node.right ) {st.push (node.right )} if (node.left ) {st.push (node.left )} st.push (node) st.push (null ) } else { st.pop () res.push (st.pop ().val ) } } return res };
后序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 var postorderTraversal = function (root ) { const top = stack => stack[stack.length - 1 ] let res = [] let st = [] if (root) {st.push (root)} while (st.length ) { let node = top (st) if (node !== null ) { st.pop () st.push (node) st.push (null ) if (node.right ) {st.push (node.right )} if (node.left ) {st.push (node.left )} } else { st.pop () res.push (st.pop ().val ) } } return res };
二叉树的层序遍历 102 二叉树的层序遍历 队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 var levelOrder = function (root ) { let res = [] let queue = [] if (root) {queue.push (root)} while (queue.length ) { let len = queue.length let arr = [] while (len--) { let node = queue.shift () if (node) {arr.push (node.val )} if (node.left ) {queue.push (node.left )} if (node.right ) {queue.push (node.right )} } res.push (arr) } return res };