LeetcodeTop100 刷题总结(二)

LeetCode 热题 100:https://leetcode.cn/studyplan/top-100-liked/


文章目录

  • 八、二叉树
    • 94. 二叉树的中序遍历(递归与非递归)
    • 补充:144. 二叉树的前序遍历(递归与非递归)
    • 补充:145. 二叉树的后序遍历(递归与非递归)
    • 104. 二叉树的最大深度
    • 226. 翻转二叉树
    • 101. 对称二叉树
    • 543. 二叉树的直径
    • 102. 二叉树的层序遍历
    • 108. 将有序数组转换为二叉搜索树
    • 98. 验证二叉搜索树
    • 230. 二叉搜索树中第 K 小的元素
    • 199. 二叉树的右视图
    • 114. 二叉树展开为链表
    • 105. 从前序与中序遍历序列构造二叉树
    • 补充:106. 从中序与后序遍历序列构造二叉树
    • 437. 路径总和 III
    • 补充:112. 路径总和
    • 补充:113. 路径总和 II
    • 236. 二叉树的最近公共祖先
    • 124. 二叉树中的最大路径和
  • 九、图论
    • 200. 岛屿数量
    • 994. 腐烂的橘子
    • 207. 课程表
    • 补充:210. 课程表 II
    • 208. 实现 Trie (前缀树)
  • 十、回溯
    • 46. 全排列
    • 补充:47. 全排列 II
    • 78. 子集
    • 17. 电话号码的字母组合
    • 39. 组合总和
    • 补充:40. 组合总和 II
    • 补充:216. 组合总和 III
    • 22. 括号生成
    • 79. 单词搜索
    • 131. 分割回文串
    • 51. N 皇后


八、二叉树

二叉树的定义:

public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) {this.val = val;}TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}

94. 二叉树的中序遍历(递归与非递归)

在这里插入图片描述

递归方式:

class Solution {List<Integer> res = new ArrayList<>();/*** 输入:root = [1,null,2,3] * 输出:[1,3,2]*/public List<Integer> inorderTraversal(TreeNode root) {InOrder(root);return res;}public void InOrder(TreeNode root){if(root != null){InOrder(root.left);res.add(root.val);InOrder(root.right);}}
}

非递归方式:

