语言:java
做题顺序:先数据结构,后算法;先easy,后medium
尽量都是选择高效点的解法,思路和复杂度有的忘记模板了
目录
- 搜索
- 动态规划
- 二分查找
- 贪心思想
- 双指针
- 树
- 104. 二叉树的最大深度
- 110. 平衡二叉树
- 543. 二叉树的直径
- 226. 翻转二叉树
- 617. 合并二叉树
- 112. 路径总和
- 437. 路径总和 III
- 572. 另一棵树的子树
- 101. 对称二叉树
- 111. 二叉树的最小深度
- 404. 左叶子之和
- 687. 最长同值路径
- 337. 打家劫舍 III
- 671. 二叉树中第二小的节点
- 637. 二叉树的层平均值
- 513. 找树左下角的值
- 144. 二叉树的前序遍历
- 145. 二叉树的后序遍历
- 94. 二叉树的中序遍历
- 669. 修剪二叉搜索树
- 230. 二叉搜索树中第K小的元素
- 538. 把二叉搜索树转换为累加树
- 235. 二叉搜索树的最近公共祖先
- 236. 二叉树的最近公共祖先
- 108. 将有序数组转换为二叉搜索树
- 653. 两数之和 IV - 输入二叉搜索树
- 链表
- 数组与矩阵
- 栈和队列
- 字符串
- 位运算
- 哈希表
搜索
1091. 二进制矩阵中的最短路径
思路:
时间复杂度:O(N²)
空间复杂度:O(N²)
class Solution {
public int shortestPathBinaryMatrix(int[][] grid) {
if (grid[0][0] == 1) return -1;
int n = grid.length;
int[][] dist = new int[n][n]; // 起点到该点的最短距离
for (int[] arr : dist) Arrays.fill(arr, Integer.MAX_VALUE);
dist[0][0] = 1;
Queue<int[]> queue = new ArrayDeque<int[]>();
queue.offer(new int[]{0, 0});
while (!queue.isEmpty()) {
int[] arr = queue.poll();
int x = arr[0], y = arr[1];
if (x == n - 1 && y == n - 1) return dist[x][y];
for (int dx = -1; dx < 2; dx++) {
for (int dy = -1; dy < 2; dy++) {
if (x + dx < 0 || x + dx >= n || y + dy < 0 || y + dy >= n) continue;
else if (grid[x + dx][y + dy] == 1 || dist[x + dx][y + dy] <= dist[x][y] + 1) continue;
else {
dist[x + dx][y + dy] = dist[x][y] + 1;
queue.offer(new int[]{x + dx, y + dy});
}
}
}
}
return -1;
}
}
695. 岛屿的最大面积
思路:
时间复杂度:O(m*n)
空间复杂度:O(m*n)
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for (int i = 0; i != grid.length; i++) {
for (int j = 0; j != grid[0].length; j++) {
ans = Math.max(ans, IslandDfs(grid, i, j));
}
}
return ans;
}
private int IslandDfs(int[][] grid, int x, int y) {
if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length || grid[x][y] == 0) return 0;
grid[x][y] = 0;
return 1
+ IslandDfs(grid, x - 1, y)
+ IslandDfs(grid, x, y + 1)
+ IslandDfs(grid, x, y - 1)
+ IslandDfs(grid, x + 1, y);
}
}
200. 岛屿数量
思路:
时间复杂度:O(m*n)
空间复杂度:O(m*n)
class Solution {
public int numIslands(char[][] grid) {
int ans = 0;
for (int i = 0; i != grid.length; i++) {
for (int j = 0; j != grid[0].length; j++) {
if (grid[i][j] == '1') ans += 1;
numIslandsDfs(grid, i, j);
}
}
return ans;
}
private void numIslandsDfs(char[][] grid, int x, int y) {
if (x < 0 || y < 0 || x >= grid.length || y >= grid[0].length || grid[x][y] == '0') return;
grid[x][y] = '0';
numIslandsDfs(grid, x - 1, y);
numIslandsDfs(grid, x, y + 1);
numIslandsDfs(grid, x, y - 1);
numIslandsDfs(grid, x + 1, y);
}
}
130. 被围绕的区域
思路:特殊标记边界上的’O’为’#’
时间复杂度:O(m*n)
空间复杂度:O(m*n)
class Solution {
int[] dx = new int[]{-1, 0, 0, 1};
int[] dy = new int[]{0, 1, -1, 0};
public void solve(char[][] board) {
int[] x = new int[]{0, board.length - 1};
int[] y = new int[]{0, board[0].length - 1};
for (int i = 0; i < board.length; i++)
for (int j : y)
turnDFS(board, i, j);
for (int i : x)
for (int j = 0; j < board[0].length; j++)
turnDFS(board, i, j);
for (int i = 0; i < board.length; i++)
for (int j = 0; j < board[0].length; j++) {
if (board[i][j] == '#') board[i][j] = 'O';
else if (board[i][j] == 'O') board[i][j] = 'X';
}
}
private void turnDFS(char[][] board, int x, int y) {
if (x < 0 || y < 0 || x >= board.length || y >= board[0].length || board[x][y] == '#' || board[x][y] == 'X')
return;
board[x][y] = '#';
for (int k = 0; k < 4; k++) {
turnDFS(board, x + dx[k], y + dy[k]);
}
}
}
417. 太平洋大西洋水流问题
思路:水往高处流,取交集得出结果
时间复杂度:O(mn)
空间复杂度:O(mn)
class Solution {
int[][] heights;
int m, n;
int[] dx = new int[] { -1, 0, 0, 1 };
int[] dy = new int[] { 0, 1, -1, 0 };
public List<List<Integer>> pacificAtlantic(int[][] heights) {
this.heights = heights;
this.m = heights.length;
this.n = heights[0].length;
boolean[][] pacific = new boolean[m][n];
boolean[][] atlantic = new boolean[m][n];
for (int x = 0; x < m; x++) waterDFS(pacific, x, 0);
for (int y = 1; y < n; y++) waterDFS(pacific, 0, y);
for (int x = 0; x < m; x++) waterDFS(atlantic, x, n - 1);
for (int y = 0; y < n-1; y++) waterDFS(atlantic, m - 1, y);
List<List<Integer>> result = new ArrayList<List<Integer>>();
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (pacific[i][j] && atlantic[i][j]) {
List<Integer> t = new ArrayList<Integer>();
t.add(i);
t.add(j);
result.add(t);
}
return result;
}
private void waterDFS(boolean[][] ocean, int x, int y) {
if (ocean[x][y]) return;
ocean[x][y] = true;
int fx, fy;
for (int k = 0; k < 4; k++) {
fx = x + dx[k];
fy = y + dy[k];
if (fx >= 0 && fx < m && fy >= 0 && fy < n && heights[x][y] <= heights[fx][fy])
waterDFS(ocean, fx, fy);
}
}
}
17. 电话号码的字母组合
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public List<String> letterCombinations(String digits) {
List<String> conbanations = new ArrayList<String>();
if (digits.length() == 0) return conbanations;
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(conbanations, phoneMap, digits, 0, new StringBuffer());
return conbanations;
}
private void backtrack(List<String> conbanations, Map<Character, String> phoneMap, String digits, int index, StringBuffer conbanation) {
if (index == digits.length()) {
conbanations.add(conbanation.toString());
return;
}
Character digit = digits.charAt(index);
String letters = phoneMap.get(digit);
int letterCount = letters.length();
for (int i = 0; i < letterCount; i++) {
conbanation.append(letters.charAt(i));
backtrack(conbanations, phoneMap, digits, index + 1, conbanation);
conbanation.deleteCharAt(index);
}
}
}
93. 复原 IP 地址
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
List<String> Ips = new ArrayList<String>();
List<String> store = new ArrayList<String>();
public List<String> restoreIpAddresses(String s) {
if (s.length() < 4 || s.length() > 12) return Ips;
IpBacktrack(s, 0, 4);
return Ips;
}
public void IpBacktrack(String s, int index, int num) {
if (index == s.length() && num == 0) { //没有剩余字符且刚好4段
System.out.println(String.join(",", store));
Ips.add(String.join(".", store));
return;
}
if (s.length() < num + index || s.length() > 3 * num + index) return; //每个ip段至多3位至少1位
for (int i = 1; i <= 3; i++) {
if (index + i <= s.length()) {
String sub = s.substring(index, index + i);
if (sub.charAt(0) == '0' && sub.length() > 1 || Integer.parseInt(sub) > 255) return;
store.add(sub);
IpBacktrack(s, index + i, num - 1);
store.remove(4 - num);
}
}
}
}
79. 单词搜索
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
int[] dx = new int[]{-1, 0, 0, 1};
int[] dy = new int[]{0, 1, -1, 0};
int m, n;
boolean[][] used;
public boolean exist(char[][] board, String word) {
this.m = board.length;
this.n = board[0].length;
if (word.length() > m * n) return false;
this.used = new boolean[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++)
if (board[i][j] == word.charAt(0) &&
existDFS(board, word, 0, i, j)) return true;
}
return false;
}
private boolean existDFS(char[][] board, String word, int index, int x, int y) {
if (index == word.length()) return true;
if (x < 0 || y < 0 || x == m || y == n || board[x][y] != word.charAt(index) || used[x][y] == true) return false;
used[x][y] = true;
for (int k = 0; k < 4; k++)
if (existDFS(board, word, index + 1, x + dx[k], y + dy[k]))
return true;
used[x][y] = false;
return false;
}
}
257. 二叉树的所有路径
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
List<String> store;
List<String> treePaths;
public List<String> binaryTreePaths(TreeNode root) {
treePaths = new ArrayList<String>();
store = new ArrayList<String>();
treePathDFS(root, 0);
return treePaths;
}
private void treePathDFS(TreeNode root, int level) {
store.add(String.valueOf(root.val));
if (root.left == null && root.right == null) treePaths.add(String.join("->", store));
if (root.left != null) treePathDFS(root.left, level + 1);
if (root.right != null) treePathDFS(root.right, level + 1);
store.remove(level);
}
}
46. 全排列
思路:注意result.add这行代码,需要将turn作为参数new一个对象
时间复杂度:O()
空间复杂度:O()
class Solution {
List<List<Integer>> result;
List<Integer> turn;
boolean[] selected;
public List<List<Integer>> permute(int[] nums) {
int len = nums.length;
selected = new boolean[len];
result = new ArrayList<List<Integer>>();
turn = new ArrayList<Integer>();
for (int i = 0; i < nums.length; i++)
permuterDFS(nums, i, 0);
return result;
}
private void permuterDFS(int[] nums, int index, int level) {
turn.add(nums[index]);
selected[index] = true;
if (level == nums.length - 1) result.add(new ArrayList<Integer>(turn));
for (int i = 0; i < nums.length; i++)
if (!selected[i]) permuterDFS(nums, i, level + 1);
turn.remove(level);
selected[index] = false;
}
}
77. 组合
思路:组合不考虑顺序,i <= n - (k - level) + 1的作用是剪枝
时间复杂度:O()
空间复杂度:O()
class Solution {
List<List<Integer>> result;
List<Integer> turn;
public List<List<Integer>> combine(int n, int k) {
result = new ArrayList<List<Integer>>();
turn = new ArrayList<Integer>();
combineDFS(n, k, 0, 0);
return result;
}
private void combineDFS(int n, int k, int num, int level) {
if (level == k) {
result.add(new ArrayList<Integer>(turn));
return;
}
for (int i = num + 1; i <= n - (k - level) + 1; i++) {
turn.add(i);
combineDFS(n, k, i, level + 1);
turn.remove(level);
}
}
}
思路:
时间复杂度:O()
空间复杂度:O()
思路:
时间复杂度:O()
空间复杂度:O()
思路:
时间复杂度:O()
空间复杂度:O()
动态规划
70. 爬楼梯
思路:动态规划,f(x)=f(x−1)+f(x−2)
时间复杂度:O(N)
空间复杂度:O(1)
class Solution {
public int climbStairs(int n) {
int one = 0, two = 0, result = 1;
for (int i = 1; i <= n; i++) {
one = two;
two = result;
result = one + two;
}
return result;
}
}
198. 打家劫舍
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public int rob(int[] nums) {
int prev = 0, curr = 0;
for (int i : nums) {
int turn = Math.max(prev + i, curr);
prev = curr;
curr = turn;
}
return curr;
}
}
213. 打家劫舍 II
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public int rob(int[] nums) {
int len = nums.length;
return Math.max(nums[0] + robDirect(nums, 2, len - 1), robDirect(nums, 1, len));
}
private int robDirect(int[] nums, int start, int end) {
int prev = 0, curr = 0;
for (int i = start; i < end; i++) {
int turn = Math.max(prev + nums[i], curr);
prev = curr;
curr = turn;
}
return curr;
}
}
64. 最小路径和
思路:从左到右,从上往下
时间复杂度:O(M*N)
空间复杂度:O(1)
class Solution {
public int minPathSum(int[][] grid) {
int row = grid.length, column = grid[0].length;
for (int i = 0; i < row; i++) {
for (int j = 0; j < column; j++) {
if (0 == i && 0 == j) continue;
else if (0 == i) grid[i][j] = grid[i][j - 1] + grid[i][j];
else if (0 == j) grid[i][j] = grid[i - 1][j] + grid[i][j];
else grid[i][j] = Math.min(grid[i][j - 1], grid[i - 1][j]) + grid[i][j];
}
}
return grid[row - 1][column - 1];
}
}
62. 不同路径
思路:不需要M*N的空间,交换m和n不影响结果,空间只需要min(m,n),答案没有体现这一点
时间复杂度:O(M*N)
空间复杂度:O(min(M,N))
解法一:
class Solution {
public int uniquePaths(int m, int n) {
int[] grid = new int[n];
Arrays.fill(grid, 1);
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
grid[j] += grid[j - 1];
}
}
return grid[n - 1];
}
}
思路:组合计算,从总移动次数中取出t-1次向下【t=min(m,n)】
时间复杂度:O(min(m,n))
空间复杂度:O(1)
解法二:
class Solution {
public int uniquePaths(int m, int n) {
int t = Math.min(m, n); //向下移动次数,用min减少计算次数
int k = Math.max(m, n);
long result = 1; // 防止溢出
for (int x = k, y = 1; y < t; x++, y++) result = result * x / y; //经过推导,减少计算次数
return (int) result;
}
}
303. 区域和检索 - 数组不可变
思路:如果多次调用sumRange,存在大量重复计算,采用前缀和,空间换时间
时间复杂度:初始化O(N)、单次sumRangeO(1)
空间复杂度:O(N)
class NumArray {
int[] sums;
public NumArray(int[] nums) {
int len = nums.length;
sums = new int[len + 1];
for (int i = 0; i < len; i++) sums[i + 1] = nums[i] + sums[i];
}
public int sumRange(int left, int right) {
return sums[right + 1] - sums[left];
}
}
413. 等差数列划分
思路:
时间复杂度:O(N)
空间复杂度:O(N)
class Solution {
public int numberOfArithmeticSlices(int[] nums) {
if (nums.length < 3) return 0;
int len = nums.length;
int[] dps = new int[len];
for (int i = 2; i < len; i++)
if (nums[i] - nums[i - 1] == nums[i - 1] - nums[i - 2])
dps[i] = dps[i - 1] + 1;
int result=0;
for(int i:dps) result+=i;
return result;
}
}
343. 整数拆分
思路:当f >= 4,2(f-2)=2f-4>=f,所以不需要>=4的因子
时间复杂度:O(1)
空间复杂度:O(1)
class Solution {
public int integerBreak(int n) {
if (n <= 3) return 1 * (n - 1);
int a = n / 3, b = n % 3;
int result = 1;
for (int i = 1; i < a; i++) result *= 3;
if (b == 1) return 4 * result;
else if (b == 2) return 6 * result;
else return 3 * result;
}
}
279. 完全平方数
思路:参考四平方定理
时间复杂度:O(√N)
空间复杂度:O()
class Solution {
public int numSquares(int n) {
if (isPerfectSquare(n)) return 1;
else if (checkAnswer4(n)) return 4;
for (int i = 1; i * i < n; i++) {
int j = n - i * i;
if (isPerfectSquare(j)) return 2;
}
return 3;
}
private boolean checkAnswer4(int n) {
while (n % 4 == 0) n /= 4;
return n % 8 == 7;
}
private boolean isPerfectSquare(int n) {
int x = (int) Math.sqrt(n);
return x * x == n;
}
}
300. 最长递增子序列
思路:动态规划
时间复杂度:O(N²)
空间复杂度:O(N)
解法一:
class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
int result = 1;
for (int i = 0; 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);
result = Math.max(result, dp[i]);
}
return result;
}
}
思路:贪心+二分查找,要使上升子序列尽可能长,则序列上升得尽可能慢,因此每次在上升子序列最后加上的数尽可能的小。
时间复杂度:O(NlogN)
空间复杂度:O(N)
解法二:
class Solution {
public int lengthOfLIS(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int len = 0;
for (int i = 0; i < nums.length; i++) {
if (dp[len] < nums[i]) dp[++len] = nums[i];
else if (dp[0] > nums[i]) dp[0] = nums[i];
else {
//二分查找,找到dp中第一个大于等于nums[i]的元素,替换这个元素
int left = 0, right = len;
while (left < right) {
int mid = (left + right) >> 1;
if (dp[mid] < nums[i]) left = mid + 1;
else right = mid;
}
dp[left] = nums[i];
}
}
return len + 1;
}
}
646. 最长数对链
思路:
时间复杂度:O()
空间复杂度:O()
思路:
时间复杂度:O()
空间复杂度:O()
二分查找
我在做这类型题目的时候,解题思路是一部分,
另一部分卡在循环判断【left < right 还是 left <= right】
、左右边界修改[left 和 right 如何利用 mid 修改边界,才不会导致死循环或者溢出]
每道题都需要具体的判断,暂时是通过画图解决,
会有特殊处理,例如数组仅包含 0 或者 1 个元素
69. x 的平方根
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public int mySqrt(int x) {
if (x <= 1) return x;
int left = 1, right = x / 2;
while (left < right) {
int mid = left + (right - left + 1) / 2;
if (mid == x / mid) return mid;
else if (mid < x / mid) left = mid;
else right = mid - 1;
}
return left;
}
}
744. 寻找比目标字母大的最小字母
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public char nextGreatestLetter(char[] letters, char target) {
int len = letters.length;
if (letters[len - 1] <= target || letters[0] > target) return letters[0];
int left = 0, right = len - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (letters[mid] <= target) left = mid + 1;
else right = mid;
}
return letters[right] > target ? letters[right] : letters[0];
}
}
540. 有序数组中的单一元素
思路:
时间复杂度:O()
空间复杂度:O()
解法一:
class Solution {
public int singleNonDuplicate(int[] nums) {
int ans = 0;
for (int i : nums) ans ^= i;
return ans;
}
}
解法二:
class Solution {
public int singleNonDuplicate(int[] nums) {
int low = 0, high = nums.length - 1;
while (low < high) {
int mid = low + (high - low) / 2;
if (nums[mid] == nums[mid ^ 1]) low = mid + 1;
else high = mid;
}
return nums[low];
}
}
278. 第一个错误的版本
思路:
时间复杂度:O()
空间复杂度:O()
/* The isBadVersion API is defined in the parent class VersionControl.
boolean isBadVersion(int version); */
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1, right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (isBadVersion(mid)) right = mid;
else left = mid + 1;
}
return left;
}
}
153. 寻找旋转排序数组中的最小值
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public int findMin(int[] nums) {
int left = 0, right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < nums[right]) right = mid;
else left = mid + 1;
}
return nums[left];
}
}
34. 在排序数组中查找元素的第一个和最后一个位置
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public int[] searchRange(int[] nums, int target) {
if (nums.length == 0) return new int[]{-1, -1};
int left = findleft(nums, target);
int right = findright(nums, target);
return new int[]{left, right};
}
// 找第一个等于target的index
private int findleft(int[] nums, int target) {
int left = 0, right = nums.length - 1, result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
result = mid;
right = mid - 1;
} else if (nums[mid] > target) right = mid - 1;
else left = mid + 1;
}
return result;
}
// 找最后一个等于target的index
private int findright(int[] nums, int target) {
int left = 0, right = nums.length - 1, result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
result = mid;
left = mid + 1;
} else if (nums[mid] > target) right = mid - 1;
else left = mid + 1;
}
return result;
}
}
贪心思想
思路:
时间复杂度:O()
空间复杂度:O()
双指针
167. 两数之和 II - 输入有序数组
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public int[] twoSum(int[] numbers, int target) {
int l = 0, r = numbers.length - 1;
while (l < r) {
if (numbers[l] + numbers[r] < target) l++;
else if (numbers[l] + numbers[r] > target) r--;
else break;
}
return new int[]{l + 1, r + 1};
}
}
633. 平方数之和
思路:不可以二分: mid = l + r / 2,z = x * x + y * y是抛物面
时间复杂度:O()
空间复杂度:O()
class Solution {
public boolean judgeSquareSum(int c) {
long r = (int) Math.sqrt(c), l = 0;
long Square;
while (l <= r) {
Square = l * l + r * r;
if (Square == c) return true;
else if (Square < c) l++;
else if (Square > c) r--;
}
return false;
}
}
345. 反转字符串中的元音字母
思路:
时间复杂度:O(N)
空间复杂度:O(N)
class Solution {
private final static HashSet<Character> vowels = new HashSet<>(
Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
public String reverseVowels(String s) {
char[] cs = s.toCharArray();
int l = 0, r = cs.length - 1, n = cs.length;
while (l < r) {
while (l < n && !vowels.contains(cs[l]))
l++;
while (r > 0 && !vowels.contains(cs[r]))
r--;
if (l < r) {
char turn = cs[l];
cs[l] = cs[r];
cs[r] = turn;
l++;
r--;
}
}
return new String(cs);
}
}
680. 验证回文串 II
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
boolean deleted = false;
public boolean validPalindrome(String s) {
char[] arr = s.toCharArray();
int l = 0, r = arr.length - 1;
while (l < r) {
if (arr[l] == arr[r]) {
l++;
r--;
} else return isPalindrome(s.substring(l, r)) || isPalindrome(s.substring(l + 1, r + 1));
}
return true;
}
private boolean isPalindrome(String s) {
char[] arr = s.toCharArray();
int l = 0, r = arr.length - 1;
while (l < r) {
if (arr[l] == arr[r]) {
l++;
r--;
} else return false;
}
return true;
}
}
88. 合并两个有序数组
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
if (0 == n) return;
if (0 == m) {
for (int i = 0; i < n; i++) nums1[i] = nums2[i];
return;
}
int tail = m + n - 1;
m--;
n--;
while (n >= 0) {
if (m >= 0 && nums1[m] >= nums2[n]) nums1[tail--] = nums1[m--];
else nums1[tail--] = nums2[n--];
}
}
}
141. 环形链表
思路:
时间复杂度:O()
空间复杂度:O()
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null)
return false;
ListNode slow = head, fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null)
return false;
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
524. 通过删除字母匹配到字典里最长单词
思路:先排序,后证明子字符串
时间复杂度:O()
空间复杂度:O()
class Solution {
public String findLongestWord(String s, List<String> dictionary) {
Collections.sort(dictionary, (a, b) -> {
if (a.length() != b.length()) return b.length() - a.length(); // 降序
else return a.compareTo(b); //升序
});
for (String elem : dictionary) {
int i = 0, j = 0, len1 = s.length(), len2 = elem.length();
while (i < len1 && j < len2) {
if (s.charAt(i) == elem.charAt(j)) j++;
i++;
}
if (j == len2) return elem;
}
return "";
}
}
思路:
时间复杂度:O()
空间复杂度:O()
树
104. 二叉树的最大深度
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int maxDepth(TreeNode root) {
if (null == root) return 0;
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
110. 平衡二叉树
思路:
时间复杂度:O(N)
空间复杂度:O(N)
class Solution {
public boolean isBalanced(TreeNode root) {
return recur(root) >= 0;
}
private int recur(TreeNode root) {
if (null == root) return 0;
int left = recur(root.left);
if (left == -1) return -1;
int right = recur(root.right);
if (right == -1) return -1;
return Math.abs(left - right) < 2 ? Math.max(left, right) + 1 : -1;
}
}
543. 二叉树的直径
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
depth(root);
return max;
}
private int depth(TreeNode root) {
if (null == root) return 0;
int left = depth(root.left);
int right = depth(root.right);
max = Math.max(max, left + right);
return Math.max(left, right) + 1;
}
}
226. 翻转二叉树
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public TreeNode invertTree(TreeNode root) {
if (null == root) return root;
TreeNode turn = root.left;
root.left = root.right;
root.right = turn;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
617. 合并二叉树
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (null == root1) return root2;
if (null == root2) return root1;
root1.val += root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
}
}
112. 路径总和
思路:
时间复杂度:O(N)
空间复杂度:O(height)
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if (null == root) return false;
if (null != root.left || null != root.right)
return hasPathSum(root.left, targetSum - root.val)
|| hasPathSum(root.right, targetSum - root.val);
return targetSum == root.val;
}
}
437. 路径总和 III
思路:
时间复杂度:O(1)
空间复杂度:O(1)
解法一:
class Solution {
int ans = 0;
public int pathSum(TreeNode root, int targetSum) {
allPath(root, targetSum);
return ans;
}
private void allPath(TreeNode root, int targetSum) {
if (null == root) return;
findPath(root, targetSum);
allPath(root.left, targetSum);
allPath(root.right, targetSum);
}
private void findPath(TreeNode root, long targetSum) {
if (null == root) return;
if (root.val == targetSum) ans++;
findPath(root.left, targetSum - root.val);
findPath(root.right, targetSum - root.val);
}
}
解法二:
class Solution {
int t, ans;
Map<Long, Integer> map = new HashMap<>();
public int pathSum(TreeNode root, int targetSum) {
if (null == root) return 0;
t = targetSum;
map.put(0L, 1);
dfs(root, root.val);
return ans;
}
void dfs(TreeNode root, long val) {
if (map.containsKey(val - t)) ans += map.get(val - t);
map.put(val, map.getOrDefault(val, 0) + 1);
if (null != root.left) dfs(root.left, val + root.left.val);
if (null != root.right) dfs(root.right, val + root.right.val);
map.put(val, map.getOrDefault(val, 0) - 1);
}
}
572. 另一棵树的子树
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (null == root && null == subRoot) return true;
if (null == root || null == subRoot) return false;
return isSametree(root, subRoot) || isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
}
private boolean isSametree(TreeNode t1, TreeNode t2) {
if (null == t1 && null == t2) return true;
if (null == t1 || null == t2) return false;
return t1.val == t2.val && isSametree(t1.left, t2.left) && isSametree(t1.right, t2.right);
}
}
101. 对称二叉树
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public boolean isSymmetric(TreeNode root) {
return check(root, root);
}
private boolean check(TreeNode p, TreeNode q) {
if (null == p && null == q) return true;
if (null == p || null == q) return false;
return p.val == q.val && check(p.left, q.right) && check(p.right, q.left);
}
}
111. 二叉树的最小深度
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int minDepth(TreeNode root) {
if (null == root) return 0;
int m1 = minDepth(root.left);
int m2 = minDepth(root.right);
return null == root.right || null == root.left ? m1 + m2 + 1 : Math.min(m1, m2) + 1;
}
}
404. 左叶子之和
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if (null == root) return 0;
if (null != root.left && null == root.left.left && null == root.left.right)
return root.left.val + sumOfLeftLeaves(root.right);
return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
}
}
687. 最长同值路径
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
int ans;
public int longestUnivaluePath(TreeNode root) {
ans = 0;
maxSingleLine(root);
return ans;
}
private int maxSingleLine(TreeNode root) {
if (null == root) return 0;
int left = maxSingleLine(root.left), left1 = 0;
int right = maxSingleLine(root.right), right1 = 0;
if (null != root.left && root.val == root.left.val) left1 = left + 1;
if (null != root.right && root.val == root.right.val) right1 = right + 1;
ans = Math.max(ans, left1 + right1);
return Math.max(left1, right1);
}
}
337. 打家劫舍 III
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int rob(TreeNode root) {
int[] result = robDfs(root);
return Math.max(result[0], result[1]);
}
private int[] robDfs(TreeNode root) {
if (null == root) return new int[2];
int[] l = robDfs(root.left);
int[] r = robDfs(root.right);
int selected = root.val + l[1] + r[1];
int nonselected = Math.max(l[0], l[1]) + Math.max(r[0], r[1]);
return new int[]{selected, nonselected};
}
}
671. 二叉树中第二小的节点
思路:
时间复杂度:O(N)
空间复杂度:O(N)
class Solution {
int ans, t;
public int findSecondMinimumValue(TreeNode root) {
ans = -1;
t = root.val;
secondDfs(root);
return ans;
}
private void secondDfs(TreeNode root) {
if (null == root) return;
if (-1 != ans && root.val >= ans) return;
if (root.val > t) {
ans = root.val;
return;
}
secondDfs(root.left);
secondDfs(root.right);
}
}
637. 二叉树的层平均值
思路:
时间复杂度:O(N)
空间复杂度:O(1)
class Solution {
public List<Double> averageOfLevels(TreeNode root) {
List<Double> averages = new ArrayList<Double>();
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
double sum = 0;
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode t = queue.poll();
sum += t.val;
if (null != t.left) queue.offer(t.left);
if (null != t.right) queue.offer(t.right);
}
averages.add(sum / size);
}
return averages;
}
}
513. 找树左下角的值
思路:
时间复杂度:O(N)
空间复杂度:O(1)
class Solution {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<TreeNode>();
int ans = 0;
queue.offer(root);
while (!queue.isEmpty()) {
int sz = queue.size();
ans = queue.peek().val;
while (sz-- > 0) {
TreeNode t = queue.poll();
if (null != t.left) queue.offer(t.left);
if (null != t.right) queue.offer(t.right);
}
}
return ans;
}
}
144. 二叉树的前序遍历
思路:前中后序只是顺序不同,分别有递归、迭代、Morris 遍历三种解法
时间复杂度:O(N)
空间复杂度:O(N)
递归解法
class Solution {
List<Integer> res = new ArrayList<Integer>();
public List<Integer> preorderTraversal(TreeNode root) {
preorder(root);
return res;
}
private void preorder(TreeNode root) {
if (null == root) return;
res.add(root.val);
preorder(root.left);
preorder(root.right);
}
}
145. 二叉树的后序遍历
思路:迭代即使用显式的栈来存储遍历过程中的节点信息
时间复杂度:O(N)
空间复杂度:O(N)
递归解法
class Solution {
List<Integer> res = new ArrayList<Integer>();
public List<Integer> postorderTraversal(TreeNode root) {
postorder(root);
return res;
}
private void postorder(TreeNode root) {
if (null == root) return;
postorder(root.left);
postorder(root.right);
res.add(root.val);
}
}
94. 二叉树的中序遍历
思路:
时间复杂度:O(N)
空间复杂度:O(1)
递归解法
class Solution {
List<Integer> res = new ArrayList<Integer>();
public List<Integer> inorderTraversal(TreeNode root) {
inorder(root);
return res;
}
private void inorder(TreeNode root) {
if (null == root) return;
inorder(root.left);
res.add(root.val);
inorder(root.right);
}
}
669. 修剪二叉搜索树
思路:
时间复杂度:O(N)
空间复杂度:O(N)
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if (null == root) return root;
if (root.val < low) return trimBST(root.right, low, high);
if (root.val > high) return trimBST(root.left, low, high);
root.left = trimBST(root.left, low, high);
root.right = trimBST(root.right, low, high);
return root;
}
}
230. 二叉搜索树中第K小的元素
思路:
时间复杂度:O(N)
空间复杂度:O(1)
class Solution {
public int kthSmallest(TreeNode root, int k) {
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
while (null != root || !stack.isEmpty()) {
while (null != root) {
stack.push(root);
root = root.left;
}
root = stack.pop();
k--;
if (0 == k) break;
root = root.right;
}
return root.val;
}
}
538. 把二叉搜索树转换为累加树
思路:
时间复杂度:O(N)
空间复杂度:O(N)
解法一 DFS
class Solution {
int sum = 0;
public TreeNode convertBST(TreeNode root) {
if (null != root) {
convertBST(root.right);
sum += root.val;
root.val = sum;
convertBST(root.left);
}
return root;
}
}
解法二 BFS
class Solution {
public TreeNode convertBST(TreeNode root) {
Deque<TreeNode> stack = new ArrayDeque<TreeNode>();
TreeNode t = root;
int n = 0;
while (null != t || !stack.isEmpty()) {
while (null != t) {
stack.push(t);
t = t.right;
}
t = stack.pop();
t.val += n;
n = t.val;
t = t.left;
}
return root;
}
}
235. 二叉搜索树的最近公共祖先
思路:
时间复杂度:O(N)
空间复杂度:O(1)
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
TreeNode t = root;
while (true) {
if (p.val < t.val && q.val < t.val)
t = t.left;
else if (p.val > t.val && q.val > t.val)
t = t.right;
else
break;
}
return t;
}
}
236. 二叉树的最近公共祖先
思路:
时间复杂度:O(N)
空间复杂度:O(1)
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (null == root || p == root || q == root) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (null != left && null != right) return root;
return null != left ? left : right;
}
}
108. 将有序数组转换为二叉搜索树
思路:
时间复杂度:O(N)
空间复杂度:O(H)
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
private TreeNode helper(int[] nums, int left, int right) {
if (left > right) return null;
int mid = (left + right) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, left, mid - 1);
root.right = helper(nums, mid + 1, right);
return root;
}
}
653. 两数之和 IV - 输入二叉搜索树
思路:
时间复杂度:O(N)
空间复杂度:O(1)
class Solution {
Set<Integer> set = new HashSet<Integer>();
public boolean findTarget(TreeNode root, int k) {
if (null == root) return false;
if (set.contains(k - root.val)) return true;
set.add(root.val);
return findTarget(root.left, k) || findTarget(root.right, k);
}
}
思路:
时间复杂度:O(N)
空间复杂度:O(1)
链表
160. 相交链表
思路:
时间复杂度:O()
空间复杂度:O()
解法一
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
解法二
用哈希表存储第一条单链表每个节点,第二次遍历另一条单链表每个节点是否存在于哈希表中
206. 反转链表
思路:
时间复杂度:O(N)
空间复杂度:O(1)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode curr = head, prev = null;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}
21. 合并两个有序链表
思路:
时间复杂度:O()
空间复杂度:O()
解法一:迭代
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode(-1);
ListNode prev = head;
while (null != l1 && null != l2) {
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
prev = prev.next;
}
prev.next = l1 == null ? l2 : l1;
return head.next;
}
}
解法二:递归
83. 删除排序链表中的重复元素
思路:代码可以精简一下,不需要prev
时间复杂度:O(N)
空间复杂度:O(1)
解法一:
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (null == head) return head;
ListNode curr = head;
ListNode prev = head.next;
while (null != curr.next) {
if (curr.val == prev.val) {
curr.next = prev.next;
prev = curr.next;
} else {
curr = curr.next;
prev = prev.next;
}
}
return head;
}
}
解法二:
19. 删除链表的倒数第 N 个结点
思路:
时间复杂度:O()
空间复杂度:O()
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
int len = getLength(head);
ListNode curr = dummy;
for (int i = 0; i < len - n; i++) curr = curr.next;
curr.next = curr.next.next;
return dummy.next;
}
public int getLength(ListNode head) {
int num = 0;
while (null != head) {
num++;
head = head.next;
}
return num;
}
}
24. 两两交换链表中的节点
思路:
时间复杂度:O(N)
空间复杂度:O(N)
class Solution {
public ListNode swapPairs(ListNode head) {
if (null == head || null == head.next) return head;
ListNode newhead = head.next;
head.next = swapPairs(newhead.next);
newhead.next = head;
return newhead;
}
}
445. 两数相加 II
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Deque<Integer> stack1 = new ArrayDeque<Integer>();
Deque<Integer> stack2 = new ArrayDeque<Integer>();
while (null != l1) {
stack1.push(l1.val);
l1 = l1.next;
}
while (null != l2) {
stack2.push(l2.val);
l2 = l2.next;
}
int carry = 0;
ListNode ans = null;
while (!stack1.isEmpty() || !stack2.isEmpty() || 0 != carry) {
int a = stack1.isEmpty() ? 0 : stack1.pop();
int b = stack2.isEmpty() ? 0 : stack2.pop();
int cur = a + b + carry;
carry = cur / 10;
cur = cur % 10;
ListNode node = new ListNode(cur);
node.next = ans;
ans = node;
}
return ans;
}
}
234. 回文链表
思路:
时间复杂度:O()
空间复杂度:O()
class Solution {
public boolean isPalindrome(ListNode head) {
ListNode slow = endOfFirstHalt(head);
ListNode reserve = reverse(slow.next);
ListNode turn = reserve;
slow = head;
while (reserve != null) {
if (reserve.val != slow.val) {
reverse(reserve);
return false;
}
slow=slow.next;
reserve=reserve.next;
}
reverse(reserve);
return true;
}
// 234的反转函数
private ListNode reverse(ListNode head) {
ListNode prev = null, curr = head, next = null;
while (curr != null) {
next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
// 234的找中点函数
private ListNode endOfFirstHalt(ListNode head) {
ListNode slow = head, fast = head;
while (fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
725. 分隔链表
思路:
时间复杂度:O()
空间复杂度:O()
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode[] splitListToParts(ListNode head, int k) {
int len = getLength(head);
int remainder = len % k;
int quotient = len / k;
ListNode[] result = new ListNode[k];
if (quotient > 0) {
for (int i = 0; i < remainder; i++) {
result[i] = head;
for (int j = 0; j < quotient; j++) head = head.next;
ListNode turn = head.next;
head.next = null;
head = turn;
}
for (int i = remainder; i < k; i++) {
result[i] = head;
for (int j = 0; j < quotient - 1; j++) head = head.next;
ListNode turn = head.next;
head.next = null;
head = turn;
}
} else {
for (int i = 0; i < remainder; i++) {
result[i] = head;
ListNode turn = head.next;
head.next = null;
head = turn;
}
}
return result;
}
private int getLength(ListNode head) {
int num = 0;
while (null != head) {
num++;
head = head.next;
}
return num;
}
}
328. 奇偶链表
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public ListNode oddEvenList(ListNode head) {
if (null == head) return head;
ListNode odd = head;
ListNode evenhead = head.next, even = head.next;
while (null != even && null != even.next) {
odd.next = even.next;
odd = odd.next;
even.next = odd.next;
even = even.next;
}
odd.next = evenhead;
return head;
}
}
思路:
时间复杂度:O(n)
空间复杂度:O(1)
数组与矩阵
283. 移动零
思路:双指针
时间复杂度:O(N)
空间复杂度:O(1)
class Solution {
public void moveZeroes(int[] nums) {
if (nums == null) {
return;
}
int j = 0;
for (int i = 0; i < nums.length; i++) {
if (0 != nums[i]) {
nums[j++] = nums[i];
}
}
for (; j < nums.length; j++) {
nums[j] = 0;
}
}
}
566. 重塑矩阵
思路:逐一映射,flatten
时间复杂度:O(mn)
空间复杂度:O(1)
class Solution {
public int[][] matrixReshape(int[][] mat, int r, int c) {
int m = mat.length;
int n = mat[0].length;
if (m * n != r * c) return mat;
int[][] ans = new int[r][c];
int ri = 0, cj = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
ans[ri][cj++] = mat[i][j];
if (cj == c) {
ri++;
cj=0;
}
}
}
return ans;
}
}
485. 最大连续 1 的个数
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int findMaxConsecutiveOnes(int[] nums) {
int n = nums.length;
int maxCount = 0, count = 0;
for (int i = 0; i < n; i++) {
if (1 == nums[i]) {
count++;
} else {
maxCount = Math.max(maxCount, count);
count = 0;
}
}
maxCount = Math.max(maxCount, count);
return maxCount;
}
}
240. 搜索二维矩阵 II
思路:
- 每行最后元素较小,整行舍弃。剩余行二分查找
- 从右上角二分查找,向左变小,向下变大【采用】
时间复杂度:O(m+n)
空间复杂度:O(1)
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = 0, n = matrix[0].length - 1;
while (m < matrix.length && n >= 0) {
if (target < matrix[m][n]) {
n--;
} else if (target > matrix[m][n]) {
m++;
} else {
return true;
}
}
return false;
}
}
378. 有序矩阵中第 K 小的元素
思路:
时间复杂度:O(n)
空间复杂度:O(1)
645. 错误的集合
思路:
时间复杂度:O(n)
空间复杂度:O(1)
287. 寻找重复数
思路:
时间复杂度:O(n)
空间复杂度:O(1)
. 模板
思路:
时间复杂度:O(n)
空间复杂度:O(1)
栈和队列
232. 用栈实现队列
思路:双指针
时间复杂度:O(N)
空间复杂度:O(1)
. 模板
思路:
时间复杂度:O(n)
空间复杂度:O(1)
字符串
242. 有效的字母异位词
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public boolean isAnagram(String s, String t) {
if (s.length() != t.length()) return false;
int[] record = new int[26];
for (char i : s.toCharArray()) {
record[i - 'a'] += 1;
}
for (char j : t.toCharArray()) {
record[j - 'a'] -= 1;
}
for (int k : record) {
if (k != 0) return false;
}
return true;
}
}
409. 最长回文串
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int longestPalindrome(String s) {
int[] record = new int[128];
for (char c : s.toCharArray()) record[c]++;
int ans = 0;
for (int i : record) {
ans += 2 * (i / 2);
if (i % 2 == 1 && ans % 2 == 00) ans++;
}
return ans;
}
}
205. 同构字符串
思路:
时间复杂度:O(n)
空间复杂度:O(1)
解法一:
class Solution {
public boolean isIsomorphic(String s, String t) {
return isIsomorphicHelper(s, t) && isIsomorphicHelper(t, s);
}
private boolean isIsomorphicHelper(String s, String t) {
if (s.length() != t.length()) return false;
HashMap<Character, Character> record = new HashMap<Character, Character>();
int len = s.length();
for (int i = 0; i < len; i++) {
char S = s.charAt(i);
char T = t.charAt(i);
if (record.containsKey(S)) {
if (record.get(S) != T) return false;
} else {
record.put(S, T);
}
}
return true;
}
}
解法二:
class Solution {
public boolean isIsomorphic(String s, String t) {
if (s.length() != t.length()) return false;
int[] S = new int[128];
int[] T = new int[128];
for (int i = 0; i < s.length(); i++) {
char cs = s.charAt(i);
char ct = t.charAt(i);
if (S[cs] == T[ct]) S[cs] = T[ct] = i + 1;
else return false;
}
return true;
}
}
647. 回文子串
思路:
时间复杂度:O(n)
空间复杂度:O(1)
9. 回文数
思路:
时间复杂度:O(n)
空间复杂度:O(n)
解法一:
class Solution {
public boolean isPalindrome(int x) {
if (x < 0) return false;
String record = Integer.toString(x);
StringBuilder reverse = new StringBuilder(record).reverse();
return record.equals(reverse.toString());
}
}
696. 计数二进制子串
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int countBinarySubstrings(String s) {
List<Integer> record = new ArrayList<Integer>();
char[] S = s.toCharArray();
int count = 1;
char t = S[0];
for (int i = 1; i < S.length; i++) {
if (t != S[i]) {
t = S[i];
record.add(count);
count = 1;
} else count++;
}
record.add(count);
int ans = 0;
for (int i = 0; i < record.size() - 1; i++) {
ans += Math.min(record.get(i), record.get(i + 1));
}
return ans;
}
}
位运算
461. 汉明距离
思路:
时间复杂度:O(1)
空间复杂度:O(1)
class Solution {
public int hammingDistance(int x, int y) {
return Integer.bitCount(x ^ y);
}
}
136. 只出现一次的数字
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int singleNumber(int[] nums) {
int j = 0;
for (int i : nums) {
j = j ^ i;
}
return j;
}
}
268. 丢失的数字
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int missingNumber(int[] nums) {
int n = nums.length;
int xor = 0;
for (int i = 0; i <= n; i++) {
xor ^= i;
}
for (int i = 0; i < n; i++) {
xor ^= nums[i];
}
return xor;
}
}
260. 只出现一次的数字 III
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int[] singleNumber(int[] nums) {
int xor = 0;
for (int num : nums) xor ^= num;
int lsb = (xor == Integer.MIN_VALUE ? xor : xor & (-xor));
int num1 = 0, num2 = 0;
for (int num : nums) {
if ((num & lsb) != 0) num1 ^= num;
else num2 ^= num;
}
return new int[]{num1, num2};
}
}
190. 颠倒二进制位
思路:
时间复杂度:O(n)
空间复杂度:O(1)
public class Solution {
// you need treat n as an unsigned value
public int reverseBits(int n) {
int record = 0;
for (int i = 0; i < 32 & n != 0; i++) {
record |= (n & 1) << (31 - i);
n >>>= 1;
}
return record;
}
}
231. 2 的幂
思路:
时间复杂度:O(1)
空间复杂度:O(1)
解法一:
class Solution {
public boolean isPowerOfTwo(int n) {
return n > 0 && 0 == (n & (n - 1));
}
}
解法二:
class Solution {
public boolean isPowerOfTwo(int n) {
return n > 0 && n == (n & (-n));
}
}
342. 4的幂
思路:
时间复杂度:O(n)
空间复杂度:O(1)
解法一:
class Solution {
public boolean isPowerOfFour(int n) {
return n > 0 && n == (n & (-n)) && (n & 0xaaaaaaaa) == 0;
}
}
解法二:
class Solution {
public boolean isPowerOfFour(int n) {
return n > 0 && n == (n & (-n)) && n % 3 == 1;
}
}
693. 交替位二进制数
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public boolean hasAlternatingBits(int n) {
int a = n ^ (n >> 1);
return 0 == (a & (a + 1));
}
}
476. 数字的补数
思路:
时间复杂度:O(n)
空间复杂度:O(1)
解法一:
class Solution {
public int findComplement(int num) {
long turn = 1;
while (turn <= num) {
turn = turn << 1;
}
return (int) (turn - 1) ^ num;
}
}
解法二:
class Solution {
public int findComplement(int num) {
int x = 0;
for (int i = num; i > 0; i -= i & (-i)) x = i;
return (x - 1) & ~num;
}
}
371. 两整数之和
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int getSum(int a, int b) {
while (b != 0) {
int turn = (a & b) << 1;
a = a ^ b;
b = turn;
}
return a;
}
}
318. 最大单词长度乘积
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int maxProduct(String[] words) {
int len = words.length, index = 0;
int[] masks = new int[len];
for (String word : words) {
int t = 0;
for (int i = 0; i < word.length(); i++) {
int u = word.charAt(i) - 'a';
t |= (1 << u);
}
masks[index++] = t;
}
int ans = 0;
for (int i = 0; i < len; i++) {
for (int j = 0; j < i; j++) {
if ((masks[i] & masks[j]) == 0) ans = Math.max(ans, words[i].length() * words[j].length());
}
}
return ans;
}
}
338. 比特位计数
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int[] countBits(int n) {
int[] record = new int[n + 1];
record[0] = 0;
for (int i = 1; i <= n; i++) {
if (0 == i % 2) {
record[i] = record[i / 2];
} else if (1 == i % 2) {
record[i] = record[i - 1] + 1;
}
}
return record;
}
}
哈希表
1. 两数之和
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; i++) {
if (hashtable.containsKey(target - nums[i])) {
return new int[]{i, hashtable.get(target - nums[i])};
} else {
hashtable.put(nums[i], i);
}
}
return new int[0];
}
}
217. 存在重复元素
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<Integer>();
for(int x : nums){
if(!set.add(x)){
return true;
}
}
return false;
}
}
594. 最长和谐子序列
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int findLHS(int[] nums) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
int ans = 0;
for (int i : nums) map.put(i, map.getOrDefault(i, 0) + 1);
for (int i : nums)
if (map.containsKey(i - 1)) ans = Math.max(ans, map.get(i) + map.get(i - 1));
return ans;
}
}
128. 最长连续序列
思路:
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public int longestConsecutive(int[] nums) {
Set<Integer> num_set = new HashSet<Integer>();
for (int num : nums) num_set.add(num);
int longestStreak = 0;
for (int num : num_set) {
if (!num_set.contains(num - 1)) {
int currentnum = num;
int currentStreak = 1;
while (num_set.contains(currentnum + 1)) {
currentnum += 1;
currentStreak += 1;
}
longestStreak = Math.max(longestStreak, currentStreak);
}
}
return longestStreak;
}
}
思路:
时间复杂度:O(n)
空间复杂度:O(1)