二叉树 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
进阶: 递归算法很简单,你可以通过迭代算法完成吗?
提示:
树中节点数目在范围 [0, 100] 内
100 <= Node.val <= 100
示例:

1 2 输入:root = [1,null,2,3] 输出:[1,3,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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class Solution { public List<Integer> inorderTraversal (TreeNode root) { List<Integer> res = new ArrayList <>(); if (root == null ) { return res; } TreeNode current = root; while (current != null ) { if (current.left == null ) { res.add(current.val); current = current.right; } else { TreeNode predecessor = current.left; while (predecessor.right != null && predecessor.right != current) { predecessor = predecessor.right; } if (predecessor.right == null ) { predecessor.right = current; current = current.left; } else { predecessor.right = null ; res.add(current.val); current = current.right; } } } return res; } }
给定一个二叉树 root ,返回其最大深度。
二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
提示:
树中节点的数量在 [0, 104] 区间内。
100 <= Node.val <= 100
示例:

1 2 输入:root = [3,9,20,null,null,15,7] 输出:3
代码:
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 class Solution { public int maxDepth (TreeNode root) { if (root == null ) { return 0 ; } else { int leftHeight = maxDepth(root.left); int rightHeight = maxDepth(root.right); return Math.max(leftHeight, rightHeight) + 1 ; } } } class Solution { public int maxDepth (TreeNode root) { if (root == null ) { return 0 ; } Queue<TreeNode> queue = new LinkedList <TreeNode>(); queue.offer(root); int ans = 0 ; while (!queue.isEmpty()) { int size = queue.size(); while (size > 0 ) { TreeNode node = queue.poll(); if (node.left != null ) { queue.offer(node.left); } if (node.right != null ) { queue.offer(node.right); } size--; } ans++; } return ans; } }
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。
提示:
树中的节点数为 n 。
1 <= k <= n <= 104
0 <= Node.val <= 104
示例:
1 2 输入:root = [3,1,4,null,2], k = 1 输出:1
1 2 输入:root = [5,3,6,2,4,null,null,1], k = 3 输出:3
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public int kthSmallest (TreeNode root, int k) { Deque<TreeNode> stack = new ArrayDeque <TreeNode>(); while (root != null || !stack.isEmpty()) { while (root != null ) { stack.push(root); root = root.left; } root = stack.pop(); --k; if (k == 0 ) { break ; } root = root.right; } return root.val; }
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
提示:
树中节点数目范围在 [0, 100] 内
-100 <= Node.val <= 100
示例 1:
输入: root = [4,2,7,1,3,6,9]
输出: [4,7,2,9,6,3,1]
示例 2:
输入: root = [2,1,3]
输出: [2,3,1]
示例 3:
输入: root = []
输出: []
代码:
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 class Solution { public TreeNode invertTree (TreeNode root) { if (root == null ) { return null ; } TreeNode left = invertTree(root.left); TreeNode right = invertTree(root.right); root.left = right; root.right = left; return root; } }
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
提示 :
树中节点数目在范围 [0, 2000] 内
-1000 <= Node.val <= 1000
示例:
1 2 3 4 5 6 7 8 9 10 输入:root = [3,9,20,null,null,15,7] 输出:[[3],[9,20],[15,7]] 示例 2: 输入:root = [1] 输出:[[1]] 示例 3: 输入:root = [] 输出:[]
代码:
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 public List<List<Integer>> levelOrder (TreeNode root) { List<List<Integer>> ret = new ArrayList <List<Integer>>(); if (root == null ) { return ret; } Queue<TreeNode> queue = new LinkedList <TreeNode>(); queue.offer(root); while (!queue.isEmpty()) { List<Integer> level = new ArrayList <Integer>(); int currentLevelSize = queue.size(); for (int i = 1 ; i <= currentLevelSize; ++i) { TreeNode node = queue.poll(); level.add(node.val); if (node.left != null ) { queue.offer(node.left); } if (node.right != null ) { queue.offer(node.right); } } ret.add(level); } return ret; }
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
提示:
树中节点数目范围在 [1, 104] 内
-231 <= Node.val <= 231 - 1
示例 1:
1 2 输入:root = [2,1,3] 输出:true
示例 2:
1 2 3 输入:root = [5,1,4,null,null,3,6] 输出:false 解释:根节点的值是 5 ,但是右子节点的值是 4 。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 public boolean isValidBST (TreeNode root) { return isValidBST(root, Long.MIN_VALUE, Long.MAX_VALUE); } private boolean isValidBST (TreeNode root, long min, long max) { if (root == null ) { return true ; } if (root.val <= min || root.val >= max) { return false ; } return isValidBST(root.left, min, root.val) && isValidBST(root.right, root.val, max); }
有效 二叉搜索树定义如下:
节点的左子树只包含** 小于 **当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
提示:
树中节点数目在范围 [1, 1000] 内
-100 <= Node.val <= 100
进阶: 你可以运用递归和迭代两种方法解决这个问题吗?
示例 1:
输入: root = [1,2,2,3,4,4,3]
输出: true
示例 2:
输入: root = [1,2,2,null,3,null,3]
输出: false
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public boolean isSymmetric (TreeNode root) { return root == null || rece(root.left, root.right); } private boolean rece (TreeNode L, TreeNode R) { if (L == null && R == null ) return true ; if (L == null || R== null || L.val != R.val) return false ; return rece(L.right, R.left) && rece(L.left, R.right); } }
给你一棵二叉树的根节点,返回该树的 直径 。
二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。
两节点之间路径的 长度 由它们之间边数表示。
提示:
树中节点数目在范围 [1, 10<sup>4</sup>] 内
-100 <= Node.val <= 100
示例 1:
输入: root = [1,2,3,4,5]
输出: 3
解释: 3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。
示例 2:
输入: root = [1,2]
输出: 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 class Solution { int ans = 0 ; public int diameterOfBinaryTree (TreeNode root) { ans = 1 ; dfs(root); return ans - 1 ; } private int dfs (TreeNode root) { if (root == null ) { return 0 ; } int l = dfs(root.left); int r = dfs(root.right); ans = Math.max(ans, l+r+1 ); return Math.max(l, r) + 1 ; } }
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。
提示:
1 <= nums.length <= 10<sup>4</sup>
-10<sup>4</sup> <= nums[i] <= 10<sup>4</sup>
nums 按 严格递增 顺序排列
示例 1:
输入: nums = [-10,-3,0,5,9]
输出: [0,-3,9,-10,null,5]
解释: [0,-10,5,null,-3,null,9] 也将被视为正确答案:
示例 2:
输入: nums = [1,3]
输出: [3,1]
解释: [1,null,3] 和 [3,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 class Solution { Random rand = new Random (); public TreeNode sortedArrayToBST (int [] nums) { return helper(nums, 0 , nums.length - 1 ); } public TreeNode helper (int [] nums, int left, int right) { if (left > right) { return null ; } int mid = (left + right + rand.nextInt(2 )) / 2 ; TreeNode root = new TreeNode (nums[mid]); root.left = helper(nums, left, mid - 1 ); root.right = helper(nums, mid + 1 , right); return root; } }
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
提示:
二叉树的节点个数的范围是 [0,100]
-100 <= Node.val <= 100
示例 1:
输入: root = [1,2,3,null,5,null,4]
输出: [1,3,4]
解释:
示例 2:
输入: root = [1,2,3,4,null,null,null,5]
输出: [1,3,4,5]
解释:
示例 3:
输入: root = [1,null,3]
输出: [1,3]
示例 4:
输入: root = []
输出: []
代码 :
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 class Solution { public List<Integer> rightSideView (TreeNode root) { Map<Integer, Integer> rightmostValueAtDepth = new HashMap <Integer, Integer>(); int max_depth = -1 ; Deque<TreeNode> nodeStack = new LinkedList <TreeNode>(); Deque<Integer> depthStack = new LinkedList <Integer>(); nodeStack.push(root); depthStack.push(0 ); while (!nodeStack.isEmpty()) { TreeNode node = nodeStack.pop(); int depth = depthStack.pop(); if (node != null ) { max_depth = Math.max(max_depth, depth); if (!rightmostValueAtDepth.containsKey(depth)) { rightmostValueAtDepth.put(depth, node.val); } nodeStack.push(node.left); nodeStack.push(node.right); depthStack.push(depth + 1 ); depthStack.push(depth + 1 ); } } List<Integer> rightView = new ArrayList <Integer>(); for (int depth = 0 ; depth <= max_depth; depth++) { rightView.add(rightmostValueAtDepth.get(depth)); } return rightView; } }
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
提示:
树中结点数在范围 [0, 2000] 内
-100 <= Node.val <= 100
进阶: 你可以使用原地算法(O(1) 额外空间)展开这棵树吗?
示例 1:
输入: root = [1,2,5,3,4,null,6]
输出: [1,null,2,null,3,null,4,null,5,null,6]
示例 2:
输入: root = []
输出: []
示例 3:
输入: root = [0]
输出: [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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 class Solution { public void flatten (TreeNode root) { List<TreeNode> list = new ArrayList <TreeNode>(); preorderTraversal(root, list); int size = list.size(); for (int i = 1 ; i < size; i++) { TreeNode prev = list.get(i - 1 ), curr = list.get(i); prev.left = null ; prev.right = curr; } } public void preorderTraversal (TreeNode root, List<TreeNode> list) { if (root != null ) { list.add(root); preorderTraversal(root.left, list); preorderTraversal(root.right, list); } } } class Solution { public void flatten (TreeNode root) { List<TreeNode> list = new ArrayList <TreeNode>(); Deque<TreeNode> stack = new LinkedList <TreeNode>(); TreeNode node = root; while (node != null || !stack.isEmpty()) { while (node != null ) { list.add(node); stack.push(node); node = node.left; } node = stack.pop(); node = node.right; } int size = list.size(); for (int i = 1 ; i < size; i++) { TreeNode prev = list.get(i - 1 ), curr = list.get(i); prev.left = null ; prev.right = curr; } } }
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的 先序遍历 , inorder 是同一棵树的 中序遍历 ,请构造二叉树并返回其根节点。
提示:
1 <= preorder.length <= 3000
inorder.length == preorder.length
-3000 <= preorder[i], inorder[i] <= 3000
preorder 和 inorder 均 无重复 元素
inorder 均出现在 preorder
preorder 保证 为二叉树的前序遍历序列
inorder 保证 为二叉树的中序遍历序列
示例 1:
输入 : preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1]
输出: [-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 class Solution { private Map<Integer, Integer> indexMap; public TreeNode myBuildTree (int [] preorder, int [] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) { if (preorder_left > preorder_right) { return null ; } int preorder_root = preorder_left; int inorder_root = indexMap.get(preorder[preorder_root]); TreeNode root = new TreeNode (preorder[preorder_root]); int size_left_subtree = inorder_root - inorder_left; root.left = myBuildTree(preorder, inorder, preorder_left + 1 , preorder_left + size_left_subtree, inorder_left, inorder_root - 1 ); root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1 , preorder_right, inorder_root + 1 , inorder_right); return root; } public TreeNode buildTree (int [] preorder, int [] inorder) { int n = preorder.length; indexMap = new HashMap <Integer, Integer>(); for (int i = 0 ; i < n; i++) { indexMap.put(inorder[i], i); } return myBuildTree(preorder, inorder, 0 , n - 1 , 0 , n - 1 ); } }
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
提示:
二叉树的节点个数的范围是 [0,1000]
-10<sup>9</sup> <= Node.val <= 10<sup>9</sup>
-1000 <= targetSum <= 1000
示例 1:
输入: root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出: 3
解释: 和等于 8 的路径有 3 条,如图所示。
示例 2:
输入: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出: 3
代码 :
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 class Solution { public int pathSum (TreeNode root, long targetSum) { if (root == null ) { return 0 ; } int ret = rootSum(root, targetSum); ret += pathSum(root.left, targetSum); ret += pathSum(root.right, targetSum); return ret; } public int rootSum (TreeNode root, long targetSum) { int ret = 0 ; if (root == null ) { return 0 ; } int val = root.val; if (val == targetSum) { ret++; } ret += rootSum(root.left, targetSum - val); ret += rootSum(root.right, targetSum - val); return ret; } } class Solution { public int pathSum (TreeNode root, int targetSum) { Map<Long, Integer> prefix = new HashMap <Long, Integer>(); prefix.put(0L , 1 ); return dfs(root, prefix, 0 , targetSum); } public int dfs (TreeNode root, Map<Long, Integer> prefix, long curr, int targetSum) { if (root == null ) { return 0 ; } int ret = 0 ; curr += root.val; ret = prefix.getOrDefault(curr - targetSum, 0 ); prefix.put(curr, prefix.getOrDefault(curr, 0 ) + 1 ); ret += dfs(root.left, prefix, curr, targetSum); ret += dfs(root.right, prefix, curr, targetSum); prefix.put(curr, prefix.getOrDefault(curr, 0 ) - 1 ); return ret; } }
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科 中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大( 一个节点也可以是它自己的祖先 )。”
提示:
树中节点数目在范围 [2, 10<sup>5</sup>] 内。
-10<sup>9</sup> <= Node.val <= 10<sup>9</sup>
所有 Node.val 互不相同 。
p != q
p 和 q 均存在于给定的二叉树中。
示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3 。
示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5 。因为根据定义最近公共祖先节点可以为节点本身。
示例 3:
输入: root = [1,2], p = 1, q = 2
输出: 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 class Solution { private TreeNode ans; public Solution () { this .ans = null ; } private boolean dfs (TreeNode root, TreeNode p, TreeNode q) { if (root == null ) return false ; boolean lson = dfs(root.left, p, q); boolean rson = dfs(root.right, p, q); if ((lson && rson) || ((root.val == p.val || root.val == q.val) && (lson || rson))) { ans = root; } return lson || rson || (root.val == p.val || root.val == q.val); } public TreeNode lowestCommonAncestor (TreeNode root, TreeNode p, TreeNode q) { this .dfs(root, p, q); return this .ans; } }
二叉树中的** 路径** 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径** 至少包含一个 **节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
提示:
树中节点数目范围是 [1, 3 * 10<sup>4</sup>]
-1000 <= Node.val <= 1000
示例 1:
输入: root = [1,2,3]
输出: 6
解释: 最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
示例 2:
输入: root = [-10,9,20,null,null,15,7]
输出: 42
解释: 最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
代码 :
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 class Solution { int maxSum = Integer.MIN_VALUE; public int maxPathSum (TreeNode root) { maxGain(root); return maxSum; } public int maxGain (TreeNode node) { if (node == null ) { return 0 ; } int leftGain = Math.max(maxGain(node.left), 0 ); int rightGain = Math.max(maxGain(node.right), 0 ); int priceNewpath = node.val + leftGain + rightGain; maxSum = Math.max(maxSum, priceNewpath); return node.val + Math.max(leftGain, rightGain); } }
图论 1. **岛屿数量 (中) ** 给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] 的值为 '0' 或 '1'
示例 1:
1 2 3 4 5 6 7 输入:grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] 输出:1
示例 2:
1 2 3 4 5 6 输入:grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","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 class Solution { void dfs (char [][] grid, int r, int c) { int nr = grid.length; int nc = grid[0 ].length; if (r < 0 || c < 0 || r >= nr || c >= nc || grid[r][c] == '0' ) { return ; } grid[r][c] = '0' ; dfs(grid, r - 1 , c); dfs(grid, r + 1 , c); dfs(grid, r, c - 1 ); dfs(grid, r, c + 1 ); } public int numIslands (char [][] grid) { if (grid == null || grid.length == 0 ) { return 0 ; } int nr = grid.length; int nc = grid[0 ].length; int num_islands = 0 ; for (int r = 0 ; r < nr; ++r) { for (int c = 0 ; c < nc; ++c) { if (grid[r][c] == '1' ) { ++num_islands; dfs(grid, r, c); } } } return num_islands; } }
在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:
值 0 代表空单元格;
值 1 代表新鲜橘子;
值 2 代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。
返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 10
grid[i][j] 仅为 0、1 或 2
示例 1:
输入: grid = [[2,1,1],[1,1,0],[0,1,1]]
输出: 4
示例 2:
输入: grid = [[2,1,1],[0,1,1],[1,0,1]]
输出: -1
解释: 左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个方向上。
示例 3:
输入: grid = [[0,2]]
输出: 0
解释: 因为 0 分钟时已经没有新鲜橘子了,所以答案就是 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 33 34 35 36 37 38 39 40 41 42 43 44 class Solution { int [] dr = new int []{-1 , 0 , 1 , 0 }; int [] dc = new int []{0 , -1 , 0 , 1 }; public int orangesRotting (int [][] grid) { int R = grid.length, C = grid[0 ].length; Queue<Integer> queue = new ArrayDeque <Integer>(); Map<Integer, Integer> depth = new HashMap <Integer, Integer>(); for (int r = 0 ; r < R; ++r) { for (int c = 0 ; c < C; ++c) { if (grid[r][c] == 2 ) { int code = r * C + c; queue.add(code); depth.put(code, 0 ); } } } int ans = 0 ; while (!queue.isEmpty()) { int code = queue.remove(); int r = code / C, c = code % C; for (int k = 0 ; k < 4 ; ++k) { int nr = r + dr[k]; int nc = c + dc[k]; if (0 <= nr && nr < R && 0 <= nc && nc < C && grid[nr][nc] == 1 ) { grid[nr][nc] = 2 ; int ncode = nr * C + nc; queue.add(ncode); depth.put(ncode, depth.get(code) + 1 ); ans = depth.get(ncode); } } } for (int [] row: grid) { for (int v: row) { if (v == 1 ) { return -1 ; } } } return ans; } }
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [a<sub>i</sub>, b<sub>i</sub>] ,表示如果要学习课程 a<sub>i</sub> 则 必须 先学习课程 b<sub>i</sub> ~ ~ 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
提示:
1 <= numCourses <= 2000
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= a<sub>i</sub>, b<sub>i</sub> < numCourses
prerequisites[i] 中的所有课程对 互不相同
示例 1:
输入: numCourses = 2, prerequisites = [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:
输入: numCourses = 2, prerequisites = [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 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 class Solution { List<List<Integer>> edges; int [] visited; boolean valid = true ; public boolean canFinish (int numCourses, int [][] prerequisites) { edges = new ArrayList <List<Integer>>(); for (int i = 0 ; i < numCourses; ++i) { edges.add(new ArrayList <Integer>()); } visited = new int [numCourses]; for (int [] info : prerequisites) { edges.get(info[1 ]).add(info[0 ]); } for (int i = 0 ; i < numCourses && valid; ++i) { if (visited[i] == 0 ) { dfs(i); } } return valid; } public void dfs (int u) { visited[u] = 1 ; for (int v: edges.get(u)) { if (visited[v] == 0 ) { dfs(v); if (!valid) { return ; } } else if (visited[v] == 1 ) { valid = false ; return ; } } visited[u] = 2 ; } }
Trie (发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
提示:
1 <= word.length, prefix.length <= 2000
word 和 prefix 仅由小写英文字母组成
insert、search 和 startsWith 调用次数 总计 不超过 3 * 10<sup>4</sup> 次
示例:
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 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 43 44 45 class Trie { private Trie[] children; private boolean isEnd; public Trie () { children = new Trie [26 ]; isEnd = false ; } public void insert (String word) { Trie node = this ; for (int i = 0 ; i < word.length(); i++) { char ch = word.charAt(i); int index = ch - 'a' ; if (node.children[index] == null ) { node.children[index] = new Trie (); } node = node.children[index]; } node.isEnd = true ; } public boolean search (String word) { Trie node = searchPrefix(word); return node != null && node.isEnd; } public boolean startsWith (String prefix) { return searchPrefix(prefix) != null ; } private Trie searchPrefix (String prefix) { Trie node = this ; for (int i = 0 ; i < prefix.length(); i++) { char ch = prefix.charAt(i); int index = ch - 'a' ; if (node.children[index] == null ) { return null ; } node = node.children[index]; } return node; } }
回溯 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
示例 1:
1 2 输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
1 2 输入:nums = [0,1] 输出:[[0,1],[1,0]]
示例 3:
代码 :
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 public List<List<Integer>> permute (int [] nums) { List<List<Integer>> res = new ArrayList <List<Integer>>(); List<Integer> output = new ArrayList <Integer>(); for (int num : nums) { output.add(num); } int n = nums.length; backtrack(n, output, res, 0 ); return res; } public void backtrack (int n, List<Integer> output, List<List<Integer>> res, int first) { if (first == n) { res.add(new ArrayList <Integer>(output)); } for (int i = first; i < n; i++) { Collections.swap(output, first, i); backtrack(n, output, res, first + 1 ); Collections.swap(output, first, i); } }
2. 子集 (中) 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入: nums = [1,2,3]
输出: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:
输入: nums = [0]
输出: [[],[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 33 34 35 36 37 38 39 40 41 class Solution { List<Integer> t = new ArrayList <Integer>(); List<List<Integer>> ans = new ArrayList <List<Integer>>(); public List<List<Integer>> subsets (int [] nums) { int n = nums.length; for (int mask = 0 ; mask < (1 << n); ++mask) { t.clear(); for (int i = 0 ; i < n; ++i) { if ((mask & (1 << i)) != 0 ) { t.add(nums[i]); } } ans.add(new ArrayList <Integer>(t)); } return ans; } } class Solution { List<Integer> t = new ArrayList <Integer>(); List<List<Integer>> ans = new ArrayList <List<Integer>>(); public List<List<Integer>> subsets (int [] nums) { dfs(0 , nums); return ans; } public void dfs (int cur, int [] nums) { if (cur == nums.length) { ans.add(new ArrayList <Integer>(t)); return ; } t.add(nums[cur]); dfs(cur + 1 , nums); t.remove(t.size() - 1 ); dfs(cur + 1 , nums); } }
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
提示:
0 <= digits.length <= 4
digits[i] 是范围 ['2', '9'] 的一个数字。
示例 1:
输入: digits = "23"
输出: ["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入: digits = ""
输出: []
示例 3:
输入: digits = "2"
输出: ["a","b","c"]
代码 :
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 class Solution { public List<String> letterCombinations (String digits) { List<String> combinations = new ArrayList <String>(); if (digits.length() == 0 ) { return combinations; } Map<Character, String> phoneMap = new HashMap <Character, String>() {{ put('2' , "abc" ); put('3' , "def" ); put('4' , "ghi" ); put('5' , "jkl" ); put('6' , "mno" ); put('7' , "pqrs" ); put('8' , "tuv" ); put('9' , "wxyz" ); }}; backtrack(combinations, phoneMap, digits, 0 , new StringBuffer ()); return combinations; } public void backtrack (List<String> combinations, Map<Character, String> phoneMap, String digits, int index, StringBuffer combination) { if (index == digits.length()) { combinations.add(combination.toString()); } else { char digit = digits.charAt(index); String letters = phoneMap.get(digit); int lettersCount = letters.length(); for (int i = 0 ; i < lettersCount; i++) { combination.append(letters.charAt(i)); backtrack(combinations, phoneMap, digits, index + 1 , combination); combination.deleteCharAt(index); } } } }
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
提示:
1 <= candidates.length <= 30
2 <= candidates[i] <= 40
candidates 的所有元素 互不相同
1 <= target <= 40
示例 1:
输入: candidates = [2,3,6,7], target = 7
输出: [[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 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 class Solution { public List<List<Integer>> combinationSum (int [] candidates, int target) { List<List<Integer>> ans = new ArrayList <List<Integer>>(); List<Integer> combine = new ArrayList <Integer>(); dfs(candidates, target, ans, combine, 0 ); return ans; } public void dfs (int [] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) { if (idx == candidates.length) { return ; } if (target == 0 ) { ans.add(new ArrayList <Integer>(combine)); return ; } dfs(candidates, target, ans, combine, idx + 1 ); if (target - candidates[idx] >= 0 ) { combine.add(candidates[idx]); dfs(candidates, target - candidates[idx], ans, combine, idx); combine.remove(combine.size() - 1 ); } } }
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 **有效的 **括号组合。
提示:
示例 1:
输入: n = 3
输出: ["((()))","(()())","(())()","()(())","()()()"]
示例 2:
输入: 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 class Solution { public List<String> generateParenthesis (int n) { List<String> ans = new ArrayList <String>(); backtrack(ans, new StringBuilder (), 0 , 0 , n); return ans; } public void backtrack (List<String> ans, StringBuilder cur, int open, int close, int max) { if (cur.length() == max * 2 ) { ans.add(cur.toString()); return ; } if (open < max) { cur.append('(' ); backtrack(ans, cur, open + 1 , close, max); cur.deleteCharAt(cur.length() - 1 ); } if (close < open) { cur.append(')' ); backtrack(ans, cur, open, close + 1 , max); cur.deleteCharAt(cur.length() - 1 ); } } }
给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
提示:
m == board.length
n = board[i].length
1 <= m, n <= 6
1 <= word.length <= 15
board 和 word 仅由大小写英文字母组成
进阶: 你可以使用搜索剪枝的技术来优化解决方案,使其在 board 更大的情况下可以更快解决问题?
示例 1:
输入: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出: true
示例 2:
输入: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出: true
示例 3:
输入: board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
输出: false
代码 :
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 class Solution { public boolean exist (char [][] board, String word) { boolean [][] visited = new boolean [board.length][board[0 ].length]; for (int i = 0 ; i < board.length; i++) { for (int j = 0 ; j < board[i].length; j++) { if (dfsChecker(board, visited, i, j, 0 , word)) { return true ; } } } return false ; } private boolean dfsChecker (char [][] board, boolean [][] visited, int i, int j, int k, String word) { if (board[i][j] != word.charAt(k)) { return false ; } if (k == word.length() - 1 ) { return true ; } visited[i][j] = true ; int [][] directions = {{0 , 1 }, {0 , -1 }, {1 , 0 }, {-1 , 0 }}; boolean flag = false ; for (int [] direction : directions) { int newI = i + direction[0 ], newJ = j + direction[1 ]; if (newI >= 0 && newI < board.length && newJ >= 0 && newJ < board[0 ].length && !visited[newI][newJ]) { boolean checker = dfsChecker(board, visited, newI, newJ, k + 1 , word); if (checker) { flag = true ; break ; } } } visited[i][j] = false ; return flag; } }
给你一个字符串 s,请你将 s 分割成一些 子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
提示:
1 <= s.length <= 16
s 仅由小写英文字母组成
示例 1:
输入: s = "aab"
输出: [["a","a","b"],["aa","b"]]
示例 2:
输入: s = "a"
输出: [["a"]]
代码 :
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 class Solution { boolean [][] f; List<List<String>> ret = new ArrayList <List<String>>(); List<String> ans = new ArrayList <String>(); int n; public List<List<String>> partition (String s) { n = s.length(); f = new boolean [n][n]; for (int i = 0 ; i < n; ++i) { Arrays.fill(f[i], true ); } for (int i = n - 1 ; i >= 0 ; --i) { for (int j = i + 1 ; j < n; ++j) { f[i][j] = (s.charAt(i) == s.charAt(j)) && f[i + 1 ][j - 1 ]; } } dfs(s, 0 ); return ret; } public void dfs (String s, int i) { if (i == n) { ret.add(new ArrayList <String>(ans)); return ; } for (int j = i; j < n; ++j) { if (f[i][j]) { ans.add(s.substring(i, j + 1 )); dfs(s, j + 1 ); ans.remove(ans.size() - 1 ); } } } }
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
提示:
示例 1:
输入: n = 4
输出: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
解释: 如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入: n = 1
输出: [["Q"]]
代码 :
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 class Solution { public List<List<String>> solveNQueens (int n) { List<List<String>> solutions = new ArrayList <List<String>>(); int [] queens = new int [n]; Arrays.fill(queens, -1 ); Set<Integer> columns = new HashSet <Integer>(); Set<Integer> diagonals1 = new HashSet <Integer>(); Set<Integer> diagonals2 = new HashSet <Integer>(); backtrack(solutions, queens, n, 0 , columns, diagonals1, diagonals2); return solutions; } public void backtrack (List<List<String>> solutions, int [] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) { if (row == n) { List<String> board = generateBoard(queens, n); solutions.add(board); } else { for (int i = 0 ; i < n; i++) { if (columns.contains(i)) { continue ; } int diagonal1 = row - i; if (diagonals1.contains(diagonal1)) { continue ; } int diagonal2 = row + i; if (diagonals2.contains(diagonal2)) { continue ; } queens[row] = i; columns.add(i); diagonals1.add(diagonal1); diagonals2.add(diagonal2); backtrack(solutions, queens, n, row + 1 , columns, diagonals1, diagonals2); queens[row] = -1 ; columns.remove(i); diagonals1.remove(diagonal1); diagonals2.remove(diagonal2); } } } public List<String> generateBoard (int [] queens, int n) { List<String> board = new ArrayList <String>(); for (int i = 0 ; i < n; i++) { char [] row = new char [n]; Arrays.fill(row, '.' ); row[queens[i]] = 'Q' ; board.add(new String (row)); } return board; } }
二分查找 给你一个满足下述两条属性的 m x n 整数矩阵:
每行中的整数从左到右按非严格递增顺序排列。
每行的第一个整数大于前一行的最后一个整数。
给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104
示例 1:
1 2 输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 输出:true
示例 2:
1 2 输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 输出:false
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public boolean searchMatrix (int [][] matrix, int target) { int cols = matrix[0 ].length, rows = matrix.length; while (rows > 0 ) { int left = 0 , right = cols - 1 ; while (left <= right) { int mid = left + (right - left) / 2 ; if (matrix[rows - 1 ][mid] == target) { return true ; } else if (matrix[rows - 1 ][mid] < target) { left = mid + 1 ; } else { right = mid - 1 ; } } rows --; } return false ; }
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
提示:
1 <= nums.length <= 10<sup>4</sup>
-10<sup>4</sup> <= nums[i] <= 10<sup>4</sup>
nums 为 **无重复元素 **的 **升序 **排列数组
-10<sup>4</sup> <= target <= 10<sup>4</sup>
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public int searchInsert (int [] nums, int target) { int n = nums.length; int left = 0 , right = n - 1 , ans = n; while (left <= right) { int mid = ((right - left) >> 1 ) + left; if (target <= nums[mid]) { ans = mid; right = mid - 1 ; } else { left = mid + 1 ; } } return ans; } }
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
提示:
0 <= nums.length <= 10<sup>5</sup>
-10<sup>9</sup> <= nums[i] <= 10<sup>9</sup>
nums 是一个非递减数组
-10<sup>9</sup> <= target <= 10<sup>9</sup>
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
示例 3:
输入: nums = [], target = 0
输出: [-1,-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 class Solution { public int [] searchRange(int [] nums, int target) { int leftIdx = binarySearch(nums, target, true ); int rightIdx = binarySearch(nums, target, false ) - 1 ; if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) { return new int []{leftIdx, rightIdx}; } return new int []{-1 , -1 }; } public int binarySearch (int [] nums, int target, boolean lower) { int left = 0 , right = nums.length - 1 , ans = nums.length; while (left <= right) { int mid = (left + right) / 2 ; if (nums[mid] > target || (lower && nums[mid] >= target)) { right = mid - 1 ; ans = mid; } else { left = mid + 1 ; } } return ans; } }
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
提示:
1 <= nums.length <= 5000
-10<sup>4</sup> <= nums[i] <= 10<sup>4</sup>
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-10<sup>4</sup> <= target <= 10<sup>4</sup>
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
示例 3:
输入: nums = [1], target = 0
输出: -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 class Solution { public int search (int [] nums, int target) { int n = nums.length; if (n == 0 ) { return -1 ; } if (n == 1 ) { return nums[0 ] == target ? 0 : -1 ; } int l = 0 , r = n - 1 ; while (l <= r) { int mid = (l + r) / 2 ; if (nums[mid] == target) { return mid; } if (nums[0 ] <= nums[mid]) { if (nums[0 ] <= target && target < nums[mid]) { r = mid - 1 ; } else { l = mid + 1 ; } } else { if (nums[mid] < target && target <= nums[n - 1 ]) { l = mid + 1 ; } else { r = mid - 1 ; } } } return -1 ; } }
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:* 若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums 中的所有整数 互不相同
nums 原来是一个升序排序的数组,并进行了 1 至 n 次旋转
示例 1:
输入: nums = [3,4,5,1,2]
输出: 1
解释: 原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2:
输入: nums = [4,5,6,7,0,1,2]
输出: 0
解释: 原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。
示例 3:
输入: nums = [11,13,15,17]
输出: 11
解释: 原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Solution { public int findMin (int [] nums) { int low = 0 ; int high = nums.length - 1 ; while (low < high) { int pivot = low + (high - low) / 2 ; if (nums[pivot] < nums[high]) { high = pivot; } else { low = pivot + 1 ; } } return nums[low]; } }
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
提示:
nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-10<sup>6</sup> <= nums1[i], nums2[i] <= 10<sup>6</sup>
示例 1:
输入: nums1 = [1,3], nums2 = [2]
输出: 2.00000
解释: 合并数组 = [1,2,3] ,中位数 2
示例 2:
输入: nums1 = [1,2], nums2 = [3,4]
输出: 2.50000
解释: 合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
代码 :
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 class Solution { public double findMedianSortedArrays (int [] nums1, int [] nums2) { int length1 = nums1.length, length2 = nums2.length; int totalLength = length1 + length2; if (totalLength % 2 == 1 ) { int midIndex = totalLength / 2 ; double median = getKthElement(nums1, nums2, midIndex + 1 ); return median; } else { int midIndex1 = totalLength / 2 - 1 , midIndex2 = totalLength / 2 ; double median = (getKthElement(nums1, nums2, midIndex1 + 1 ) + getKthElement(nums1, nums2, midIndex2 + 1 )) / 2.0 ; return median; } } public int getKthElement (int [] nums1, int [] nums2, int k) { int length1 = nums1.length, length2 = nums2.length; int index1 = 0 , index2 = 0 ; int kthElement = 0 ; while (true ) { if (index1 == length1) { return nums2[index2 + k - 1 ]; } if (index2 == length2) { return nums1[index1 + k - 1 ]; } if (k == 1 ) { return Math.min(nums1[index1], nums2[index2]); } int half = k / 2 ; int newIndex1 = Math.min(index1 + half, length1) - 1 ; int newIndex2 = Math.min(index2 + half, length2) - 1 ; int pivot1 = nums1[newIndex1], pivot2 = nums2[newIndex2]; if (pivot1 <= pivot2) { k -= (newIndex1 - index1 + 1 ); index1 = newIndex1 + 1 ; } else { k -= (newIndex2 - index2 + 1 ); index2 = newIndex2 + 1 ; } } } }
栈 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。
提示:
1 <= s.length <= 104
s 仅由括号 '()[]{}' 组成
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 示例 1: 输入:s = "()" 输出:true 示例 2: 输入:s = "()[]{}" 输出:true 示例 3: 输入:s = "(]" 输出:false 示例 4: 输入:s = "([])" 输出:true
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public boolean isValid (String s) { if (s.length() % 2 != 0 ) { return false ; } Map<Character, Character> map = new HashMap <>(){{ put(')' , '(' ); put(']' , '[' ); put('}' , '{' ); }}; Deque<Character> stack = new LinkedList <>(); for (int i = 0 ; i < s.length(); i++) { char c = s.charAt(i); if (map.containsKey(c)) { if (stack.isEmpty() || map.get(c) != stack.peek()) { return false ; } stack.pop(); } else { stack.push(c); } } return stack.isEmpty(); }
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
提示:
-231 <= val <= 231 - 1
pop、top 和 getMin 操作总是在 非空栈 上调用
push, pop, top, and getMin最多被调用 3 * 104 次
示例 1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 输入: ["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.
代码 :
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 class MinStack { Deque<Integer> xStack; Deque<Integer> minStack; public MinStack () { xStack = new LinkedList <Integer>(); minStack = new LinkedList <Integer>(); minStack.push(Integer.MAX_VALUE); } public void push (int x) { xStack.push(x); minStack.push(Math.min(minStack.peek(), x)); } public void pop () { xStack.pop(); minStack.pop(); } public int top () { return xStack.peek(); } public int getMin () { return minStack.peek(); } }
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
提示:
1 <= s.length <= 30
s 由小写英文字母、数字和方括号 '[]' 组成
s 保证是一个 有效 的输入。
s 中所有整数的取值范围为 [1, 300]
示例 1:
输入: s = "3[a]2[bc]"
输出: "aaabcbc"
示例 2:
输入: s = "3[a2[c]]"
输出: "accaccacc"
示例 3:
输入: s = "2[abc]3[cd]ef"
输出: "abcabccdcdcdef"
示例 4:
输入: s = "abc3[cd]xyz"
输出: "abccdcdcdxyz"
代码 :
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 class Solution { public String decodeString (String s) { StringBuilder res = new StringBuilder (); int multi = 0 ; LinkedList<Integer> stack_multi = new LinkedList <>(); LinkedList<String> stack_res = new LinkedList <>(); for (Character c : s.toCharArray()) { if (c == '[' ) { stack_multi.addLast(multi); stack_res.addLast(res.toString()); multi = 0 ; res = new StringBuilder (); } else if (c == ']' ) { StringBuilder tmp = new StringBuilder (); int cur_multi = stack_multi.removeLast(); for (int i = 0 ; i < cur_multi; i++) tmp.append(res); res = new StringBuilder (stack_res.removeLast() + tmp); } else if (c >= '0' && c <= '9' ) multi = multi * 10 + Integer.parseInt(c + "" ); else res.append(c); } return res.toString(); } } class Solution { public String decodeString (String s) { return dfs(s, 0 )[0 ]; } private String[] dfs(String s, int i) { StringBuilder res = new StringBuilder (); int multi = 0 ; while (i < s.length()) { if (s.charAt(i) >= '0' && s.charAt(i) <= '9' ) multi = multi * 10 + Integer.parseInt(String.valueOf(s.charAt(i))); else if (s.charAt(i) == '[' ) { String[] tmp = dfs(s, i + 1 ); i = Integer.parseInt(tmp[0 ]); while (multi > 0 ) { res.append(tmp[1 ]); multi--; } } else if (s.charAt(i) == ']' ) return new String [] { String.valueOf(i), res.toString() }; else res.append(String.valueOf(s.charAt(i))); i++; } return new String [] { res.toString() }; } }
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。
提示:
1 <= temperatures.length <= 10<sup>5</sup>
30 <= temperatures[i] <= 100
示例 1:
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例 2:
输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
示例 3:
输入: temperatures = [30,60,90]
输出: [1,1,0]
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Solution { public int [] dailyTemperatures(int [] temperatures) { int length = temperatures.length; int [] ans = new int [length]; Deque<Integer> stack = new LinkedList <Integer>(); for (int i = 0 ; i < length; i++) { int temperature = temperatures[i]; while (!stack.isEmpty() && temperature > temperatures[stack.peek()]) { int prevIndex = stack.pop(); ans[prevIndex] = i - prevIndex; } stack.push(i); } return ans; } }
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
提示:
1 <= heights.length <=10<sup>5</sup>
0 <= heights[i] <= 10<sup>4</sup>
示例 1:
输入: heights = [2,1,5,6,2,3]
输出: 10
解释: 最大的矩形为图中红色区域,面积为 10
示例 2:
输入: heights = [2,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 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 class Solution { public int largestRectangleArea (int [] heights) { int n = heights.length; int [] left = new int [n]; int [] right = new int [n]; Deque<Integer> mono_stack = new ArrayDeque <Integer>(); for (int i = 0 ; i < n; ++i) { while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) { mono_stack.pop(); } left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek()); mono_stack.push(i); } mono_stack.clear(); for (int i = n - 1 ; i >= 0 ; --i) { while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) { mono_stack.pop(); } right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek()); mono_stack.push(i); } int ans = 0 ; for (int i = 0 ; i < n; ++i) { ans = Math.max(ans, (right[i] - left[i] - 1 ) * heights[i]); } return ans; } } class Solution { public int largestRectangleArea (int [] heights) { int n = heights.length; int [] left = new int [n]; int [] right = new int [n]; Arrays.fill(right, n); Deque<Integer> mono_stack = new ArrayDeque <Integer>(); for (int i = 0 ; i < n; ++i) { while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) { right[mono_stack.peek()] = i; mono_stack.pop(); } left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek()); mono_stack.push(i); } int ans = 0 ; for (int i = 0 ; i < n; ++i) { ans = Math.max(ans, (right[i] - left[i] - 1 ) * heights[i]); } return ans; } }
堆 给定整数数组 nums 和整数 k,请返回数组中第 **k** 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
提示:
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104
示例 1:
1 2 输入: [3,2,1,5,6,4], k = 2 输出: 5
示例 2:
1 2 输入: [3,2,3,1,2,4,5,5,6], k = 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Solution { public int findKthLargest (int [] nums, int k) { Arrays.sort(nums); return nums[nums.length - k]; } public int findKthLargest (int [] nums, int k) { int heapSize = nums.length; buildMaxHeap(nums, heapSize); for (int i = nums.length - 1 ; i >= nums.length - k + 1 ; --i) { swap(nums, 0 , i); --heapSize; maxHeapify(nums, 0 , heapSize); } return nums[0 ]; } public void buildMaxHeap (int [] a, int heapSize) { for (int i = heapSize / 2 - 1 ; i >= 0 ; --i) { maxHeapify(a, i, heapSize); } } public void maxHeapify (int [] a, int i, int heapSize) { int l = i * 2 + 1 , r = i * 2 + 2 , largest = i; if (l < heapSize && a[l] > a[largest]) { largest = l; } if (r < heapSize && a[r] > a[largest]) { largest = r; } if (largest != i) { swap(a, i, largest); maxHeapify(a, largest, heapSize); } } public void swap (int [] a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } }
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
提示:
1 <= nums.length <= 10<sup>5</sup>
k 的取值范围是 [1, 数组中不相同的元素的个数]
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
进阶: 你所设计算法的时间复杂度 必须 优于 O(n log n) ,其中 n 是数组大小。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [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 49 50 class Solution { public int [] topKFrequent(int [] nums, int k) { Map<Integer, Integer> occurrences = new HashMap <Integer, Integer>(); for (int num : nums) { occurrences.put(num, occurrences.getOrDefault(num, 0 ) + 1 ); } List<int []> values = new ArrayList <int []>(); for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) { int num = entry.getKey(), count = entry.getValue(); values.add(new int []{num, count}); } int [] ret = new int [k]; qsort(values, 0 , values.size() - 1 , ret, 0 , k); return ret; } public void qsort (List<int []> values, int start, int end, int [] ret, int retIndex, int k) { int picked = (int ) (Math.random() * (end - start + 1 )) + start; Collections.swap(values, picked, start); int pivot = values.get(start)[1 ]; int index = start; for (int i = start + 1 ; i <= end; i++) { if (values.get(i)[1 ] >= pivot) { Collections.swap(values, index + 1 , i); index++; } } Collections.swap(values, start, index); if (k <= index - start) { qsort(values, start, index - 1 , ret, retIndex, k); } else { for (int i = start; i <= index; i++) { ret[retIndex++] = values.get(i)[0 ]; } if (k > index - start + 1 ) { qsort(values, index + 1 , end, ret, retIndex, k - (index - start + 1 )); } } } }
中位数 是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。
例如 arr = [2,3,4] 的中位数是 3 。
例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。
实现 MedianFinder 类:
MedianFinder() 初始化 MedianFinder 对象。
void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10<sup>-5</sup> 以内的答案将被接受。
提示:
-10<sup>5</sup> <= num <= 10<sup>5</sup>
在调用 findMedian 之前,数据结构中至少有一个元素
最多 5 * 10<sup>4</sup> 次调用 addNum 和 findMedian
示例 1:
输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]
解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3); // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.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 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 class MedianFinder { TreeMap<Integer, Integer> nums; int n; int [] left; int [] right; public MedianFinder () { nums = new TreeMap <Integer, Integer>(); n = 0 ; left = new int [2 ]; right = new int [2 ]; } public void addNum (int num) { nums.put(num, nums.getOrDefault(num, 0 ) + 1 ); if (n == 0 ) { left[0 ] = right[0 ] = num; left[1 ] = right[1 ] = 1 ; } else if ((n & 1 ) != 0 ) { if (num < left[0 ]) { decrease(left); } else { increase(right); } } else { if (num > left[0 ] && num < right[0 ]) { increase(left); decrease(right); } else if (num >= right[0 ]) { increase(left); } else { decrease(right); System.arraycopy(right, 0 , left, 0 , 2 ); } } n++; } public double findMedian () { return (left[0 ] + right[0 ]) / 2.0 ; } private void increase (int [] iterator) { iterator[1 ]++; if (iterator[1 ] > nums.get(iterator[0 ])) { iterator[0 ] = nums.ceilingKey(iterator[0 ] + 1 ); iterator[1 ] = 1 ; } } private void decrease (int [] iterator) { iterator[1 ]--; if (iterator[1 ] == 0 ) { iterator[0 ] = nums.floorKey(iterator[0 ] - 1 ); iterator[1 ] = nums.get(iterator[0 ]); } } }
贪心算法 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
示例 1:
1 2 3 4 输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
1 2 3 输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Solution { public int maxProfit (int prices[]) { int minprice = Integer.MAX_VALUE; int maxprofit = 0 ; for (int i = 0 ; i < prices.length; i++) { if (prices[i] < minprice) { minprice = prices[i]; } else if (prices[i] - minprice > maxprofit) { maxprofit = prices[i] - minprice; } } return maxprofit; } }
给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 105
示例 1:
1 2 3 输入:nums = [2,3,1,1,4] 输出:true 解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。
示例 2:
1 2 3 输入:nums = [3,2,1,0,4] 输出:false 解释:无论怎样,总会到达下标为 3 的位置。但该下标的最大跳跃长度是 0 , 所以永远不可能到达最后一个下标。
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 public boolean canJump (int [] nums) { int max = 0 ; for (int i = 0 ; i < nums.length; i++) { max = Math.max(max, i + nums[i]); if (max <= i) { return false ; } } return true ; }
给定一个长度为 n 的 0 索引 整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
提示:
1 <= nums.length <= 104
0 <= nums[i] <= 1000
题目保证可以到达 nums[n-1]
示例 1:
1 2 3 4 输入: nums = [2,3,1,1,4] 输出: 2 解释: 跳到最后一个位置的最小跳跃数是 2。 从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:
1 2 输入: nums = [2,3,0,1,4] 输出: 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 public int jump (int [] nums) { if (nums == null || nums.length == 0 ) { throw new IllegalArgumentException ("Input array is null or empty" ); } int n = nums.length; if (n == 1 ) { return 0 ; } int currentEnd = 0 ; int farthest = 0 ; int steps = 0 ; for (int i = 0 ; i < n - 1 ; i++) { farthest = Math.max(farthest, i + nums[i]); if (i == currentEnd) { steps++; currentEnd = farthest; if (currentEnd >= n - 1 ) { return steps; } } } return -1 ; }
给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。例如,字符串 "ababcc" 能够被分为 ["abab", "cc"],但类似 ["aba", "bcc"] 或 ["ab", "ab", "cc"] 的划分是非法的。
注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。
返回一个表示每个字符串片段的长度的列表。
提示:
1 <= s.length <= 500
s 仅由小写英文字母组成
示例 1:
1 2 3 4 5 6 输入:s = "ababcbacadefegdehijhklij" 输出:[9,7,8] 解释: 划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。 每个字母最多出现在一个片段中。 像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。
示例 2:
1 2 输入:s = "eccbbbbdec" 输出:[10]
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public List<Integer> partitionLabels (String s) { int n=s.length(); int [] cnt=new int [26 ]; for (int i=0 ;i<n;i++) cnt[s.charAt(i)-'a' ]=i; List<Integer> ans=new ArrayList <>(); int end=0 ,start=0 ; for (int i=0 ;i<n;i++){ end=Math.max(cnt[s.charAt(i)-'a' ],end); if (end==i){ ans.add(end-start+1 ); start=end+1 ; } } return ans; }
动态规划 给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
进阶:
提示:
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
示例:
1 2 3 4 5 6 7 8 9 10 11 12 示例 1: 输入:nums = [10,9,2,5,3,7,101,18] 输出:4 解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。 示例 2: 输入:nums = [0,1,0,3,2,3] 输出:4 示例 3: 输入:nums = [7,7,7,7,7,7,7] 输出:1
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public int lengthOfLIS (int [] nums) { if (nums.length == 0 ) { return 0 ; } int [] dp = new int [nums.length]; dp[0 ] = 1 ; int maxans = 1 ; for (int i = 1 ; i < nums.length; i++) { dp[i] = 1 ; for (int j = 0 ; j < i; j++) { if (nums[i] > nums[j]) { dp[i] = Math.max(dp[i], dp[j] + 1 ); } } maxans = Math.max(maxans, dp[i]); } return maxans; }
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
提示:
1 <= coins.length <= 12
1 <= coins[i] <= 231 - 1
0 <= amount <= 104
示例:
1 2 3 4 5 6 7 8 9 10 11 12 示例 1: 输入:coins = [1, 2, 5], amount = 11 输出:3 解释:11 = 5 + 5 + 1 示例 2: 输入:coins = [2], amount = 3 输出:-1 示例 3: 输入:coins = [1], amount = 0 输出:0
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int coinChange (int [] coins, int amount) { int max = amount + 1 ; int [] dp = new int [amount + 1 ]; Arrays.fill(dp, amount + 1 ); dp[0 ] = 0 ; for (int i = 0 ; i <= amount; i++) { for (int j = 0 ; j < coins.length; j++) { if (coins[j] <= i) { dp[i] = Math.min(dp[i - coins[j]] + 1 , dp[i]); } } } return dp[amount] == amount + 1 ? -1 : dp[amount]; }
给定一个非负整数 numRows, 生成「杨辉三角」的前 *numRows *行。
在「杨辉三角」中,每个数是它左上方和右上方的数的和。
示例 1:
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
示例 2:
输入: numRows = 1
输出: [[1]]
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public List<List<Integer>> generate (int numRows) { List<List<Integer>> ret = new ArrayList <List<Integer>>(); for (int i = 0 ; i < numRows; ++i) { List<Integer> row = new ArrayList <Integer>(); for (int j = 0 ; j <= i; ++j) { if (j == 0 || j == i) { row.add(1 ); } else { row.add(ret.get(i - 1 ).get(j - 1 ) + ret.get(i - 1 ).get(j)); } } ret.add(row); } return ret; } }
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统, 如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 ** 不触动警报装置的情况下 ** ,一夜之内能够偷窃到的最高金额。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 400
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
代码:
1 2 3 4 5 6 7 8 9 10 11 class Solution { public int rob (int [] nums) { int [] dp = new int [nums.length + 1 ]; dp[0 ] = 0 ; dp[1 ] = nums[0 ]; for (int i = 2 ; i <= nums.length; i++) { dp[i] = Math.max(dp[i - 1 ], dp[i - 2 ] + nums[i - 1 ]); } return dp[nums.length]; } }
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
提示:
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 class Solution { public int numSquares (int n) { int [] dp = new int [n +1 ]; for (int i = 1 ; i <= n; i ++ ) { int l = Integer.MAX_VALUE; for (int j = 1 ; j *j <= i; j++) { l = Math.min(dp[i - j * j], l); } dp[i] = l + 1 ; } return dp[n]; } }
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
注意: 不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
提示:
1 <= s.length <= 300
1 <= wordDict.length <= 1000
1 <= wordDict[i].length <= 20
s 和 wordDict[i] 仅由小写英文字母组成
wordDict 中的所有字符串 互不相同
示例 1:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。
示例 2:
输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
注意,你可以重复使用字典中的单词。
示例 3:
输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Solution { public boolean wordBreak (String s, List<String> wordDict) { Set<String> wordDictSet = new HashSet (wordDict); boolean [] dp = new boolean [s.length() + 1 ]; dp[0 ] = true ; for (int i = 1 ; i <= s.length(); i++) { for (int j = 0 ; j < i; j++) { if (dp[j] && wordDictSet.contains(s.substring(j, i))) { dp[i] = true ; break ; } } } return dp[s.length()]; } }
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
**子序列 **是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
提示:
1 <= nums.length <= 2500
-10<sup>4</sup> <= nums[i] <= 10<sup>4</sup>
示例 1:
输入: nums = [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入: nums = [0,1,0,3,2,3]
输出: 4
示例 3:
输入: nums = [7,7,7,7,7,7,7]
输出: 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 class Solution { public int lengthOfLIS (int [] nums) { int len = 1 , n = nums.length; if (n == 0 ) { return 0 ; } int [] d = new int [n + 1 ]; d[len] = nums[0 ]; for (int i = 1 ; i < n; ++i) { if (nums[i] > d[len]) { d[++len] = nums[i]; } else { int l = 1 , r = len, pos = 0 ; while (l <= r) { int mid = (l + r) >> 1 ; if (d[mid] < nums[i]) { pos = mid; l = mid + 1 ; } else { r = mid - 1 ; } } d[pos + 1 ] = nums[i]; } } return len; } }
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
提示:
1 <= nums.length <= 2 * 10<sup>4</sup>
-10 <= nums[i] <= 10
nums 的任何子数组的乘积都 保证 是一个 32-位 整数
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-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 49 50 class Solution { public int maxProduct (int [] nums) { int length = nums.length; long [] maxF = new long [length]; long [] minF = new long [length]; for (int i = 0 ; i < length; i++) { maxF[i] = nums[i]; minF[i] = nums[i]; } for (int i = 1 ; i < length; ++i) { maxF[i] = Math.max(maxF[i - 1 ] * nums[i], Math.max(nums[i], minF[i - 1 ] * nums[i])); minF[i] = Math.min(minF[i - 1 ] * nums[i], Math.min(nums[i], maxF[i - 1 ] * nums[i])); if (minF[i] < (-1 << 31 )) { minF[i] = nums[i]; } } int ans = (int ) maxF[0 ]; for (int i = 1 ; i < length; ++i) { ans = Math.max(ans, (int ) maxF[i]); } return ans; } } class Solution {public : int maxProduct (vector<int >& nums) { long maxF = nums[0 ], minF = nums[0 ], ans = nums[0 ]; for (int i = 1 ; i < nums.size(); ++i) { long mx = maxF, mn = minF; maxF = max(mx * nums[i], max((long )nums[i], mn * nums[i])); minF = min(mn * nums[i], min((long )nums[i], mx * nums[i])); if (minF<INT_MIN) { minF=nums[i]; } ans = max(maxF, ans); } return ans; } };
给你一个 **只包含正整数 **的 **非空 **数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
提示:
1 <= nums.length <= 200
1 <= nums[i] <= 100
示例 1:
输入: nums = [1,5,11,5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11] 。
示例 2:
输入: nums = [1,2,3,5]
输出: false
解释: 数组不能分割成两个元素和相等的子集。
代码:
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 class Solution { public boolean canPartition (int [] nums) { int n = nums.length; if (n < 2 ) { return false ; } int sum = 0 , maxNum = 0 ; for (int num : nums) { sum += num; maxNum = Math.max(maxNum, num); } if (sum % 2 != 0 ) { return false ; } int target = sum / 2 ; if (maxNum > target) { return false ; } boolean [][] dp = new boolean [n][target + 1 ]; for (int i = 0 ; i < n; i++) { dp[i][0 ] = true ; } dp[0 ][nums[0 ]] = true ; for (int i = 1 ; i < n; i++) { int num = nums[i]; for (int j = 1 ; j <= target; j++) { if (j >= num) { dp[i][j] = dp[i - 1 ][j] | dp[i - 1 ][j - num]; } else { dp[i][j] = dp[i - 1 ][j]; } } } return dp[n - 1 ][target]; } } class Solution { public boolean canPartition (int [] nums) { int n = nums.length; if (n < 2 ) { return false ; } int sum = 0 , maxNum = 0 ; for (int num : nums) { sum += num; maxNum = Math.max(maxNum, num); } if (sum % 2 != 0 ) { return false ; } int target = sum / 2 ; if (maxNum > target) { return false ; } boolean [] dp = new boolean [target + 1 ]; dp[0 ] = true ; for (int i = 0 ; i < n; i++) { int num = nums[i]; for (int j = target; j >= num; --j) { dp[j] |= dp[j - num]; } } return dp[target]; } }
给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
提示:
0 <= s.length <= 3 * 10<sup>4</sup>
s[i] 为 '(' 或 ')'
示例 1:
输入: s = "(()"
输出: 2
解释: 最长有效括号子串是 "()"
示例 2:
输入: s = ")()())"
输出: 4
解释: 最长有效括号子串是 "()()"
示例 3:
输入: s = ""
输出: 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 33 34 35 class Solution { public int longestValidParentheses (String s) { if (s == null || s.isEmpty()) { return 0 ; } int maxans = 0 ; Stack<Integer> stack = new Stack <>(); stack.push(-1 ); for (int i = 0 ; i < s.length(); i++) { char c = s.charAt(i); if (c == '(' ) { stack.push(i); } else if (c == ')' ) { if (!stack.isEmpty()) { stack.pop(); if (!stack.isEmpty()) { maxans = Math.max(maxans, i - stack.peek()); } else { stack.push(i); } } } else { continue ; } } return maxans; } }
多维动态规划 给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
提示:
1 <= text1.length, text2.length <= 1000
text1 和 text2 仅由小写英文字符组成
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 示例 1: 输入:text1 = "abcde", text2 = "ace" 输出:3 解释:最长公共子序列是 "ace" ,它的长度为 3 。 示例 2: 输入:text1 = "abc", text2 = "abc" 输出:3 解释:最长公共子序列是 "abc" ,它的长度为 3 。 示例 3: 输入:text1 = "abc", text2 = "def" 输出:0 解释:两个字符串没有公共子序列,返回 0 。
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int longestCommonSubsequence (String text1, String text2) { int m = text1.length(); int n = text2.length(); int [][] dp = new int [m + 1 ][n + 1 ]; for (int i = 1 ; i <= m; i++) { for (int j = 1 ; j <= n; j++) { if (text1.charAt(i - 1 ) == text2.charAt(j - 1 )) { dp[i][j] = dp[i - 1 ][j - 1 ] + 1 ; } else { dp[i][j] = Math.max(dp[i - 1 ][j], dp[i][j -1 ]); } } } return dp[m][n]; }
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
提示:
1 <= m, n <= 100
题目数据保证答案小于等于 2 * 109
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 输入:m = 3, n = 7 输出:28 示例 2: 输入:m = 3, n = 2 输出:3 解释: 从左上角开始,总共有 3 条路径可以到达右下角。 1. 向右 -> 向下 -> 向下 2. 向下 -> 向下 -> 向右 3. 向下 -> 向右 -> 向下 示例 3: 输入:m = 7, n = 3 输出:28 示例 4: 输入:m = 3, n = 3 输出:6
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int uniquePaths (int m, int n) { int [][] dp = new int [m][n]; for (int i = 0 ; i < m; i++) { dp[i][0 ] = 1 ; } for (int i = 0 ; i < n; i++) { dp[0 ][i] = 1 ; } for (int i = 1 ; i < m; i++) { for (int j = 1 ; j < n; j++) { dp[i][j] = dp[i - 1 ][j] + dp[i][j - 1 ]; } } return dp[m - 1 ][n - 1 ]; }
给你一个字符串 s,找到 s 中最长的 回文 子串。
提示:
1 <= s.length <= 1000
s 仅由数字和英文字母组成
示例 1:
1 2 3 输入:s = "babad" 输出:"bab" 解释:"aba" 同样是符合题意的答案。
示例 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 38 39 40 41 42 43 44 45 46 47 48 public String longestPalindrome (String s) { int len = s.length(); if (len < 2 ) { return s; } int maxLen = 1 ; int begin = 0 ; boolean [][] dp = new boolean [len][len]; for (int i = 0 ; i < len; i++) { dp[i][i] = true ; } char [] charArray = s.toCharArray(); for (int L = 2 ; L <= len; L++) { for (int i = 0 ; i < len; i++) { int j = L + i - 1 ; if (j >= len) { break ; } if (charArray[i] != charArray[j]) { dp[i][j] = false ; } else { if (j - i < 3 ) { dp[i][j] = true ; } else { dp[i][j] = dp[i + 1 ][j - 1 ]; } } if (dp[i][j] && j - i + 1 > maxLen) { maxLen = j - i + 1 ; begin = i; } } } return s.substring(begin, begin + maxLen); }
给定一个包含非负整数的 <em>m</em> x <em>n</em> 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明: 每次只能向下或者向右移动一步。
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 200
0 <= grid[i][j] <= 200
示例 1:
输入: grid = [[1,3,1],[1,5,1],[4,2,1]]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。
示例 2:
输入: grid = [[1,2,3],[4,5,6]]
输出: 12
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public int minPathSum (int [][] grid) { if (grid == null || grid.length == 0 || grid[0 ].length == 0 ) { return 0 ; } int rows = grid.length, columns = grid[0 ].length; int [][] dp = new int [rows][columns]; dp[0 ][0 ] = grid[0 ][0 ]; for (int i = 1 ; i < rows; i++) { dp[i][0 ] = dp[i - 1 ][0 ] + grid[i][0 ]; } for (int j = 1 ; j < columns; j++) { dp[0 ][j] = dp[0 ][j - 1 ] + grid[0 ][j]; } for (int i = 1 ; i < rows; i++) { for (int j = 1 ; j < columns; j++) { dp[i][j] = Math.min(dp[i - 1 ][j], dp[i][j - 1 ]) + grid[i][j]; } } return dp[rows - 1 ][columns - 1 ]; } }
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
提示:
0 <= word1.length, word2.length <= 500
word1 和 word2 由小写英文字母组成
示例 1:
输入: word1 = "horse", word2 = "ros"
输出: 3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:
输入: word1 = "intention", word2 = "execution"
输出: 5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
代码 :
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 class Solution { public int minDistance (String word1, String word2) { int n = word1.length(); int m = word2.length(); if (n * m == 0 ) { return n + m; } int [][] D = new int [n + 1 ][m + 1 ]; for (int i = 0 ; i < n + 1 ; i++) { D[i][0 ] = i; } for (int j = 0 ; j < m + 1 ; j++) { D[0 ][j] = j; } for (int i = 1 ; i < n + 1 ; i++) { for (int j = 1 ; j < m + 1 ; j++) { int left = D[i - 1 ][j] + 1 ; int down = D[i][j - 1 ] + 1 ; int left_down = D[i - 1 ][j - 1 ]; if (word1.charAt(i - 1 ) != word2.charAt(j - 1 )) { left_down += 1 ; } D[i][j] = Math.min(left, Math.min(down, left_down)); } } return D[n][m]; } }
技巧 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
提示 :
1 <= nums.length <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
除了某个元素只出现一次以外,其余每个元素均出现两次。
示例 :
1 2 3 4 5 6 7 8 9 10 11 示例 1 : 输入:nums = [2,2,1] 输出:1 示例 2 : 输入:nums = [4,1,2,1,2] 输出:4 示例 3 : 输入:nums = [1] 输出:1
代码 :
1 2 3 4 5 6 7 8 public int singleNumber (int [] nums) { int single = 0 ; for (int num : nums) { single ^= num; } return single; }
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
提示: * n == nums.length
1 <= n <= 5 * 10<sup>4</sup>
-10<sup>9</sup> <= nums[i] <= 10<sup>9</sup>
进阶: 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
示例 1:
输入: nums = [3,2,3]
输出: 3
示例 2:
输入: nums = [2,2,1,1,1,2,2]
输出: 2
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public int majorityElement (int [] nums) { int count = 0 ; Integer candidate = null ; for (int num : nums) { if (count == 0 ) { candidate = num; } count += (num == candidate) ? 1 : -1 ; } return candidate; } }
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,**原地 **对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库内置的 sort 函数的情况下解决这个问题。
提示:
n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2
进阶:
示例 1:
输入: nums = [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
示例 2:
输入: nums = [2,0,1]
输出: [0,1,2]
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public void sortColors (int [] nums) { int n = nums.length; int ptr = 0 ; for (int i = 0 ; i < n; ++i) { if (nums[i] == 0 ) { int temp = nums[i]; nums[i] = nums[ptr]; nums[ptr] = temp; ++ptr; } } for (int i = ptr; i < n; ++i) { if (nums[i] == 1 ) { int temp = nums[i]; nums[i] = nums[ptr]; nums[ptr] = temp; ++ptr; } } } }
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须** 原地 **修改,只允许使用额外常数空间。
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 100
示例 1:
输入: nums = [1,2,3]
输出: [1,3,2]
示例 2:
输入: nums = [3,2,1]
输出: [1,2,3]
示例 3:
输入: nums = [1,1,5]
输出: [1,5,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 class Solution { public void nextPermutation (int [] nums) { int i = nums.length - 2 ; while (i >= 0 && nums[i] >= nums[i + 1 ]) { i--; } if (i >= 0 ) { int j = nums.length - 1 ; while (j >= 0 && nums[i] >= nums[j]) { j--; } swap(nums, i, j); } reverse(nums, i + 1 ); } public void swap (int [] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; } public void reverse (int [] nums, int start) { int left = start, right = nums.length - 1 ; while (left < right) { swap(nums, left, right); left++; right--; } } }
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
提示:
1 <= n <= 10<sup>5</sup>
nums.length == n + 1
1 <= nums[i] <= n
nums 中 只有一个整数 出现 两次或多次 ,其余整数均只出现 一次
进阶:
如何证明 nums 中至少存在一个重复的数字?
你可以设计一个线性级时间复杂度 O(n) 的解决方案吗?
示例 1:
输入: nums = [1,3,4,2,2]
输出: 2
示例 2:
输入: nums = [3,1,3,4,2]
输出: 3
示例 3 :
输入: nums = [3,3,3,3,3]
输出: 3
代码 :
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 class Solution {public : int findDuplicate (vector<int >& nums) { int n = nums.size(); int l = 1 , r = n - 1 , ans = -1 ; while (l <= r) { int mid = (l + r) >> 1 ; int cnt = 0 ; for (int i = 0 ; i < n; ++i) { cnt += nums[i] <= mid; } if (cnt <= mid) { l = mid + 1 ; } else { r = mid - 1 ; ans = mid; } } return ans; } }; class Solution { public int findDuplicate (int [] nums) { int slow = 0 , fast = 0 ; do { slow = nums[slow]; fast = nums[nums[fast]]; } while (slow != fast); slow = 0 ; while (slow != fast) { slow = nums[slow]; fast = nums[fast]; } return slow; } }