class Solution {public List<Integer> inorderTraversal(TreeNode root) {Deque<TreeNode> stack = new LinkedList<>(); // 双端队列模拟栈List<Integer> list = new ArrayList<>();TreeNode p = root;while(p != null || !stack.isEmpty()){if(p != null){stack.push(p);p = p.left;}else{p = stack.pop();list.add(p.val);p = p.right;}}return list;}
}

补充:144. 二叉树的前序遍历(递归与非递归)

题目链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/description/

递归做法:

class Solution {List<Integer> list = new ArrayList<>();public List<Integer> preorderTraversal(TreeNode root) {preOrder(root);return list;}public void preOrder(TreeNode root){if(root != null){list.add(root.val);preOrder(root.left);preOrder(root.right);}}
}

非递归做法:

class Solution {public List<Integer> preorderTraversal(TreeNode root) {Deque<TreeNode> stack = new LinkedList<>(); // 双端队列模拟栈List<Integer> list = new ArrayList<>();TreeNode p = root;while(p != null || !stack.isEmpty()){if(p != null){list.add(p.val);stack.push(p);p = p.left;}else{p = stack.pop();p = p.right;}}return list;}
}

补充:145. 二叉树的后序遍历(递归与非递归)

题目链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/description/

递归做法:

class Solution {List<Integer> list = new ArrayList<>();public List<Integer> postorderTraversal(TreeNode root) {postOrder(root);return list;}public void postOrder(TreeNode root){if(root != null){postOrder(root.left);postOrder(root.right);list.add(root.val);}}
}

非递归做法:

class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();Deque<TreeNode> stack = new LinkedList<>();TreeNode p, r;p = root;r = null;while(p != null || !stack.isEmpty()){if(p != null){ // 走到最左边stack.push(p);p = p.left;}else{ // 向右p = stack.peek(); // 得到栈顶元素if(p.right != null && p.right != r){ // 右子树存在,且未访问p = p.right;}else{ // 否则弹出节点并访问p = stack.pop();list.add(p.val);r = p; // 记录最近访问节点p = null; // 节点访问后,重置 p}}}return list;}
}

104. 二叉树的最大深度

在这里插入图片描述

class Solution {/*** 输入:root = [3,9,20,null,null,15,7]* 输出:3*/public int maxDepth(TreeNode root) {return getMaxDepth(root);}private int getMaxDepth(TreeNode root) {if (root == null) {return 0;}int left = getMaxDepth(root.left);int right = getMaxDepth(root.right);return Math.max(left, right) + 1;}
}

226. 翻转二叉树

在这里插入图片描述

class Solution {public TreeNode invertTree(TreeNode root) {reverseTree(root);return root;}private void reverseTree(TreeNode root) {if (root == null) {return;}TreeNode temp = root.left;root.left = root.right;root.right = temp;reverseTree(root.left);reverseTree(root.right);}
}

101. 对称二叉树

在这里插入图片描述

class Solution {/*** 输入:root = [1,2,2,3,4,4,3]* 输出:true*/public boolean isSymmetric(TreeNode root) {if (root == null) {return true;}return recur(root.left, root.right);}public boolean recur(TreeNode l, TreeNode r) {if (l == null && r == null) {return true;}if (l == null || r == null || l.val != r.val) {return false;}return recur(l.left, r.right) && recur(l.right, r.left);}
}

543. 二叉树的直径

在这里插入图片描述

class Solution {int diam;/*** 输入:root = [1,2,3,4,5]* 输出:3* 解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。*/public int diameterOfBinaryTree(TreeNode root) {getMaxDepth(root);return diam;}public int getMaxDepth(TreeNode root) {if (root == null) {return 0;}int left = getMaxDepth(root.left);int right = getMaxDepth(root.right);diam = Math.max(diam, left + right);return Math.max(left, right) + 1;}
}

102. 二叉树的层序遍历

思路:使用 Deque 模拟队列数据结构,每次进队将一层节点出队,并将下一层节点进队。

在这里插入图片描述

class Solution {/*** 输入:root = [3,9,20,null,null,15,7]* 输出:[[3],[9,20],[15,7]]*/public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> res = new ArrayList<>();Deque<TreeNode> queue = new LinkedList<>();if (root != null) {queue.add(root);}while (!queue.isEmpty()) {int size = queue.size(); // 当前队列中节点数量 sizeList<Integer> list = new ArrayList<>();for (int i = size; i > 0; i--) { // 循环 size 次,存储该层节点TreeNode node = queue.remove();list.add(node.val);if (node.left != null) {queue.add(node.left);}if (node.right != null) {queue.add(node.right);}}res.add(list);}return res;}
}

注意:每次循环提前求出队列中节点数量,是因为如果没有提前求出 for (int i = 0; i < queue.size; i++),由于队列的节点数量是变化的,循环结束的条件会发生变化。


108. 将有序数组转换为二叉搜索树

题意要求:将有序数组转换为 平衡 二叉搜索树,这里可以借鉴二分查找的思路。

在这里插入图片描述  在这里插入图片描述

class Solution {/*** 输入:nums = [-10,-3,0,5,9]* 输出:[0,-3,9,-10,null,5]* 解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案*/public TreeNode sortedArrayToBST(int[] nums) {return buildBST(nums, 0, nums.length - 1);}private TreeNode buildBST(int[] nums, int left, int right) {if (left > right) {return null;}int mid = left - (left - right) / 2; // 防溢出TreeNode node = new TreeNode(nums[mid]);node.left = buildBST(nums, left, mid - 1);node.right = buildBST(nums, mid + 1, right);return node;}
}

98. 验证二叉搜索树

思路:二叉搜索树中序遍历是递增的,因此可以利用栈辅助完成中序遍历的同时来验证是否是二叉搜索树。

class Solution {/*** 输入:root = [2,1,3]* 输出:true*/public boolean isValidBST(TreeNode root) {Deque<TreeNode> stack = new LinkedList<>();long val = Long.MIN_VALUE;TreeNode p = root;while (!stack.isEmpty() || p != null) {if (p != null) {stack.push(p);p = p.left;} else {TreeNode top = stack.pop();if (val >= top.val) {return false;}val = top.val;p = top.right;}}return true;}
}

也可使用递归的思路:

class Solution {long max = Long.MIN_VALUE;public boolean isValidBST(TreeNode root) {if(root == null){return true;}if(!isValidBST(root.left)){return false;}if(root.val <= max){return false;}else{max = root.val;}return isValidBST(root.right);}
}

230. 二叉搜索树中第 K 小的元素

思路同上,使用栈进行中序遍历的同时,查询第 K 小的元素。

在这里插入图片描述

class Solution {/*** 输入:root = [3,1,4,null,2], k = 1* 输出:1*/public int kthSmallest(TreeNode root, int k) {Deque<TreeNode> stack = new LinkedList<>();TreeNode p = root;while (!stack.isEmpty() || p != null) {if (p != null) {stack.push(p);p = p.left;} else {p = stack.pop();if (--k == 0) {return p.val;}p = p.right;}}return 0;}
}

199. 二叉树的右视图

借鉴层序遍历算法,每次取每一层最后一个元素。

在这里插入图片描述

class Solution {/*** 输入: [1,2,3,null,5,null,4]* 输出: [1,3,4]*/public List<Integer> rightSideView(TreeNode root) {if (root == null) {return new ArrayList<>();}Deque<TreeNode> queue = new LinkedList<>();List<Integer> res = new ArrayList<>();queue.add(root);while (!queue.isEmpty()) {int size = queue.size();for (int i = size; i > 0; i--) {TreeNode node = queue.remove();if (node.left != null) {queue.add(node.left);}if (node.right != null) {queue.add(node.right);}if (i == 1) {res.add(node.val);}}}return res;}
}

114. 二叉树展开为链表

思路:
① 将左子树的最右节点指向右子树的根节点;
② 将左子树接到右子树的位置;
③ 处理右子树的根节点。
一直重复上边的过程,直到新的右子树为 null。

在这里插入图片描述

class Solution {/*** 输入:root = [1,2,5,3,4,null,6]* 输出:[1,null,2,null,3,null,4,null,5,null,6]*/public void flatten(TreeNode root) {TreeNode p = root;while (p != null) {if (p.left != null) {TreeNode pLeft = p.left;TreeNode pLeftRight = p.left;while (pLeftRight.right != null) { // 查询左子树的最右节点pLeftRight = pLeftRight.right;}pLeftRight.right = p.right;p.right = pLeft;p.left = null;}p = p.right;}}
}

105. 从前序与中序遍历序列构造二叉树

思路:团体程序设计天梯赛-练习集 L2-011 玩转二叉树 (25分) 先序中序建树 思路详解
在这里插入图片描述

class Solution {/*** 输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]* 输出: [3,9,20,null,null,15,7]*/public TreeNode buildTree(int[] preorder, int[] inorder) { // 先序,中序return build(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);}/*** 先序中序构建二叉树** @param preorder 先序遍历* @param preLeft 先序左边界* @param preRight 先序右边界* @param inorder 中序遍历* @param inLeft 中序左边界* @param inRight 中序右边界* @return 根节点*/public TreeNode build(int[] preorder, int preLeft, int preRight, int[] inorder, int inLeft, int inRight) {if (inLeft > inRight || preLeft > preRight) {return null;}TreeNode root = new TreeNode(preorder[preLeft]); // 先序遍历的第一个节点是根节点int rootInd = inLeft; // root 代表中序遍历数组中的根节点的索引for (int i = inLeft; i <= inRight; i++) {if (inorder[i] == preorder[preLeft]) {rootInd = i;break;}}int len = rootInd - inLeft; // 左子树的长度// 先序(根左右):左边界+1、左边界+长度,中序(左根右):左边界、根节点位置-1root.left = build(preorder, preLeft + 1, preLeft + len, inorder, inLeft, rootInd - 1);// 先序(根左右):左边界+长度+1、右边界,中序(左根右):根节点位置+1、右边界root.right = build(preorder, preLeft + len + 1, preRight, inorder, rootInd + 1, inRight);return root;}
}

补充:106. 从中序与后序遍历序列构造二叉树

题目链接:https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/description/

class Solution {/*** 输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]* 输出:[3,9,20,null,null,15,7]*/public TreeNode buildTree(int[] inorder, int[] postorder) {return build(postorder, 0, postorder.length - 1, inorder, 0, inorder.length - 1);}/*** 后序中序构建二叉树** @param postorder 后序遍历* @param postLeft 后序左边界* @param postRight 后序右边界* @param inorder 中序遍历* @param inLeft 中序左边界* @param inRight 中序右边界* @return 根节点*/private TreeNode build(int[] postorder, int postLeft, int postRight, int[] inorder, int inLeft, int inRight) {if (postLeft > postRight || inLeft > inRight) {return null;}TreeNode root = new TreeNode(postorder[postRight]);int rootInd = inLeft;for (int i = inLeft; i <= inRight; i++) {if(inorder[i] == postorder[postRight]){rootInd = i;break;}}int len = rootInd - inLeft; // 左子树节长度// 后序(左右根):左边界、左边界+长度-1;中序(左根右):左边界、根节点位置-1root.left = build(postorder, postLeft, postLeft + len -1, inorder, inLeft, rootInd-1);// 后序(左右根):左边界+长度、右边界-1,中序(左根右):根节点位置+1、右边界root.right = build(postorder, postLeft + len, postRight-1, inorder, rootInd + 1, inRight);return root;}
}

437. 路径总和 III

思路:以当前节点为止,每次去查看之前的的 map 集合中是否还存在目标前缀和。
   
