- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
所有题目都在我的简书上有详细注释,这里用于记录思路和总结不能bugfree的原因
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
四.BFS
1.岛屿的个数
思路:for循环所有岛屿,对每一个为1的岛屿进行BFS,将周围岛屿标记为0
注意:越界检查,坐标数组
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Coordinate { int x; int y; public Coordinate(int x, int y) { this.x = x; this.y = y; } } public class Solution { /** * @param grid a boolean 2D matrix * @return an integer */ public int numIslands(boolean[][] grid) { if (grid == null || grid.length == 0 || grid[0].length == 0) { return 0; } int m = grid.length; int n = grid[0].length; int count = 0; for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { if (grid[i][j]) { islandBST(grid, i, j); count++; } } } return count; } private void islandBST(boolean[][] grid, int x, int y) { int[] detalx = {0, 0, 1, -1}; int[] detaly = {1, -1, 0, 0}; Queue<Coordinate> queue = new LinkedList<>(); queue.offer(new Coordinate(x, y)); grid[x][y] = false; while (!queue.isEmpty()) { Coordinate coor = queue.poll(); for (int direction = 0; direction < 4; direction++) { Coordinate newCoor = new Coordinate( coor.x + detalx[direction], coor.y + detaly[direction]); if (!inBound(grid, newCoor.x, newCoor.y)) { continue; } if (grid[newCoor.x][newCoor.y]) { grid[newCoor.x][newCoor.y] = false; queue.offer(newCoor); } } } } private boolean inBound(boolean[][] grid, int x, int y) { int m = grid.length; int n = grid[0].length; return x >= 0 && x < m && y >= 0 && y < n; } }
一刷:BFS模板中遍历当前结点周围结点时忘记将周围结点加入队列
2.二叉树的层次遍历
思路:从root开始,把当前结点加入队列,对左右儿子遍历,若儿子存在加入队列
注意:1.size代表每一层结点的个数,只有本层遍历完才更新;
2.把结点从队列抛出后再将结点值加入数组,不需要在把结点加入队列时进行值的添加;
3.leve值添加到results时不需要deep copy,因为每一层结束时,while循环都要重新判断队列是否为空,重新创建level;
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> results = new ArrayList<>(); if (root == null) { return results; } Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { List<Integer> level = new ArrayList<>(); int size = queue.size(); for (int i = 0; i < size; i++) { TreeNode head = queue.poll(); level.add(head.val); if (head.left != null) {; queue.offer(head.left); } if (head.right != null) { queue.offer(head.right); } } results.add(level); } return results; } }
3.
9.二叉树的序列化和反序列化
思路:用队列来控制序列化和反序列化的顺序,理解了序列化基本上大体思路就没什么问题,但细节方面很难写
注意:字符用单引号,字符串用双引号
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Solution { public String serialize(TreeNode root) { if (root == null) { return "{}"; } ArrayList<TreeNode> queue = new ArrayList<TreeNode>(); queue.add(root); for (int i = 0; i < queue.size(); i++) { TreeNode node = queue.get(i); if (node == null) { continue; } queue.add(node.left); queue.add(node.right); } while (queue.get(queue.size() - 1) == null) { queue.remove(queue.size() - 1); } StringBuilder sb = new StringBuilder(); sb.append("{"); sb.append(queue.get(0).val); for (int i = 1; i < queue.size(); i++) { if (queue.get(i) == null) { sb.append(",#"); } else { sb.append(","); sb.append(queue.get(i).val); } } sb.append("}"); return sb.toString(); } public TreeNode deserialize(String data) { if (data.equals("{}")) { return null; } String[] vals = data.substring(1, data.length() - 1).split(","); ArrayList<TreeNode> queue = new ArrayList<TreeNode>(); TreeNode root = new TreeNode(Integer.parseInt(vals[0])); boolean isLeftChild = true; int index = 0; queue.add(root); for (int i = 1; i < vals.length; i++) { if (!vals[i].equals("#")) { TreeNode node = new TreeNode(Integer.parseInt(vals[i])); if (isLeftChild) { queue.get(index).left = node; } else { queue.get(index).right = node; } queue.add(node); } if (!isLeftChild) { index++; } isLeftChild = !isLeftChild; } return root; } }
一刷:1.单引号双引号的使用,没搞清
2.忘记将已经计算过的结点添加到队列中去
3.queue在本题中采用ArrayList实现,读取结点时不能用queue.poll(),用queue.get()
三. 二叉树
1.Subtree with Maximum Average
思路:分治+遍历,定义全局变量记录最大平均值,比较套路化的题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
/** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */ public class Solution { public class ResultType { int sum; int size; public ResultType(int sum, int size) { this.sum = sum; this.size = size; } } private TreeNode subtree = null; private ResultType subtreeResult = null; public TreeNode findSubtree2(TreeNode root) { helper(root); return subtree; } private ResultType helper(TreeNode root) { if (root == null) { return new ResultType(0, 0); } ResultType left = helper(root.left); ResultType right = helper(root.right); ResultType result = new ResultType(left.sum + right.sum + root.val, left.size + right.size + 1); if (subtree == null || result.size * subtreeResult.sum < result.sum * subtreeResult.size) { subtree = root; subtreeResult = result; } return result; } }
一刷:helper函数在每一轮递归都要调用,返回结果用于辅助计算,应返回当前结点对应的resultType信息,方法定义多了有时就有点混乱
二.二分法
1.排序数组中最接近元素
思路:数组中不一定存在target,分三种情况target < A[0], target > A[A.length - 1], target在数组中
注意:1.二分后检查end, start的条件语句
2.先查end,则比较index和index -1,先查start,则比较index和index + 1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int closestNumber(int[] A, int target) { if (A == null || A.length == 0) { return -1; } int index = findIndex(A, target); if (index == 0) { return 0; } if (index == A.length) { return A.length - 1; } if (Math.abs(A[index] - target) < Math.abs(A[index - 1] - target)) { return index; } else { return index - 1; } } private int findIndex(int[] A, int target) { int start = 0; int end = A.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] == target) { return mid; } if (A[mid] < target) { start = mid; } if (A[mid] > target) { end = mid; } } if (A[end] >= target) { return end; } if (A[start] >= target) { return start; } return A.length; } }
一刷:bugfree
2.目标最后位置
思路:典型二分法模板
注意:二分完检查时先检查end
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int lastPosition(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int start = 0; int end = nums.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (nums[mid] == target) { start = mid; } if (nums[mid] < target) { start = mid; } if (nums[mid] > target) { end = mid; } } if (nums[end] == target) { return end; } if (nums[start] == target) { return start; } return -1; } }
一刷:nums[mid] == target时,错写end = mid,应写start = mid
3.搜索二维矩阵
思路:第一种:先二分确定行,再二分确定列
第二种:由于二维矩阵的排列方式可以当做一个一维数组来寻找位置,行乘列确定下标,对下标二分
注意:
第一种:在检查行时要用if ,else if来排序,先检查end;如果用多个if检查后面的start检查row = start会覆盖前面的row = end
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public boolean searchMatrix(int[][] matrix, int target) { if (matrix == null || matrix.length == 0) { return false; } if (matrix[0] == null || matrix[0].length == 0) { return false; } int row = matrix.length; int column = matrix[0].length; int start = 0; int end = row - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (matrix[mid][0] == target) { return true; } if (matrix[mid][0] < target) { start = mid; } if (matrix[mid][0] > target) { end = mid; } } if (matrix[end][0] <= target) { row = end; } else if (matrix[start][0] <= target) { row = start; } else { return false; } start = 0; end = column - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (matrix[row][mid] == target) { return true; } if (matrix[row][mid] < target) { start = mid; } if (matrix[row][mid] > target) { end = mid; } } if (matrix[row][end] == target) { return true; } if (matrix[row][start] == target) { return true; } return false; } }
一刷:多个if使用和if else使用没注意,在if 语句中没有return值时,不能写成多个if形式要用if else语句
第二种:
注意:mid在二维数组中对应的数值数字这样写的matrix[mid / column][mid % column]
4.Maximum Number in Mountain Sequence
注意:题目中不存在相等的点
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int mountainSequence(int[] nums) { if (nums == null || nums.length == 0) { return -1; } int start = 0; int end = nums.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (nums[mid] < nums[mid + 1]) { start = mid; } else { end = mid; } } return Math.max(nums[start], nums[end]); } }
5.在大数组中查找
思路:二分法的简单变形,需要测试大小来确定end值
注意:接口调用别写错了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int searchBigSortedArray(ArrayReader reader, int target) { int index = 1; while (reader.get(index) < target) { index = index * 2; } int start = 0; int end = index; while (start + 1 < end) { int mid = start + (end - start) / 2; if (reader.get(mid) == target) { end = mid; } else if (reader.get(mid) < target) { start = mid; } else { end = mid; } } if (reader.get(start) == target) { return start; } if (reader.get(end) == target) { return end; } return -1; } }
一刷:while写成了if
6.寻找旋转排序数组中的最小值
思路:最后一个元素为target,将数组两分,用二分法找到分界点
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int findMin(int[] nums) { if (nums == null || nums.length == 0) { return -1; } int start = 0; int end = nums.length - 1; int target = nums[nums.length - 1]; while (start + 1 < end) { int mid = start + (end - start) / 2; if (nums[mid] < target) { end = mid; } if (nums[mid] > target) { start = mid; } } if (nums[start] <= nums[end]) { return nums[start]; } else { return nums[end]; } } }
一刷:题目要求返回值,写成了返回下标
7.寻找峰值
思路:同题目3
注意:题目表明相邻位置的数值不同
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Solution { public int findPeak(int[] A) { int start = 1; int end = A.length - 2; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] < A[mid + 1]) { start = mid; } else { end = mid; } } if (A[start] >= A[end]) { return start; } else { return end; } } }
一刷:题目要求下标,返回了值
8.第一个错误的代码版本
思路:first position的小花样
注意:题目要求尽可能少调用isBadVersion(),所以能用else的就不要再写个if
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Solution { public int findFirstBadVersion(int n) { int start = 1; int end = n; while (start + 1 < end) { int mid = start + (end - start) / 2; if (SVNRepo.isBadVersion(mid) == true) { end = mid; } else { start = mid; } } if (SVNRepo.isBadVersion(start)) { return start; } else { return end; } } }
一刷:isBadVersion()调用太多
9.搜索旋转排序数组
思路:应该寻找一个数和A[mid]比较将数组分为两部分,这个数可以取A[start];接下来再用二分法在两段区间上寻找target
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int search(int[] A, int target) { if (A == null || A.length == 0) { return -1; } int start = 0; int end = A.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[start] < A[mid]) { if (A[mid] == target) { return mid; } else if (A[mid] >= target && A[start] <= target) { end = mid; } else { start = mid; } } if (A[start] > A[mid]) { if (A[mid] == target) { return mid; } else if (A[mid] <= target && A[end] >= target) { start = mid; } else { end = mid; } } } if (A[end] == target) { return end; } if (A[start] == target) { return start; } return -1; } }
一刷:思路不清晰
10.目标出现总和
思路:数组本身排好序,找first position和last position,count = endIndex - startIndex + 1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int totalOccurrence(int[] A, int target) { if (A == null || A.length == 0) { return 0; } int firstIndex = findFirstIndex(A, target); int lastIndex = findLastIndex(A, target); int count; if (firstIndex == -1 || lastIndex == -1) { count = 0; return count; } count = lastIndex - firstIndex + 1; return count; } private int findFirstIndex(int[] A, int target) { int start = 0; int end = A.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] == target) { end = mid; } else if (A[mid] < target) { start = mid; } else { end = mid; } } if (A[start] == target) { return start; } if (A[end] == target){ return end; } return -1; } private int findLastIndex(int[] A, int target) { int start = 0; int end = A.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] == target) { start = mid; } else if (A[mid] < target) { start = mid; } else { end = mid; } } if (A[end] == target) { return end; } if (A[start] == target) { return start; } return -1; } }
bugfree
11.Drop Egg
思路:代码不长但比较难理解的题,优化最坏情况,用第一个鸡蛋判断区间,第二个鸡蛋判断层数,每增加一层区间,该区间的层数就减一,可记住做法
注意:ans应为long型,不然n很大时,ans可能存在比n大情形,越界
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int dropEggs(int n) { long ans = 0; int count = 0; while (ans < n) { count++; ans += count; } return count; } }
一刷:count写在了ans += count后面,多计了一次
12.二分查找
思路: find first position
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Solution { public int binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int start = 0; int end = nums.length - 1; while (start + 1 < end) { int mid = (start + end) / 2; if(nums[mid] >= target) { end = mid; } if (nums[mid] < target) { start = mid; } } if (nums[start] == target) { return start; } if (nums[end] == target) { return end; } return -1; } }
bugfree
13.在排序数组中找最接近的K个数
思路:首先先在数组中找到一个等于target的index,然后向两边遍历,找足k个点;要考虑index在第一个点和最后一个点的特殊情况
注意:数组可以存在重复元素,但index无论取重复元素中的哪一个,结果都是一样的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public int[] kClosestNumbers(int[] A, int target, int k) { int[] results = new int[k]; if (A == null || A.length == 0) { return results; } if (A.length < k) { return A; } int index = findIndex(A, target); int start = index - 1; int end = index; for (int i = 0; i < k; i++) { if (start < 0) { results[i] = A[end++]; } else if (end > A.length - 1) { results[i] = A[start--]; } else { if (Math.abs(A[start] - target) <= Math.abs(A[end] - target)) { results[i] = A[start--]; } else { results[i] = A[end++]; } } } return results; } private int findIndex(int[] A, int target) { int start = 0; int end = A.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] <= target) { start = mid; } else { end = mid; } } if (A[start] >= target) { return start; } if (A[end] >= end) { return end; } return A.length; } }
一刷:1.findIndex的检查没写清楚
x的平方根
思路:从1到x开始二分法就可以了
注意:返回值的平方是小于x的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Solution { public int sqrt(int x) { long start = 1; long end = x; while (start + 1 < end) { long mid = start + (end - start) / 2; if (mid * mid == x) { return (int)mid; } if (mid * mid < x) { start = mid; } if (mid * mid > x) { end = mid; } } if (end * end <= x) { return (int)end; } else { return (int) start; } } }
一刷:start,end,mid未定义成long型
一. 字符串,子集,排列
1.strStr
思路:常规一一对比
注意:边界值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Solution { public int strStr (String source, String target) { if (source == null || target == null) { return -1; } for (int i = 0; i <= source.length() - target.length(); i++) { int j = 0; for (j = 0; j <target.length(); j++ ) { if (source.charAt(i + j) != target.charAt(j)) { break; } } if (j == target.length()) { return i; } } return -1; } }
一刷:1.边界条件忘记+1
2.在第二个循环结束检查j
2.子集
思路:递归,startIndex,回溯
注意:对数组先进行排序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Solution { public List<List<Integer>> subsets(int[] nums) { List<List<Integer>> results = new ArrayList<>(); if (nums == null) { return results; } if (nums.length == 0) { results.add(new ArrayList<Integer>()); return results; } Arrays.sort(nums); helper(new ArrayList<Integer>(), nums, 0, results); return results; } private void helper(ArrayList<Integer> subset, int[] nums, int startIndex, List<List<Integer>> results) { results.add(new ArrayList<Integer>(subset)); for (int i = startIndex; i < nums.length; i++) { subset.add(nums[i]); helper(subset, nums, i + 1, results); subset.remove(subset.size() - 1); } } }
一刷:异常写错,nums = [] 时应返回[ [] ],而不是[]
3.重复子集
思路:递归,startIndex,回溯,去重
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
class Solution { public List<List<Integer>> subsetsWithDup(int[] nums) { List<List<Integer>> results = new ArrayList<>(); if (nums == null) { return results; } if (nums.length == 0) { results.add(new ArrayList<>()); return results; } Arrays.sort(nums); List<Integer> list = new ArrayList<>(); helper(nums, 0, list, results); return results; } private void helper(int[] nums, int startIndex, List<Integer> list, List<List<Integer>> results) { results.add(new ArrayList(list)); for (int i = startIndex; i < nums.length; i++) { if (i != 0 && nums[i] == nums[i - 1] && i > startIndex) { continue; } list.add(nums[i]); helper(nums, i + 1, list, results); list.remove(list.size() - 1); } } }
一刷:nums为空集时加入results写错;
i 的初值不是0,是startIndex
4.排列
思路:同子集类似,只是把i = startIndex换成0
注意:先对数组排序
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public class Solution { public List<List<Integer>> permute(int[] nums) { List<List<Integer>> results = new ArrayList<>(); if (nums == null) { return results; } if (nums.length == 0) { results.add(new ArrayList()); return results; } Arrays.sort(nums); List<Integer> list = new ArrayList<>(); helper(nums, list, results); return results; } private void helper(int[] nums, List<Integer> list, List<List<Integer>> results) { if (list.size() == nums.length) { results.add(new ArrayList(list)); } for (int i = 0; i < nums.length; i++) { if (list.contains(nums[i])) { continue; } list.add(nums[i]); helper(nums, list, results); list.remove(list.size() - 1); } } }
一刷:list是集合,长度是list.size()
5.带有重复元素的排列
总结:
子集即组合问题设定形参startIndex来记录下标,保证往subset里添加元素时是一股脑的往后走,当前元素加入子集后下次递归直接从当前元素的下一个开始,保证了subset不重复出现[1, 2, 3]和[1, 3, 2]这种重复的解。排列和子集的最大区别是,对排列[1, 2, 3] 和 [1, 3, 2]是两个答案,对子集而言没区别,以[1, 3, 2]为例,子集问题处理完[1, 2, 3]后只会出现下一个[1, 3]的解,不会也不需要出现[1, 3, 2],而排列想要得到两个解就不能定义startIndex,定义i = 0使nums在进入下层递归后仍旧从头遍历nums