标签:duplicate 两数之和 结果 实现 之间 fas mat some 而不是
概念:算法与数据结构相辅相成
算法是为了解决某一个具体的问题,提出来的一个解法
数据结构是为了支撑这次解法,所提出的一种存储结构
1、两数之和(LeetCode1)
给定一个整数数组 nums
和一个目标值 target
,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9 因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]
题解:
/** * @param {number[]} nums * @param {number} target * @return {number[]} */ var twoSum = function(nums, target) { let obj = {} for(let i=0; i<nums.length; i++){ let temp = nums[i] if(!obj.hasOwnProperty(temp)){ // 这里也可以用 temp in obj 来判断 obj[target - temp] = i }else{ return [obj[temp], i] } } };
2、三数之和(LeetCode15)
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路:
/** * @param {number[]} nums * @return {number[][]} */ var threeSum = function(nums) { let ans = []; const len = nums.length; if(nums == null || len < 3) return ans; nums.sort((a, b) => a - b); // 排序 for (let i = 0; i < len ; i++) { if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环 if(i > 0 && nums[i] == nums[i-1]) continue; // 去重 let L = i+1; let R = len-1; while(L < R){ const sum = nums[i] + nums[L] + nums[R]; if(sum == 0){ ans.push([nums[i],nums[L],nums[R]]); while (L<R && nums[L] == nums[L+1]) L++; // 去重 while (L<R && nums[R] == nums[R-1]) R--; // 去重 L++; R--; } else if (sum < 0) L++; else if (sum > 0) R--; } } return ans; };
3、斐波那契数(LeetCode509)
通常用 F(n)
表示,形成的序列称为斐波那契数列。该数列由 0
和 1
开始,后面的每一项数字都是前面两项数字的和。也就是:
F(0) = 0, F(1) = 1
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.
给定 N
,计算 F(N)
。
/** * @param {number} N * @return {number} */ var fib = function(N) { if(N === 0 || N === 1){ return N } let cache = [] for(let i=0; i<=N; i++){ if( i == 0 || i == 1 ){ cache[i] = i }else{ cache[i] = cache[i-1] + cache[i-2] } } return cache[N] };
4、有效的括号(LeetCode20)
给定一个只包括 ‘(‘
,‘)‘
,‘{‘
,‘}‘
,‘[‘
,‘]‘
的字符串,判断字符串是否有效。
有效字符串需满足:
注意空字符串可被认为是有效字符串。
/** * @param {string} s * @return {boolean} */ var isValid = function(s) { let left = [‘(‘, ‘[‘, ‘{‘] let right = [‘)‘, ‘]‘, ‘}‘] let stack = [] let arr = s.split(‘‘) for(let i=0; i<arr.length; i++){ let temp = arr[i] if(left.indexOf(temp) !== -1){ stack.push(temp) }else{ if(left.indexOf(stack.pop()) === right.indexOf(temp)){ continue }else{ return false } } } if(stack.length === 0){ return true }else{ return false } };
5、反转链表(LeetCode206)
反转一个单链表。
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
代码如下
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @return {ListNode} */ var reverseList = function(head) { if(!head) return head let ele = { next: head } let cur = head prev = null while(cur){ let temp = cur.next cur.next = prev prev = cur cur = temp ele.next = prev } return ele.next };
6、环形链表(LeetCode141)
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
解法1:数组判重
环中两个节点相遇,说明在不同的时空内会存在相遇的那一刻,过去与未来相遇,自己和前世回眸,即是重复
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @return {boolean} */ var hasCycle = function(head) { let res = []; while(head != null){ if(res.includes(head)){ return true; }else{ res.push(head); } head = head.next; } return false; };
解法2:标记法
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @return {boolean} */ var hasCycle = function(head) { while(head && head.next){ if(head.flag){ return true; }else{ head.flag = 1; head = head.next; } } return false; };
解法3:双指针
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @return {boolean} */ var hasCycle = function(head) { if(!head || !head.next) return false; let fast = head.next; let slow = head; while(fast != slow){ if(!fast || !fast.next){ return false; } fast = fast.next.next; slow = slow.next; } return true; };
7、环形链表2(LeetCode142)
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @return {ListNode} */ var detectCycle = function(head) { // 链表中有环,slow和start重新出发,每次向前走一步 // 假设head到入环节点的距离为 a ,发生套圈时,慢节点走了 a + b,快节点走了 a + 2b + c // 则说明 2(a+b) = a+2b+c // 由此可得:a = c // 然后在此处,start节点从头出发,慢节点从被套圈的地方出发,这两个节点每次都各走一步 // 最后相遇点即是入环点 let fast = head, slow = head, start = head while(fast && fast.next){ fast = fast.next.next; slow = slow.next; if(fast == slow){ while(slow && start){ if(slow == start) return slow slow = slow.next start = start.next } } } return null };
8、相同的树(LeetCode100)
给定两个二叉树,编写一个函数来检验它们是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} p * @param {TreeNode} q * @return {boolean} */ var isSameTree = function(p, q) { // 如果p和q都是null,空二叉树,那么他们相等 if(p===null && q===null) return true if(p===null || q===null) return false if(p.val !== q.val) return false // p和q的值相等,需要递归判定左右子树 return isSameTree(p.left, q.left) && isSameTree(p.right, q.right) };
9、翻转二叉树(LeetCode226)
翻转一棵二叉树。
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {TreeNode} */ var invertTree = function(root) { // 终止条件 if(root === null){ return root } [root.left, root.right] = [invertTree(root.right), invertTree(root.left)] return root };
10、二叉树的前序遍历(LeetCode144)
给定一个二叉树,返回它的 前序 遍历。
方法1:递归
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {number[]} */ var preorderTraversal = function(root, arr=[]) { if(root){ // 前序遍历:先处理自己,再处理左右 arr.push(root.val) preorderTraversal(root.left, arr) preorderTraversal(root.right, arr) } return arr };
方法2:迭代
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {number[]} */ var preorderTraversal = function(root) { // 开始遍历,有一个stack存储 // left入栈,直到left为空 // 节点出站,右孩子为目标节点 let stack = [], cur = root, result = [] while(cur || stack.length>0){ while(cur){ stack.push(cur) // 后面要通过cur找他的right result.push(cur.val) cur = cur.left } cur = stack.pop() cur = cur.right } return result };
11、二叉树的中序遍历(LeetCode94)
给定一个二叉树,返回它的中序 遍历。
方法1:递归
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {number[]} */ var inorderTraversal = function(root, arr=[]) { if(root){ inorderTraversal(root.left, arr) arr.push(root.val) inorderTraversal(root.right, arr) } return arr };
方法2:迭代
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {number[]} */ var inorderTraversal = function(root) { let stack = [], arr = [], cur = root // cur为null时,如果stack内还有值,说明还需要继续遍历右子树 while(cur || stack.length>0){ if(cur){ stack.push(cur) cur = cur.left }else{ cur = stack.pop() arr.push(cur.val) cur = cur.right } } return arr };
12、二叉树的后序遍历(LeetCode145)
给定一个二叉树,返回它的 后序 遍历。
方法1:递归
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {number[]} */ var postorderTraversal = function(root, arr=[]) { if(root){ postorderTraversal(root.left, arr) postorderTraversal(root.right, arr) arr.push(root.val) } return arr };
方法2:迭代
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {number[]} */ var postorderTraversal = function(root) { let stack = [], arr = [], cur = root // 倒序解 // 每次先入左节点, 然后入右节点 // 每次把值都插到数组的最前面 while(cur || stack.length>0){ arr.unshift(cur.val) if(cur.left) stack.push(cur.left) if(cur.right) stack.push(cur.right) cur = stack.pop() } return arr };
13、验证二叉搜索树(LeetCode98)
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
一个二叉搜索树具有如下特征:
如果我们使用中序遍历,得到的结果一定是递增的
方法1:迭代
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {boolean} */ var isValidBST = function(root) { let stack = [], arr = [], cur = root, flag = true while(cur || stack.length>0){ if(cur){ stack.push(cur) cur = cur.left }else{ cur = stack.pop() arr.push(cur.val) cur = cur.right } } if(arr.length < 2) return flag arr.reduce((pre, cur)=>{ if(pre >= cur){ flag = false } return cur }) return flag };
方法2:递归
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {boolean} */ var isValidBST = function(root, min=null, max=null) { if(!root) return true if(min && root.val<=min.val){ return false } if(max && root.val>=max.val){ return false } return isValidBST(root.left, min, root) && isValidBST(root.right, root, max) };
14、二叉树的最大深度(LeetCode104)
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
叶子节点是指没有子节点的节点。
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @return {number} */ var maxDepth = function(root) { if(!root) return 0 return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1 };
15、二叉搜索树的最近公共祖先(LeetCode235)
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @param {TreeNode} p * @param {TreeNode} q * @return {TreeNode} */ // 由于是二叉搜索树, // 如果两个p和q的值都大于root的值,则递归root.right // 如果两个p和q的值都小于root的值,则递归root.left // 否则 root 就是最近公共祖先 var lowestCommonAncestor = function(root, p, q) { let root_val = root.val, p_val = p.val, q_val = q.val if(p_val>root_val && q_val>root_val){ return lowestCommonAncestor(root.right, p, q) }else if(p_val<root_val && q_val<root_val){ return lowestCommonAncestor(root.left, p, q) }else{ return root } };
16、二叉树的最近公共祖先(LeetCode236)
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
递归
/** * Definition for a binary tree node. * function TreeNode(val) { * this.val = val; * this.left = this.right = null; * } */ /** * @param {TreeNode} root * @param {TreeNode} p * @param {TreeNode} q * @return {TreeNode} */ var lowestCommonAncestor = function(root, p, q) { if(root == null || root == p || root == q){ return root; } let left = lowestCommonAncestor(root.left,p,q); let right = lowestCommonAncestor(root.right,p,q); if(left != null && right != null){ return root; } return left ? left : right; };
17、Pow(x, n)(LeetCode50)
实现 pow(x, n) ,即计算 x 的 n 次幂函数。
示例1:
输入: 2.00000, 10
输出: 1024.00000
示例2:
输入: 2.00000, -2 输出: 0.25000 解释: 2-2 = 1/22 = 1/4 = 0.25
代码:
/** * @param {number} x * @param {number} n * @return {number} */ var myPow = function(x, n) { if(n === 0) return 1 if(n<0) return 1 / myPow(x, -n) // 二分+递归的思想,n每次向右移动一位,如果该位上有值(二进制),则结果 * x // 每次让 x = x*x if(n%2 === 1){ return myPow(x*x, Math.floor(n/2)) * x }else{ return myPow(x*x, Math.floor(n/2)) } };
18、全排列(LeetCode46)
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
代码:
/** * @param {number[]} nums * @return {number[][]} */ var permute = function(nums) { let list = [] backtrack(list, [], nums) return list }; function backtrack(list, temp, nums){ // 终止条件 if(temp.length === nums.length){ return list.push([...temp]) } for(let i=0; i<nums.length; i++){ if(temp.includes(nums[i])) continue temp.push(nums[i]) backtrack(list, temp, nums) temp.pop() } }
19、x的平方根(LeetCode69)
实现 int sqrt(int x)
函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
示例1:
输入: 4
输出: 2
示例2:
输入: 8 输出: 2 说明: 8 的平方根是 2.82842..., 由于返回类型是整数,小数部分将被舍去。
代码:
/** * @param {number} x * @return {number} */ var mySqrt = function(x) { if(x===0 || x===1) return x let left = 1, right = x while(left<=right){ let middle = parseInt((left + right)/2) if(middle * middle === x){ return middle }else if(middle * middle > x){ right = middle - 1 }else{ left = middle + 1 } } return right };
20、单词搜索(LeetCode79)
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board = [ [‘A‘,‘B‘,‘C‘,‘E‘], [‘S‘,‘F‘,‘C‘,‘S‘], [‘A‘,‘D‘,‘E‘,‘E‘] ] 给定 word = "ABCCED", 返回 true. 给定 word = "SEE", 返回 true. 给定 word = "ABCB", 返回 false.
/** * @param {character[][]} board * @param {string} word * @return {boolean} */ var exist = function(board, word) { // 1、怎么找 // 2、什么时候终止 // 3、find内部,怎么找下一步(缓存存储中间的过程) if(board.length === 0) return false if(word.length === 0) return true const row = board.length const col = board[0].length // 怎么找 for(let i=0; i<row; i++){ for(let j=0; j<col; j++){ const ret = find(i,j,0) if(ret) return true } } return false // i,j是当前在矩阵中的位置,cur是单词word的第几个 function find(i, j, cur){ // 越界要停止 if(i>=row || i<0) return false if(j>=col || j<0) return false const letter = board[i][j] // 字母不匹配 if(letter !== word[cur]) return false // 找到最后一个了,而且相等 if(cur === word.length-1) return true // ---终止条件 board[i][j] = null // 当前路径标记为null,表示不可使用了 const ret = find(i+1,j,cur+1) || find(i-1,j,cur+1) || find(i,j+1,cur+1) || find(i,j-1,cur+1) board[i][j] = letter return ret } };
21、n皇后(LeetCode51)
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q‘
和 ‘.‘
分别代表了皇后和空位。
示例:
输入: 4 输出: [ [".Q..", // 解法 1 "...Q", "Q...", "..Q."], ["..Q.", // 解法 2 "Q...", "...Q", ".Q.."] ] 解释: 4 皇后问题存在两个不同的解法。
/** * @param {number} n * @return {string[][]} */ var solveNQueens = function(n) { // 索引特点:在0,1时 右边的斜线位置 1,2 2,3 :发现 行-列一样 // 在0,2时 左边的斜线位置 1,1 2,0 :发现 :行+列一样 // 1、行不能一样 按照行查找 // 2、列不能一样 // 3、行-列不能一样 // 4、行+列不能一样 // tmp为了记录之前的路径 // tmp的索引是行数据,值是列数据,摆放的棋子 // 例如:[2,4,1]代表 第一个摆放在2这个位置,第二列摆放在4,第三列摆放在1 let ret = [] // 查找第0行 find(0) return ret function find(row, tmp=[]){ if(row===n){ // 找完了 n-1就已经是最后一行了 tmp就是所有的摆放位置 ret.push(tmp.map(c=>{ let arr = new Array(n).fill(‘.‘) arr[c] = ‘Q‘ return arr.join(‘‘) })) } // 1、如何查找 for(let col=0; col<n; col++){ // 是不是不能放 let cantSet = tmp.some((ci,ri)=>{ // ci和ri是之前摆放棋子的行列索引 // col和row是当前所在位置的索引 return ci===col || (ri-ci)===(row-col) || (ri+ci)===(row+col) }) if(cantSet) continue // 如果能放,直接下一行 find(row+1, [...tmp, col]) } } };
22、删除排序数组中的重复项(LeetCode26)
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例1:
给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 你不需要考虑数组中超出新长度后面的元素。
示例2:
给定 nums = [0,0,1,1,1,2,2,3,3,4], 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。 你不需要考虑数组中超出新长度后面的元素。
由以上分析可知 符合题意的解题关键在于 确定最近一次重复元素第一次出现的位置 && 替换此位置上的元素为遍历的当前元素
代码:
/** * @param {number[]} nums * @return {number} */ var removeDuplicates = function(nums) { let j=0 const n = nums.length for(let i=1; i<n; i++){ if(nums[i]!==nums[i-1]){ j++ nums[j]=nums[i] } } return j+1 };
23、最大数(LeetCode179)
给定一组非负整数,重新排列它们的顺序使之组成一个最大的整数。
示例1:
输入: [10,2]
输出: 210
示例2:
输入: [3,30,34,5,9]
输出: 9534330
说明: 输出结果可能非常大,所以你需要返回一个字符串而不是整数。
代码:
/** * @param {number[]} nums * @return {string} */ var largestNumber = function(nums) { let arr = nums.sort((a,b)=>{ return `${b}${a}` - `${a}${b}` }) return arr[0] ? arr.join(‘‘) : ‘0‘ };
24、盛水最多的容器(LeetCode11)
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
代码:
/** * @param {number[]} height * @return {number} */ var maxArea = function(height) { let L = 0, // 左边界 R = height.length - 1, // 右边界 res = 0, // 最大值 lastHeight = 0; // 存放上一次的高度 while(L<R){ if(height[L]<height[R]){ // 以左边界为高 // 只考虑移动后高度增加的情况(移动后宽度肯定变小,若高度也变小,则面积是一定小于之前的) if(height[L]>lastHeight){ res = Math.max(res, (R-L)*height[L]) // 将最大值赋值给res lastHeight = height[L] // 将高度赋值给lastHeight } L++ }else{ // 以右边界为高 if(height[R]>lastHeight){ res = Math.max(res, (R-L)*height[R]) lastHeight = height[R] } R-- } } return res };
1
标签:duplicate 两数之和 结果 实现 之间 fas mat some 而不是
原文地址:https://www.cnblogs.com/haishen/p/12256279.html