   /
  
  /
  

假设目标和为 5,节点 1 的前缀和为:1,节点 3 的前缀和为: 1+2+3 = 6,那么 pre(3) - pre(1) = 5
所以从节点 1 到节点 3 之间有一条路径长度为 5。

在这里插入图片描述

class Solution {// key:前缀和,value:前缀和的数量(状态会恢复)Map<Long, Integer> map;/*** 输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8* 输出:3* 解释:和等于 8 的路径有 3 条*/public int pathSum(TreeNode root, int targetSum) {map = new HashMap<>();map.put(0l, 1);return backtrack(root, 0L, targetSum);}private int backtrack(TreeNode root, Long curr, int target) {if (root == null) {return 0;}int res = 0;curr += root.val;// 以当前节点为止,去查看从前的 map 集合中是否还存在目标前缀和res += map.getOrDefault(curr - target, 0);// 存储路径的原因是可能节点的前缀和存在相等的情况://              2//             ///            0//           ///          4// 从节点 2 到节点 4 有两条路径长度等于2map.put(curr, map.getOrDefault(curr, 0) + 1);int left = backtrack(root.left, curr, target);int right = backtrack(root.right, curr, target);res += left + right;// 状态恢复map.put(curr, map.get(curr) - 1);return res;}
}

补充:112. 路径总和

题目链接:https://leetcode.cn/problems/path-sum/description/

在这里插入图片描述

class Solution {/*** 输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22* 输出:true* 解释:等于目标和的根节点到叶节点路径如上图所示*/public boolean hasPathSum(TreeNode root, int targetSum) {return backtrack(root, 0, targetSum);}private boolean backtrack(TreeNode root, int curr, int target) {if (root == null) {return false;}curr += root.val;if (root.left == null && root.right == null) {return curr == target;}boolean left = backtrack(root.left, curr, target);boolean right = backtrack(root.right, curr, target);return left || right;}
}

补充:113. 路径总和 II

题目链接:https://leetcode.cn/problems/path-sum-ii/description/

在这里插入图片描述

class Solution {List<List<Integer>> res;/*** 输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22* 输出:[[5,4,11,2],[5,8,4,5]]*/public List<List<Integer>> pathSum(TreeNode root, int targetSum) {res = new ArrayList<>();backtrack(root, 0, targetSum, new ArrayList<>());return res;}private void backtrack(TreeNode root, int curr, int target, List<Integer> path) {if (root == null) {return;}curr += root.val;path.add(root.val);if (root.left == null && root.right == null && curr == target) {res.add(new ArrayList<>(path));}backtrack(root.left, curr, target, path);backtrack(root.right, curr, target, path);path.remove(path.size() - 1); // 恢复状态}
}

236. 二叉树的最近公共祖先

思路参考:236. 二叉树的最近公共祖先(DFS ,清晰图解)

在这里插入图片描述

class Solution {/*** 输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1* 输出:3* 解释:节点 5 和节点 1 的最近公共祖先是节点 3 。*/public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {return dfs(root, p, q);}private TreeNode dfs(TreeNode root, TreeNode p, TreeNode q) {if (root == null) {return null;}if (root == p || root == q) {return root;}TreeNode left = dfs(root.left, p, q);TreeNode right = dfs(root.right, p, q);if (left != null && right != null) {return root;} else if (left != null) {return left;} else if (right != null) {return right;} else {return null;}}
}

124. 二叉树中的最大路径和

在这里插入图片描述

class Solution {private int maxPath = Integer.MIN_VALUE;/*** 输入:root = [-10,9,20,null,null,15,7]* 输出:42* 解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42*/public int maxPathSum(TreeNode root) {getMaxPath(root);return maxPath;}private int getMaxPath(TreeNode root) {if (root == null) {return 0;}int leftMaxPath = Math.max(0, getMaxPath(root.left));int rightMaxPath = Math.max(0, getMaxPath(root.right));maxPath = Math.max(leftMaxPath + root.val + rightMaxPath, maxPath);return Math.max(leftMaxPath, rightMaxPath) + root.val;}
}

九、图论

200. 岛屿数量

class Solution {/*** 输入:grid = [*   ["1","1","1","1","0"],*   ["1","1","0","1","0"],*   ["1","1","0","0","0"],*   ["0","0","0","0","0"]* ]* 输出:1*/public int numIslands(char[][] grid) {int m = grid.length;int n = grid[0].length;int res = 0;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == '1') {dfs(grid, i, j, m, n);res++;}}}return res;}private void dfs(char[][] grid, int x, int y, int m, int n) {if (0 <= x && x < m && 0 <= y && y < n && grid[x][y] == '1') {grid[x][y] = '0';dfs(grid, x, y - 1, m, n); // 左上右下dfs(grid, x - 1, y, m, n);dfs(grid, x, y + 1, m, n);dfs(grid, x + 1, y, m, n);}}
}

994. 腐烂的橘子

思路:遍历的方式参考树的层序遍历,区别在于题目刚开始的入队的节点可能有多个。

在这里插入图片描述

class Solution {/*** 输入:grid = [[2,1,1],[1,1,0],[0,1,1]]* 输出:4*/public int orangesRotting(int[][] grid) {int m = grid.length;int n = grid[0].length;Deque<Point> queue = new LinkedList<>();int flesh = 0;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == 2) {queue.add(new Point(i, j));}if (grid[i][j] == 1) {flesh++;}}}if (flesh == 0) {return 0;}int res = 0;int[][] dirs = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}}; // 左上右下四个方向while (!queue.isEmpty() && flesh > 0) { // 注意:如果不用 flesh 做判断,队列中还存在节点,因此还会再循环一轮int size = queue.size();res++;for (int i = size; i > 0; i--) {Point point = queue.remove();int x = point.x;int y = point.y;for (int[] dir : dirs) {int newX = x + dir[0];int newY = y + dir[1];if (0 <= newX && newX < m && 0 <= newY && newY < n && grid[newX][newY] == 1) {grid[newX][newY] = 2;flesh--;queue.add(new Point(newX, newY));}}}}return flesh == 0 ? res : -1;}class Point {int x;int y;public Point() {}public Point(int x, int y) {this.x = x;this.y = y;}}
}

207. 课程表

思路:拓扑排序

class Solution {/*** 输入:numCourses = 2, prerequisites = [[1,0]]* 输出:true* 解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。*/public boolean canFinish(int numCourses, int[][] prerequisites) {List<List<Integer>> neighborTable = buildNeighborTable(numCourses, prerequisites);Deque<Integer> stack = new LinkedList<>(); // 用来存储入度为0的节点int count = 0; // 用来记录拓扑排序中的节点数量int[] indegree = new int[numCourses]; // 存储每个节点的入度for (int[] prerequisite : prerequisites) {indegree[prerequisite[0]]++;}for (int i = 0; i < numCourses; i++) {if (indegree[i] == 0) {stack.push(i);}}while (!stack.isEmpty()) {Integer pop = stack.pop();count++;for (Integer ind : neighborTable.get(pop)) { // 邻接表中当前节点所关联的节点入度-1indegree[ind]--;if (indegree[ind] == 0) {stack.push(ind);}}}return count == numCourses;}private List<List<Integer>> buildNeighborTable(int numCourses, int[][] prerequisites) {List<List<Integer>> neighborTable = new ArrayList<>(numCourses);for (int i = 0; i < numCourses; i++) {neighborTable.add(new ArrayList<>());}for (int i = 0; i < prerequisites.length; i++) { // 构造邻接表neighborTable.get(prerequisites[i][1]).add(prerequisites[i][0]);}return neighborTable;}
}

补充:210. 课程表 II

题目链接:https://leetcode.cn/problems/course-schedule-ii/description/

class Solution {public int[] findOrder(int numCourses, int[][] prerequisites) {List<List<Integer>> neighborTable = buildNeighborTable(numCourses, prerequisites);Deque<Integer> stack = new LinkedList<>(); // 用来存储入度为0的节点int count = 0; // 用来记录拓扑排序中的节点数量int[] topologies = new int[numCourses];int[] indegree = new int[numCourses]; // 存储每个节点的入度for (int[] prerequisite : prerequisites) {indegree[prerequisite[0]]++;}for (int i = 0; i < numCourses; i++) {if (indegree[i] == 0) {stack.push(i);}}while (!stack.isEmpty()) {Integer pop = stack.pop();topologies[count++] = pop;for (Integer ind : neighborTable.get(pop)) { // 邻接表中当前节点所关联的节点入度-1indegree[ind]--;if (indegree[ind] == 0) {stack.push(ind);}}}return count == numCourses ? topologies : new int[0];}private List<List<Integer>> buildNeighborTable(int numCourses, int[][] prerequisites) {List<List<Integer>> neighborTable = new ArrayList<>(numCourses);for (int i = 0; i < numCourses; i++) {neighborTable.add(new ArrayList<>());}for (int i = 0; i < prerequisites.length; i++) { // 构造邻接表neighborTable.get(prerequisites[i][1]).add(prerequisites[i][0]);}return neighborTable;}
}

208. 实现 Trie (前缀树)

class Trie {class Node {Boolean isEnd;Node[] next;public Node() {this.isEnd = false;this.next = new Node[26];}}private final Node root;public Trie() {root = new Node();}public void insert(String word) {Node node = root;for (char ch : word.toCharArray()) {if (node.next[ch - 'a'] == null) {node.next[ch - 'a'] = new Node();}node = node.next[ch - 'a'];}node.isEnd = true;}public boolean search(String word) {Node node = root;for (char ch : word.toCharArray()) {if (node.next[ch - 'a'] == null) {return false;}node = node.next[ch - 'a'];}return node.isEnd;}public boolean startsWith(String prefix) {Node node = root;for (char ch : prefix.toCharArray()) {if (node.next[ch - 'a'] == null) {return false;}node = node.next[ch - 'a'];}return true;}
}/*** Your Trie object will be instantiated and called as such:* Trie obj = new Trie();* obj.insert(word);* boolean param_2 = obj.search(word);* boolean param_3 = obj.startsWith(prefix);*/

十、回溯

46. 全排列

题目:给定一个 不含重复数字 的数组 nums ,返回其所有可能的全排列。你可以按任意顺序返回答案。

在这里插入图片描述

class Solution {/*** 输入:nums = [1,2,3]* 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]*/public List<List<Integer>> permute(int[] nums) {List<List<Integer>> res = new ArrayList<>();int len = nums.length;boolean[] visited = new boolean[len];dfs(res, nums, visited, new ArrayList<>());return res;}private void dfs(List<List<Integer>> res, int[] nums, boolean[] visited, List<Integer> list) {if (list.size() == nums.length) {res.add(new ArrayList<>(list));return;}for (int i = 0; i < nums.length; i++) {if (!visited[i]) {list.add(nums[i]);visited[i] = true;dfs(res, nums, visited, list);list.remove(list.size() - 1);visited[i] = false;}}}
}

补充:47. 全排列 II

题目链接:https://leetcode.cn/problems/permutations-ii/

题目:给定一个 包含重复数字 的数组 nums ,返回其所有可能的全排列。你可以按任意顺序返回答案。

在这里插入图片描述

class Solution {/*** 输入:nums = [1,1,2]* 输出:[[1,1,2],[1,2,1],[2,1,1]]*/public List<List<Integer>> permuteUnique(int[] nums) {List<List<Integer>> res = new ArrayList<>();Arrays.sort(nums); // 排序boolean[] visited = new boolean[nums.length];backtrack(visited, nums, new ArrayList<>(), res);return res;}private void backtrack(boolean[] visited, int[] nums, List<Integer> list, List<List<Integer>> res) {if (list.size() == nums.length) {res.add(new ArrayList<>(list));return;}for (int i = 0; i < nums.length; i++) {if (!visited[i]) {if (i > 0 && nums[i] == nums[i - 1] && visited[i - 1] == false) { // 剪枝continue;}visited[i] = true;list.add(nums[i]);backtrack(visited, nums, list, res);list.remove(list.size() - 1);visited[i] = false;}}}
}

78. 子集

在这里插入图片描述

class Solution {/*** 输入:nums = [1,2,3] * 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]*/public List<List<Integer>> subsets(int[] nums) {List<List<Integer>> res = new ArrayList<>();int len = nums.length;boolean[] visited = new boolean[len];dfs(nums, 0, new ArrayList<>(), res);return res;}private void dfs(int[] nums, int ind, List<Integer> list, List<List<Integer>> res) {if (ind >= nums.length) {res.add(new ArrayList<>(list));return;}list.add(nums[ind]);dfs(nums, ind + 1, list, res);list.remove(list.size() - 1);dfs(nums, ind + 1, list, res);}
}

17. 电话号码的字母组合

class Solution {Map<Character, String> map = new HashMap<>();String digits;/*** 输入:digits = "23"* 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]*/public List<String> letterCombinations(String digits) {if (digits.length() == 0) {return new ArrayList<>();}map.put('2', "abc");map.put('3', "def");map.put('4', "ghi");map.put('5', "jkl");map.put('6', "mno");map.put('7', "pqrs");map.put('8', "tuv");map.put('9', "wxyz");this.digits = digits;List<String> res = new ArrayList<>();backtrack(0, new StringBuilder(), res);return res;}private void backtrack(int ind, StringBuilder builder, List<String> res) {if (ind == digits.length()) {res.add(new String(builder));return;}String words = map.get(digits.charAt(ind));for (char ch : words.toCharArray()) {builder.append(ch);backtrack(ind+1, builder, res);builder.deleteCharAt(builder.length() - 1);}}
}

39. 组合总和

题目: 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有不同组合,并以列表形式返回。你可以按任意顺序返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

class Solution {int[] candidates;/*** 输入:candidates = [2,3,6,7], target = 7* 输出:[[2,2,3],[7]]* 解释:* 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。* 7 也是一个候选, 7 = 7 。* 仅有这两种组合。*/public List<List<Integer>> combinationSum(int[] candidates, int target) {this.candidates = candidates;List<List<Integer>> res = new ArrayList<>();Arrays.sort(candidates);backtrack(target, 0, new ArrayList<>(), res);return res;}private void backtrack(int target, int ind, List<Integer> temp, List<List<Integer>> res) {if (target < 0) {return;}if (target == 0) {res.add(new ArrayList<>(temp));return;}for (int i = ind; i < candidates.length; i++) {if (target - candidates[i] < 0) {break;}temp.add(candidates[i]);backtrack(target - candidates[i], i, temp, res);temp.remove(temp.size() - 1);}}
}

补充:40. 组合总和 II

题目链接:https://leetcode.cn/problems/combination-sum-ii/description/

与上道题目不同的是,解集不能包含重复的组合, 参考:全排列 II:

class Solution {int[] candidates;/*** 输入: candidates = [10,1,2,7,6,1,5], target = 8,* 输出: [[1,1,6],[1,2,5],[1,7],[2,6]]*/public List<List<Integer>> combinationSum2(int[] candidates, int target) {this.candidates = candidates;List<List<Integer>> res = new ArrayList<>();boolean[] visited = new boolean[candidates.length];Arrays.sort(candidates);backtrack(target, 0, visited, new ArrayList<>(), res);return res;}private void backtrack(int target, int ind, boolean[] visited, List<Integer> list, List<List<Integer>> res) {if (target < 0) {return;}if (target == 0) {res.add(new ArrayList<>(list));return;}for (int i = ind; i < candidates.length; i++) {if (!visited[i]) {if (target - candidates[i] < 0) {break;}if (i != 0 && candidates[i - 1] == candidates[i] && !visited[i - 1]) {continue;}list.add(candidates[i]);visited[i] = true;backtrack(target - candidates[i], i + 1, visited, list, res);visited[i] = false;list.remove(list.size() - 1);}}}
}

补充:216. 组合总和 III

题目链接:https://leetcode.cn/problems/combination-sum-iii/description/

题目:找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
① 只使用数字 1 到 9
每个数字最多使用一次
③ 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

class Solution {List<List<Integer>> res;int size;/*** 输入: k = 3, n = 7* 输出: [[1,2,4]]* 解释: 1 + 2 + 4 = 7 没有其他符合的组合了。*/public List<List<Integer>> combinationSum3(int k, int n) {this.res = new ArrayList<>();this.size = k;backtrack(1, n, new ArrayList<>());return res;}private void backtrack(int ind, int target, List<Integer> list) {if (target == 0) {if (size == list.size()) {res.add(new ArrayList<>(list));}return;}for (int i = ind; i < 10; i++) {if (target - i < 0) {break;}list.add(i);backtrack(i + 1, target - i, list);list.remove(list.size() - 1);}}
}

22. 括号生成

思路:每次优先选左括号,然后再选右括号。

class Solution {/*** 输入:n = 3* 输出:["((()))","(()())","(())()","()(())","()()()"]*/public List<String> generateParenthesis(int n) {List<String> res = new ArrayList<>();backtrack(n, n, new StringBuilder(), res);return res;}private void backtrack(int left, int right, StringBuilder builder, List<String> res) {if (left > right) { // 左括号少,剪枝return;}if (left == 0 && right == 0) {res.add(builder.toString());return;}if (left != 0) { // 优先选左builder.append('(');backtrack(left - 1, right, builder, res);builder.deleteCharAt(builder.length() - 1);}builder.append(')');backtrack(left, right - 1, builder, res);builder.deleteCharAt(builder.length() - 1);}
}

79. 单词搜索

在这里插入图片描述

class Solution {/*** 输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"* 输出:true*/public boolean exist(char[][] board, String word) {int m = board.length;int n = board[0].length;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (board[i][j] == word.charAt(0) && dfs(word, i, j, 0, board)) {return true;}}}return false;}private boolean dfs(String word, int x, int y, int ind, char[][] board) {if (ind >= word.length()) {return true;}if (0 <= x && x < board.length && 0 <= y && y < board[0].length && board[x][y] == word.charAt(ind)&& board[x][y] != '\0') {char ch = board[x][y];board[x][y] = '\0';boolean left = dfs(word, x, y - 1, ind + 1, board); // 左上右下boolean up = dfs(word, x - 1, y, ind + 1, board);boolean right = dfs(word, x, y + 1, ind + 1, board);boolean down = dfs(word, x + 1, y, ind + 1, board);board[x][y] = ch;return left || up || right || down;}return false;}
}

131. 分割回文串

class Solution {List<List<String>> res;public List<List<String>> partition(String s) {res = new ArrayList<>();backtrack(s, 0, new ArrayList<>());return res;}private void backtrack(String s, int ind, List<String> list) {if (ind >= s.length()) {res.add(new ArrayList<>(list));return;}for (int i = ind; i < s.length(); i++) {if (isPalindrome(s, ind, i)) {list.add(s.substring(ind, i + 1));backtrack(s, i + 1, list);list.remove(list.size() - 1);}}}private boolean isPalindrome(String s, int start, int end) {while (start <= end) {if (s.charAt(start) != s.charAt(end)) {return false;}start++;end--;}return true;}
}

51. N 皇后

在这里插入图片描述

class Solution {/*** 输入:n = 4* 输出:[[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]* 解释:如上图所示,4 皇后问题存在两个不同的解法。*/public List<List<String>> solveNQueens(int n) {List<List<String>> res = new ArrayList<>();int[] queenCols = new int[n]; // {1,3,0,2} 存储第i行的皇后在第 queenCols.get(i) 列dfs(0, n, queenCols, res);return res;}private List<String> convert(int[] queenCols, int n) {List<String> solution = new ArrayList<>();// {1,3,0,2} -> [".Q..","...Q","Q...","..Q."],for (Integer col : queenCols) {char[] temp = new char[n];Arrays.fill(temp, '.');temp[col] = 'Q';solution.add(new String(temp));}return solution;}private void dfs(int row, int n, int[] queenCols, List<List<String>> res) {if (row == n) {res.add(convert(queenCols, n));return;}for (int col = 0; col < n; col++) { // 对当前行的每一列进行遍历if (!isValid(queenCols, row, col)) {continue;}queenCols[row] = col;dfs(row + 1, n, queenCols, res);queenCols[row] = -1;}}private boolean isValid(int[] queenCols, int row, int col) {for (int i = 0; i < row; i++) {if (queenCols[i] == col || row - i == col - queenCols[i] || row - i == queenCols[i] - col) {// 同一列;同'\'方向,“行-皇后所在行 == 列-皇后所在列”;同'/'方向,“行-皇后所在行 == 皇后所在列-列”return false;}}return true;}
}

以上解决方案也适用于:
面试题 08.12. 八皇后:https://leetcode.cn/problems/eight-queens-lcci/description/
52. N 皇后 II:https://leetcode.cn/problems/n-queens-ii/description/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/147777.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

注册商标的有关流程

注册商标的有关流程 在商业活动中&#xff0c;商标作为企业品牌的重要组成部分&#xff0c;不仅代表着企业的形象和信誉&#xff0c;更是企业资产的重要部分。因此&#xff0c;了解并遵循注册商标的流程&#xff0c;对于保护企业的合法权益至关重要。 一、确定商标注册范围并进…

大模型学习方向不知道的,看完这篇学习思路好清晰!!

入门大模型并没有想象中复杂&#xff0c;尤其对于普通程序员&#xff0c;建议采用从外到内的学习路径。下面我们通过几个步骤来探索如何系统学习大模型&#xff1a; 1️⃣初步理解应用场景与人才需求 大模型的核心应用涵盖了智能体&#xff08;AI Agent&#xff09;、微调&…

【TPAMI 2024】告别误差,OPAL算法如何让光场视差估计变得轻而易举?

题目&#xff1a;OPAL: Occlusion Pattern Aware Loss for Unsupervised Light Field Disparity Estimation OPAL&#xff1a;面向无监督光场视差估计的遮挡模式感知损失 作者&#xff1a;Peng Li; Jiayin Zhao; Jingyao Wu; Chao Deng; Yuqi Han; Haoqian Wang; Tao Yu 摘要…

一个永久的.NET渗透工具和知识仓库

01前言 为了更好地应对基于.NET技术栈的风险识别和未知威胁&#xff0c;.NET安全攻防帮会从创建以来一直聚焦于.NET领域的安全攻防技术&#xff0c;定位于高质量安全攻防社区&#xff0c;也得到了许多师傅们的支持和信任&#xff0c;通过帮会深度连接入圈的师傅们&#xff0c;…

计算机毕业设计推荐-基于PHP的律所预约服务管理系统

精彩专栏推荐订阅&#xff1a;在下方主页&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、基于PHP的律…

61.【C语言】数据在内存中的存储

1.前置知识 整数在内存中以补码形式存储 有符号整数三种码均有符号位,数值位 正整数:原码反码补码 负整数:原码≠反码≠补码 2.解释 int arr[] {1,2,3,4,5}; VSx86Debug环境下,内存窗口输入&arr VSx64Debug环境下,内存窗口输入&arr 存放的顺序都一样,均是小端序…

路由基础--路由引入

路由引入的主要作用是实现路由信息在不同路由协议之间的传递和学习。在大型企业网络中&#xff0c;多种路由协议共存是常态&#xff0c;为了实现全网互通&#xff0c;需要通过路由引入来传递路由信息。此外&#xff0c;执行路由引入时还可以部署路由控制&#xff0c;从而实现对…

Leetcode 2464. 有效分割中的最少子数组数目

1.题目基本信息 1.1.题目描述 给定一个整数数组 nums。 如果要将整数数组 nums 拆分为 子数组 后是 有效的&#xff0c;则必须满足: 每个子数组的第一个和最后一个元素的最大公约数 大于 1&#xff0c;且 nums 的每个元素只属于一个子数组。 返回 nums 的 有效 子数组拆分中…

【数据结构】Java的HashMap 和 HashSet 大全笔记,写算法用到的时候翻一下,百度都省了!(实践篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

ESP32Cam人工智能教学22

ESP32Cam人工智能教学22 在线车牌识别装置 在第十六课《tencent-OCR》中&#xff0c;已经学会了使用腾讯在线识别车牌&#xff0c;但是用的是电脑中的Python程序&#xff0c;读取一张车牌图片内容&#xff0c;然后发送给腾讯服务器进行识别&#xff0c;并获取返回的识别结果。…

基于yolov5滑块识别破解(一)

由于内容较长&#xff0c;将分为两个部分来说明&#xff0c;本文讲解yolov5的部署与训练。 1.YOLOv5部署 云端部署&#xff08;训练&#xff09; 服务器创建 如果自己的显卡算力不是很好的&#xff0c;或者是核显电脑&#xff0c;可以租用算力&#xff0c;价格还行一块钱左右就…

教你一招:在微信小程序中为用户上传的图片添加时间水印

在微信小程序开发过程中&#xff0c;我们常常需要在图片上添加水印&#xff0c;以保护版权或增加个性化元素。本文将为大家介绍如何在微信小程序中为图片添加时间水印&#xff0c;让你的小程序更具特色。 实现步骤&#xff1a; 1. 创建页面结构 在pages目录下创建一个名为upl…

springboot项目今日指数 -- 工程可用性测试

2. 编写测试 在这里我们编写一个测试文件通过用户名查询到用户信息 一. 编写service层 创建SysUserService接口 import com.jixu.stock.pojo.entity.SysUser;public interface SysUserService {public SysUser getUserByName(String username); }创建实现类 import com.ji…

Python酷库之旅-第三方库Pandas(124)

目录 一、用法精讲 551、pandas.DataFrame.notna方法 551-1、语法 551-2、参数 551-3、功能 551-4、返回值 551-5、说明 551-6、用法 551-6-1、数据准备 551-6-2、代码示例 551-6-3、结果输出 552、pandas.DataFrame.notnull方法 552-1、语法 552-2、参数 552-3…

为了不再被事务坑,我读透了Spring的事务传播性。

在之前文章中&#xff0c;我们已经被事务坑了两次&#xff1a; mq发送消息之后&#xff0c;业务代码回滚&#xff0c;导致发了一条中奖消息给用户&#xff01;&#xff01; 我又被Spring的事务坑了&#xff0c;用户兑奖之后&#xff0c;什么东西都没收到&#xff01;&#xf…

【高阶用法】uniapp的i18n/修复/增强/App无重启更换语言

痛点 在i18n多语言模块使用过程中&#xff0c;发现下面几个问题&#xff0c;需要解决 1&#xff09;uni-best框架下&#xff0c;$t功能函数无法实时的切换语言&#xff0c;可能跟使用有关 2&#xff09;uni-best建议的translate方式在vue块外使用太繁琐&#xff0c;希望不用…

10年计算机考研408-计算机网络

【题33】下列选项中&#xff0c;不属于网络体系结构所描述的内容是&#xff08;&#xff09; A.网络的层次 B.每一层使用的协议 C.协议的内部实现细节 D.每一层必须完成的功能 解析&#xff1a; 本题考查的是网络体系结构相关的概念。 图1描述了网络的7层架构以及每一层所要完成…

防火墙详解(一) 网络防火墙简介

原文链接&#xff1a;https://blog.csdn.net/qq_46254436/article/details/105519624 文章目录 定义 与路由器和交换机的区别 发展历史 防火墙安全区域 定义 防火墙主要用于保护一个网络区域免受来自另一个网络区域的网络攻击和网络入侵行为 “防火墙”一词起源于建筑领域&…

Openai gym environment for multi-agent games

题意&#xff1a;用于多智能体游戏的 OpenAI Gym 环境 问题背景&#xff1a; Is it possible to use openais gym environments for multi-agent games? Specifically, I would like to model a card game with four players (agents). The player scoring a turn starts the…

8月份工业机器人产量同比增长20%

近日&#xff0c;国家统计局公布数据显示&#xff0c;8月份&#xff0c;我国工业机器人产量为47947套&#xff0c;较去年同期增长20%&#xff1b;1-8月份&#xff0c;总产量为360592套&#xff0c;较去年同期增长9.9%。 9月14日&#xff0c;国家统计局发布数据显示&#xff0c;…