Day1——栈与队列(简单) 剑指 Offer 09. 用两个栈实现队列 用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1)
示例
TIP 输入
1 2 3 >["CQueue","deleteHead","appendTail","appendTail","deleteHead","deleteHead"] >[[],[],[5],[2],[],[]]
输出: [null,-1,null,null,5,2]
题解 解题关键:了解栈和队列的区别,两个栈模拟,一个栈用于进,一个栈用于出
栈和队列的区别:
栈:先进后出(只能一端插入和删除)
队列:先进先出(只能一端插入,另一端删除)
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 CQueue = function (quene = [] ) { this .instack = [] this .outstack = [] }; CQueue.prototype.appendTail = function (value ) { this .instack.push(value) }; CQueue.prototype.deleteHead = function ( ) { if (!this .outstack.length){ if (!this .instack.length){ return -1 } else { while (this .instack.length){ this .outstack.push(this .instack.pop()) } } } return this .outstack.pop() };
剑指 Offer 30. 包含min函数的栈 示例
TIP 输入
1 2 3 >["MinStack","push","push","push","getMin","pop","top","getMin"] >[],[-2],[0],[-3],[],[],[],[]]
输出: [null,null,null,null,-3,null,0,-2] 解释: MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); –> 返回 -3. minStack.pop(); minStack.top(); –> 返回 0. minStack.getMin(); –> 返回 -2.
题解 解题关键:创建辅助栈minStack
, 与栈空间保持同步操作, 空间换时间
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 var MinStack = function ( ) { this .inStack = [] this .minStack = [Infinity ] }; MinStack.prototype.push = function (x ) { this .inStack.push(x) this .minStack.push(Math .min(this .minStack[this .minStack.length - 1 ], x)) }; MinStack.prototype.pop = function ( ) { this .inStack.pop() this .minStack.pop() }; MinStack.prototype.top = function ( ) { return this .inStack[this .inStack.length-1 ] }; MinStack.prototype.min = function ( ) { return this .minStack[this .minStack.length - 1 ] };
Day2——链表(简单) 剑指 Offer 06. 从尾到头打印链表 示例
TIP 输入
输出: [2,3,1]
题解 方法:定义一个数组,一边遍历一边往数组前面添加
1 2 3 4 5 6 7 8 9 var reversePrint = function (head ) { let number = []; let node = head while (node){ number.unshift(node.val) node = node.next } return number };
剑指 Offer 24. 反转链表 示例
TIP 输入
输出: 5->4->3->2->1->NULL
题解 方法:定义两个指针,一前一后,一边遍历一边翻转 注意点:注意后指针指向前指针时需要一个暂存指针指向下一次循环的指针
1 2 3 4 5 6 7 8 9 10 11 12 var reverseList = function (head ) { if (!head) return head let prev = null let cur = head while (cur){ let temp = cur.next cur.next = prev prev = cur cur = temp } return prev };
剑指 Offer 35. 复杂链表的复制 示例
TIP 输入
1 >head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]
题解 陷阱:复制的时候未必所有的节点都生成了新的,要避免指到旧的
方法:
hash表,第一次遍历存好最新的,遍历hash表设置random
遍历旧链表,复制一份到每个节点的后面(不赋值random),新节点的next指向原来旧节点的下一个节点,这样可以减少空间(空间复杂度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 48 var copyRandomList = function (head ) { if (!head) return null let map = new Map () let node = head; while (node){ map.set(node, new Node(node.val)) node = node.next } node = head while (node){ map.get(node).next = node.next ? map.get(node.next) : null map.get(node).random = node.random ? map.get(node.random) : null node = node.next } return map.get(head) } var copyRandomList = function (head ) { if (!head) return null let node = head; while (node){ let nodeNew = new Node(node.val, node.next) node.next = nodeNew node = nodeNew.next } node = head; while (node){ let nodeNew = node.next; nodeNew.random = node.random ? node.random.next : null ; } node = head const returnNode = node.next while (node){ nodeNew.next = nodeNew.next ? nodeNew.next.next : null node = node.next } return returnNode; }
Day3——字符串(简单) 剑指 Offer 05. 替换空格 示例
TIP 输入
输出:”We%20are%20happy.”
题解 直接调用现成方法即可
1 2 3 4 5 6 7 var replaceSpace = function (s ) { return s.replaceAll(' ' , '%20' ) };
剑指 Offer 58 - II. 左旋转字符串 示例
TIP 输入
输出: “cdefgab”
题解 方法一: 运用取余的特性去解 (遍历到的第i项 + 第n位开始翻转) / 字符串长度 就是对应位置
方法二: 直接切割成俩数组, 倒转拼接
1 2 3 4 5 6 7 8 9 10 11 var reverseLeftWords = function (s, n ) { return Array .from(s).map((_v,i )=> s[((i+n))%s.length]).join('' ) }; var reverseLeftWords = function (s, n ) { const left = s.slice(0 , n) const right = s.slice(n, s.length) return right + left };
Day4——查找算法(简单) Day4实在太简单了,稍微挑一个有技巧一点的
剑指 Offer 53 - II. 0~n-1中缺失的数字 示例
TIP 输入
输出:2
题解 二分查找法
左指针指向第一个,右指针指向最后一个,定义一个指针指向中间
如果number[middle] === middle
, 那么右边缺, 否则左边缺
当跳出循环的时候, 左指针指向右区间第一个元素,右指针指向左区间最后一个元素, 即返回左指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var missingNumber = function (nums ) { let i = 0 ; let j = nums.length - 1 while (i <= j){ let middle = Math .floor(i + (j - i) / 2 ) if (number[middle] === middle){ i = middle + 1 } else { j = middle - 1 } } return i };
Day5——查找算法(中等) 剑指 Offer 04. 二维数组中的查找 示例
TIP 输入
1 2 3 4 5 6 7 8 9 >二维数组 >[ [1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30] >] >target 5
输出: true
题解 技巧:从右上角 开始找,如果target小,找左边一列,target大,找下一行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 var findNumberIn2DArray = function (matrix, target ) { if (!matrix || !matrix.length || !matrix[0 ].length) { return false } var line = 0 ; var col = matrix[0 ].length - 1 while (line <= matrix.length && col >= 0 ){ if (target < matrix[line][col]){ col-- } else if (target > matrix[line][col]){ line++ } else { return true } } return false };
剑指 Offer 50. 第一个只出现一次的字符 示例
TIP 输入
输出:’b’
题解 关键: 两次遍历,第一次用hash表存字符出现的次数,第二次如果次数为1直接返回
1 2 3 4 5 6 7 8 9 10 11 12 var firstUniqChar = function (s ) { var map = new Map (); for (var i = 0 ;i < s.length;i++){ const time = map.get(s[i]) || 0 map.set(s[i], time + 1 ) } for (var i = 0 ;i < s.length;i++){ if (map.get(s[i]) === 1 ) return s[i] } return " " }
Day6——搜索与回溯算法(简单) 三个题目长得一样的,挑一个讲
剑指 Offer 32 - III. 从上到下打印二叉树 III 之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印
示例
TIP 输入
1 >[3,9,20,null,null,15,7]
输出:[ [3], [20,9], [15,7] ]
题解 关键点:
广度优先(需要一个队列)
一正一反,push结果的时候根据是否需要倒序输出决定用push还是unshift
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 levelOrder = function (root ) { if (!root) return []; var quene = [[root]]; var res = []; var isLeft = 1 ; while (quene.length){ const level = quene.shift(); const levelRes = []; const levelQuene = []; level.forEach(item => { if (item.left) { levelQuene.push(item.left) } if (item.right) { levelQuene.push(item.right) } if (isLeft % 2 ){ levelRes.push(item.val) }else { levelRes.unshift(item.val) } }) isLeft++ if (levelQuene.length){ quene.push(levelQuene) } if (levelRes.length){ res.push(levelRes) } } return res }
Day7——搜索与回溯算法二(简单) 剑指 Offer 26. 树的子结构(判断子树) 示例 例如: 给定的树 A:
3
/ \
4 5
/ \
1 2
给定的树 B:
4
/
1
返回 true,因为 B 与 A 的一个子树拥有相同的结构和节点值。
TIP 输入
输出:false
题解 解题关键:
深度遍历父树,遇到val相同的,就有可能是子树,依次比较父树和子树的每个节点
递归判断相等时,如果B没有了,说明已经匹配完成了,为true。如果A没有了,说明越过A树,为false,如果val值不一样,那么匹配失败,false。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 var isSubStructure = function (A, B ) { if (!A || !B) return false ; return isSameTree(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B) } function isSameTree (A, B ) { if (!B){ return true } if (!A){ return false } if (A.val !== B.val){ return false } return isSameTree(A.left, B) && isSameTree(A.right, B) }
剑指 Offer 27. 二叉树的镜像 示例 请完成一个函数,输入一个二叉树,该函数输出它的镜像。
TIP 输入
输出: [4,7,2,9,6,3,1]
题解 解题思路:
交换left和right,递归交换
1 2 3 4 5 6 7 8 9 10 11 12 13 var mirrorTree = function (root ) { if (!root) return null ; var temp = root.left; root.left = root.right; root.right = temp; if (root.left){ mirrorTree(root.left) } if (root.right){ mirrorTree(root.right) } return root; }
剑指 Offer 28. 对称的二叉树 示例 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。 1
/ \
2 2
/ \ / \
3 4 4 3
TIP 输入
输出: true
题解 解题思路:
用两个指针,一根往左走,另一根就往右走。一根往右走,另一根就往左走。
1 2 3 4 5 6 7 8 9 10 11 12 13 var isSymmetric = function (root ) { return isMirror(root, root) } function isMirror (p, q ) { if (!p && !q){ return true } if (!p || !q){ return false } return p.val === q.val && isMirror(p.left, q.right) && isMirror(p.right, q.left) }
Day8——动态规划(简单) 剑指 Offer 10- I. 斐波那契数列 示例 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下:
F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
TIP 输入
输出:1
题解 不要用递归(暴掉的概率太大了)
提供两种解法
滚动数组解法
1 2 3 4 5 6 7 8 9 10 11 12 13 var fib = function (n ) { const mod = 1000000007 ; if (n < 2 ){ return n } let p = 0 , q = 1 , r = 1 ; for (var i = 2 ;i < n;i++){ p = q; q = r; r =(p + q) % mod; } return r; }
矩阵快速幂
2.1 快速幂 O(logn) 的复杂度
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 mutiply = (a, b ) => { var c = new Array (2 ).fill(0 ).map(() => new Array (2 ).fill(0 )) for (var i = 0 ;i < 2 ; i++){ for (var j = 0 ;j < 2 ;j++){ c[i][j] = (BigInt(a[i][0 ]) * BigInt(b[0 ][j])) + (BigInt(a[i][1 ]) * BigInt(b[1 ][j])) } } return c; } var pow = (a, n ) => { var ret = [[1 , 0 ], [0 , 1 ]] while (n > 0 ){ if ((n & 1 ) === 1 ){ ret = mutiply(ret, a) } n >> 1 a = mutiply(a, a) } } var fib = function (n ) { if (n < 2 ) { return n; } const mod = 1000000007 ; const m = [[1 , 1 ], [1 , 0 ]] const res = pow(m, n - 1 ) return BigInt(res[0 ][0 ]) % BigInt(mod) }
剑指 Offer 10- II. 青蛙跳台阶问题 示例 一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
TIP 输入
输出:2
题解 和上面的斐波那契数列一毛一样,唯一的区别就是F(0)和F(1)的初始值不一样
1 2 3 4 5 6 7 8 var numWays = function (n ) { if (n < 2 ) return 1 ; const m = [[1 , 1 ], [1 , 0 ]]; const res = pow(m, n - 1 ); const ways = (res[0 ][0 ] * 1 + res[0 ][0 ] * 1 ) % mod; return ways; }
剑指 Offer 63. 股票的最大利润 示例 假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
TIP 输入
输出: 5
题解 方法:
遍历每一天,每一天都在幻想买的是最低点,卖的是最高点即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 var maxProfit = function (prices ) { if (!prices.length) return 0 ; var minPrice = price[0 ]; var maxEarn = 0 ; for (var i = 0 ; i < prices.length;i++){ if (prices[i] < minPrice){ minPrice = prices[i] } if (prices[i] - minPrice > maxEarn){ maxEarn = prices[i] - minPrice } } return maxEarn > 0 ? maxEarn : 0 }
Day9——动态规划(中等) 剑指 Offer 42. 连续子数组的最大和 示例
TIP 输入
1 >nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6 解释:连续子数组 [4,-1,2,1] 的和最大,为 6。
题解 提供两种解法,均比较重要
动态规划, 滚动数组
Max{F(n - 1) + nums[i], nums[i]}
1 2 3 4 5 6 7 8 9 10 11 var maxSubArray = function (nums ) { if (!nums.length) return 0 ; var q = nums[0 ], max = nums[0 ] for (var i = 1 ; i < nums.length;i++){ q = Math .max(q + nums[i], nums[i]); max = Math .max(q, nums[i]) } return max; }
分治算法
分治算法就是将一个复杂的问题分成几个相似或者相同的小问题,再将小问题合并。
本题思路:
分成多段,分别找出其中连续子数组中的最大和
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 function Status (l, r, m, i ) { this .lSum = l this .rSum = r this .mSum = m this .iSum = i } function mergeArr (aStatus, bStatus ) { const l = Math .max(aStatus.lSum, bStatus.lSum + aStatus.iSum) const r = Math .max(aStatus.rSum + bStatus.iSum, bStatus.rSum) const m = Math .max(aStatus.mSum, bStatus.mSum, aStatus.rSum + bStatus.lSum) const i = aStatus.iSum + bStatus.iSum return new Status(l, r, m, i) } function getInfo (arr, start, end ) { if (start === end){ return new Status(arr[start], arr[start], arr[start], arr[start]) } const middle = (start + end) >> 1 ; const lStatus = getInfo(arr, start, middle) const rStatus = getInfo(arr, middle + 1 , end) return mergeArr(lStatus, rStatus) } var maxSubArray = function (nums ) { return getInfo(nums, 0 , nums.length - 1 ).mSum }
剑指 Offer 47. 礼物的最大价值 示例 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
TIP 输入
1 2 3 4 5 >[ [1,3,1], [1,5,1], [4,2,1] >]
输出: 12 解释:路径 1→3→5→2→1 可以拿到最多价值的礼物
题解 动态规划题,设置dp数组,这个数组存着走到那一步能获取到的最大价值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var maxValue = function (grid ) { var line = grid.length; var column = grid[0 ].length; const dp = new Array (line).fill(0 ).map(() => new Array (column).fill(0 )) dp[0 ][0 ] = grid[0 ][0 ] for (var i = 0 ;i < line;i++){ dp[i][0 ] = dp[i - 1 ][0 ] + grid[i][0 ] } for (var j = 0 ; j < column;j++){ dp[0 ][j] = dp[0 ][j - 1 ] + grid[0 ][j - 1 ] } for (var i = 1 ; i < line;i++){ for (var j = 1 ;j < column;j++){ dp[i][j] = Math .max(dp[i - 1 ][j], dp[i][j - 1 ]) + grid[i][j] } } return dp[line - 1 ][column - 1 ] }
Day10——动态规划二(中等) 剑指 Offer 46. 把数字翻译成字符串 示例
TIP 输入
输出:5 解释: 12258有5种不同的翻译,分别是”bccfi”, “bwfi”, “bczi”, “mcfi”和”mzi”
题解 动态规划,滚动数组,条件判断(类似青蛙跳台阶)
F(n) = F(n - 1)
(如果最后两个数无法构成一个字母)
F(n) = F(n - 1) + F(n - 2)
(如果最后两个数可以构成一个字母)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var translateNum = function (num ) { let stringNum = String (num) let p = 0 , q = 1 , ways = 1 ; for (var i = 0 ; i < stringNum.length;i++){ p = q; q = ways; ways = 0 ; ways += q; let lastSecondNum = stringNum[i - 1 ] + stringNum[i] if (Number (lastSecondNum) >= 10 || (Number (lastSecondNum) <= 25 ){ ways += p } } return ways; }
剑指 Offer 48. 最长不含重复字符的子字符串 示例
TIP 输入
输出: 3 解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
题解
滑动窗口(双指针) + 哈希表
1 2 3 4 5 6 7 8 9 10 11 12 13 var lengthOfLongestSubstring = function (s ) { if (!s.length) return 0 ; let left = -1 , res = 0 , hashMap = new Map (); for (var i = 0 ; i < s.length; i++){ if (hashMap.has(s[i])){ const index = hashMap.get(s[i]) left = Math .max(index, left) } hashMap.set(s[i], i) res = Math .max(res, i - left) } return res; }
Day11——双指针(简单) 剑指 Offer 18. 删除链表的节点 示例
TIP 输入
1 >head = [4,5,1,9], val = 5
输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
题解 建一个空的头结点,定义两个指针,一个先走一步,一个后走
注意点:head
也要指向空节点,因为头结点也有可能被删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var deleteNode = function (head, val ) { const empty = new ListNode() empty.next = head; let prev = empty; let node = empty; head = empty while (node){ if (node.val === val){ prev.next = node.next } prev = node; node = node.next } return head.next }
剑指 Offer 22. 链表中倒数第k个节点 示例
TIP 输入
1 >给定一个链表: 1->2->3->4->5, 和 k = 2.
输出: 返回链表 4->5.
题解 解题思路:双指针,前一个指针先走k步,第二根指针开始走,第一个指针走完的时候第二个指针走到第k个元素
1 2 3 4 5 6 7 8 9 10 11 12 13 var getKthFromEnd = function (head, k ) { var front = head; var back = head; var i = 1 ; while (front){ if (i > k){ back = back.next } front = front.next i++ } return back; }
Day12——双指针二(简单) 剑指 Offer 25. 合并两个排序的链表 示例
TIP 输入
输出:1->1->2->3->4->4
题解 一根新链表加两个指针
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 var mergeTwoLists = function (l1, l2 ) { let node1 = l1; let node2 = l2; const newHead = new ListNode(); let newNode = newHead; while (node1 && node2){ if (node2.val <= node1.val){ newNode.next = node2 node2 = node2.next; } else { newNode.next = node1; node1 = node1.next } newNode = newNode.next } newNode.next = node1 ? node1 : node2 return newHead.next };
剑指 Offer 52. 两个链表的第一个公共节点 示例
TIP 输入 输出:Reference of the node with value = 8 输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
题解 双指针,一根走完了指向另一根的头部, 相交说明有公共节点,都为null说明没有。类似于找环状链表
1 2 3 4 5 6 7 8 9 10 11 12 var getIntersectionNode = function (headA, headB ) { if (headA === null || headB === null ) { return null ; } var nodeA = headA; var nodeB = headB; while (nodeA !== nodeB){ nodeA = nodeA ? nodeA.next : headB; nodeB = nodeB ? nodeB.next : headA; } return nodeA };
Day13——双指针三(简单) 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 示例
TIP 输入
输出:[1,3,2,4]
题解 双指针,一根指向头,一根指向尾部,同时往中间移动
头部指针移动到第一个偶数位置停止
尾部指针移动到第一个奇数位置停止
1 2 3 4 5 6 7 8 9 10 11 12 var exchange = function (nums ) { if (!nums.length) return []; let i = 0 , j = nums.length - 1 ; while (i < j){ while (i < j && (nums[i] & 1 ) === 1 ) i++; while (i < j && (nums[i] & 1 ) === 0 ) j--; let temp = nums[i]; nums[i] = nums[j] nums[j] = temp } return nums; }
剑指 Offer 57. 和为s的两个数字 示例
TIP 输入
1 >nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]
题解 头尾指针和小,头指针往后,和大,尾指针往前
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var twoSum = function (nums, target ) { let i = 0 ; j = nums.length - 1 while (i < j){ if (nums[i] + nums[j] === target) { return [nums[i], nums[j]] } if (nums[i] + nums[j] > target){ j-- } if (nums[i] + nums[j] < target){ i++ } } return [] };
剑指 Offer 58 - I. 翻转单词顺序 示例
TIP 输入
输出:”world! hello” 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
题解 1 2 3 4 5 6 7 8 9 10 11 12 var reverseWords = function (s ) { const wordArray = s.trim().split(' ' ).filter(item => item !== '' ) let i = 0 , j = wordArray.length; while (i < j){ let temp = wordArray[i]; wordArray[i] = wordArray[j]; wordArray[j] = temp i++; j--; } return wordArray.join(' ' ).trim() };
Day14——搜索与回溯算法(中等) 剑指 Offer 12. 矩阵中的路径 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
例如,在下面的 3×4 的矩阵中包含单词 “ABCCED”(单词中的字母已标出)。
示例
TIP 输入
1 >board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
题解 回溯思想: 类似于在一个节点的枚举过程,不满足条件就回到那个节点
本题思路:
从盘中任意一个节点开始走,因此是一个二重循环,需要考虑每一个节点,有一个节点满足则返回true
对每一个节点进行枚举,不满足条件则回溯,满足条件的标记需要标记已经访问过了,避免再次访问
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 exist = function (board, word ) { const line = board.length; const column = board[0 ].length; let flag = false ; const visited = new Array (line).fill(false ).map(() => new Array (column).fill(false )) const checkWords = (i, j, _word, k ) => { const directions = [[1 , 0 ], [0 , 1 ], [-1 , 0 ], [0 , -1 ]] if (board[i][j] !== word.charAt(k)){ return false } if (k === _word.length - 1 ){ return true } visited[i][j] = true ; let result = false ; for (var [dx, dy] of directions){ const newX = i + dx; const newY = j + dy; if (newX >= 0 && newY >= 0 && newX <= line && newY <= column){ if (!visited[newX][newY]){ const res = checkWords(newX, newY, _word, k + 1 ) if (res){ result = true ; break ; } } } } return result; } for (var i = 0 ; i < line;i++){ for (var j = 0 ;j < column;j++){ flag = checkWords(i, j, word, 0 ) if (flag){ return true } } } }
剑指 Offer 13. 机器人的运动范围 示例 地上有一个m行n列的方格,从坐标 [0,0]
到坐标 [m-1,n-1]
。一个机器人从坐标 [0, 0]
的格子开始移动,它每次可以向左、右、上、下 移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37]
,因为3+5+3+7=18
。但它不能进入方格 [35, 38]
,因为3+5+3+8=19
。请问该机器人能够到达多少个格子?
TIP 输入
输出:3
题解 是一个深度优先遍历或者广度优先遍历的题
深度优先遍历:相当于暴力的算法,一个方向走到底,然后回溯到节点再走
广度优先遍历:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var movingCount = function (m, n, k ) { const visited = new Array (m).fill(0 ).map(() => new Array (n).fill(false )) const calc = (val ) => { let sum = 0 ; while (x !== 0 ){ sum += x % 10 ; x = Math .floor(x / 10 ); } return sum } const walk = (visit, m, n, i, j, k ) => { const sum = calc(i) + calc(j); if (sum > k || visit[i][j] || i > m || j > n) return 0 ; visit[i][j] = true ; return 1 + walk(visit, m, n, i + 1 , j, k) + walk(visit, m, n, i, j + 1 , k) } return walk(visited, m, n, 0 , 0 , k) }