283. 移动零
class Solution {
public void moveZeroes(int[] nums) {
int pos = 0;
for (int i = 0; i < nums.length; i++) {
if (nums[i] != 0) {
nums[pos++] = nums[i];
}
}
for (int i = pos; i < nums.length; i++) {
nums[i] = 0;
}
}
}
284. 顶端迭代器
class PeekingIterator implements Iterator {
Queue q;
public PeekingIterator(Iterator iterator) {
// initialize any member here.
q = new LinkedList<>();
for (; iterator.hasNext(); ) {
q.offer(iterator.next());
}
}
// Returns the next element in the iteration without advancing the iterator.
public Integer peek() {
return q.peek();
}
// hasNext() and next() should behave the same as in the Iterator interface.
// Override them if needed.
@Override
public Integer next() {
return q.poll();
}
@Override
public boolean hasNext() {
return !q.isEmpty();
}
}
287. 寻找重复数
把这道题看成在环形链表中找到环形的入口。
数组中的每个数不仅代表当前的值,还代表下一个连接的数的下标。
采用快慢指针,一定会在环中的某处相遇,这个相遇点不一定是环的入口。
然后此时把快指针置为nums[0],之后快慢指针每次都只走一步,再次相遇时一定是环的入口。
class Solution {
public int findDuplicate(int[] nums) {
int slow = nums[0], fast = nums[0];
while (true) {
slow = nums[slow];
fast = nums[nums[fast]];
if (slow == fast) {
break;
}
}
fast = nums[0];
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
}
289. 生命游戏
再开一个二维数组,时间复杂度On2,空间复杂度On2
class Solution {
int[] X = {-1, 0, 1, 1, 1, 0, -1, -1}, Y = {1, 1, 1, 0, -1, -1, -1, 0};
private int cal(int[][] board, int i, int j) {
int count = 0;
for (int k = 0; k < 8; k++) {
int newI = i + X[k], newJ = j + Y[k];
if (newI >= 0 && newI < board.length && newJ >= 0 && newJ < board[0].length && board[newI][newJ] == 1) {
count++;
}
}
if (board[i][j] == 1) {
if (count < 2) {
return 0;
} else if (count == 2 || count == 3) {
return 1;
} else {
return 0;
}
} else if (board[i][j] == 0) {
if (count == 3) {
return 1;
} else {
return 0;
}
}
return -1;
}
public void gameOfLife(int[][] board) {
if (board.length == 0) {
return;
}
int row = board.length, col = board[0].length;
int[][] res = new int[row][col];
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
res[i][j] = cal(board, i, j);
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] = res[i][j];
}
}
}
}
不开新的数组,如果以前是活的后来死了,标记为2,如果以前是死的,后来活了标记为3。最后再更新成0或1即可。
class Solution {
int[] X = {-1, 0, 1, 1, 1, 0, -1, -1}, Y = {1, 1, 1, 0, -1, -1, -1, 0};
private int cal(int[][] board, int i, int j) {
int count = 0;
for (int k = 0; k < 8; k++) {
int newI = i + X[k], newJ = j + Y[k];
if (newI >= 0 && newI < board.length && newJ >= 0 && newJ < board[0].length && (board[newI][newJ] == 1 || board[newI][newJ] == 2)) {
count++;
}
}
if (board[i][j] == 1) {
if (count < 2) {
return 2;
} else if (count == 2 || count == 3) {
return 1;
} else {
return 2;
}
} else if (board[i][j] == 0) {
if (count == 3) {
return 3;
} else {
return 0;
}
}
return -1;
}
public void gameOfLife(int[][] board) {
if (board.length == 0) {
return;
}
int row = board.length, col = board[0].length;
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
board[i][j] = cal(board, i, j);
}
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (board[i][j] == 2) {
board[i][j] = 0;
} else if (board[i][j] == 3) {
board[i][j] = 1;
}
}
}
}
}
290. 单词规律
class Solution {
public boolean wordPattern(String pattern, String s) {
Map map = new HashMap<>();
String[] splits = s.split(" ");
if (splits.length != pattern.length()) {
return false;
}
for (int i = 0; i < splits.length; i++) {
if (map.containsKey(pattern.charAt(i))) {
if (!map.get(pattern.charAt(i)).equals(splits[i])) {
return false;
}
} else {
if (map.containsValue(splits[i])) {
return false;
}
map.put(pattern.charAt(i), splits[i]);
}
}
return true;
}
}
292. Nim 游戏
class Solution {
public boolean canWinNim(int n) {
return n % 4 != 0;
}
}
295. 数据流的中位数
比较容易想到的就是用数组存数据,每次查找的时候先排序之后再输出中位数。时间复杂度Onlogn,空间复杂度On。
使用大根堆存储左半部分的数字,小根堆来存储右半部分的数字。
需要保证大根堆中的所有数字都要小于小根堆的所有数字,且大根堆的数字个数必须等于小根堆的数字个数或者比小根堆的数字个数大一。
那么只要总数字个数为奇数时,大根堆的堆顶就是中位数;总数字个数为偶数时,大根堆的堆顶与小根堆的堆顶的平均数就是中位数。
使用size来记录当前的数字总数,每次增加数字的时候,先加到大根堆,再把大根堆的堆顶加到小根堆,这样就可以保证大根堆的数一定比小根堆的数小。只要size为奇数,还要再将小根堆的堆顶加到大根堆去,这样就可以保证大根堆的数字个数比小根堆大1或者相等。
class MedianFinder {
Queue maxHeap, minHeap;
int size;
/**
* initialize your data structure here.
*/
public MedianFinder() {
maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
minHeap = new PriorityQueue<>();
size = 0;
}
public void addNum(int num) {
size++;
maxHeap.add(num);
minHeap.add(maxHeap.poll());
if (size % 2 == 1) {
maxHeap.add(minHeap.poll());
}
}
public double findMedian() {
if (size % 2 == 1) {
return maxHeap.peek();
} else {
return (maxHeap.peek() + minHeap.peek()) / 2.0;
}
}
}
297. 二叉树的序列化与反序列化
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) {
return "null";
}
StringBuilder res = new StringBuilder();
Queue q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()) {
TreeNode front = q.poll();
if (front == null) {
res.append("null,");
continue;
} else {
res.append(front.val + ",");
}
q.add(front.left);
q.add(front.right);
}
res.deleteCharAt(res.length() - 1);
return res.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if ("null".equals(data)) {
return null;
}
String[] splits = data.split(",");
Queue q = new LinkedList<>();
TreeNode root = new TreeNode(Integer.parseInt(splits[0]));
q.add(root);
for (int i = 1; i < splits.length; i++) {
TreeNode front = q.poll();
if (!"null".equals(splits[i])) {
TreeNode left = new TreeNode(Integer.parseInt(splits[i]));
front.left = left;
q.add(left);
}
i++;
if (!"null".equals(splits[i])) {
TreeNode right = new TreeNode(Integer.parseInt(splits[i]));
front.right = right;
q.add(right);
}
}
return root;
}
}
299. 猜数字游戏
class Solution {
public String getHint(String secret, String guess) {
Map mp1 = new HashMap<>(), mp2 = new HashMap<>();
int bulls = 0, cows = 0;
for (int i = 0; i < secret.length(); i++) {
if (secret.charAt(i) == guess.charAt(i)) {
bulls++;
} else {
mp1.put(secret.charAt(i) - '0', mp1.getOrDefault(secret.charAt(i) - '0', 0) + 1);
mp2.put(guess.charAt(i) - '0', mp2.getOrDefault(guess.charAt(i) - '0', 0) + 1);
}
}
for (Integer key : mp1.keySet()) {
if (mp2.containsKey(key)) {
cows += Math.min(mp1.get(key), mp2.get(key));
}
}
return bulls + "A" + cows + "B";
}
}
300. 最长上升子序列
经典dp问题。
dp[i]代表以i结尾最长上升子序列长度。
边界:dp[0] = 1
转移方程:
dp[i] 等于所有从0到i-1中的所有dp[j]较大值+1,且必须要满足nums[i] > nums[j]。
再更新所有dp的最大值即可。
时间复杂度On2,空间复杂度On。
class Solution {
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) {
return 0;
}
int[] dp = new int[nums.length];
int res = 1;
dp[0] = 1;
for (int i = 1; i < nums.length; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
res = Math.max(res, dp[i]);
}
return res;
}
}
303. 区域和检索 - 数组不可变
class NumArray {
int[] preSum;
public NumArray(int[] nums) {
preSum = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
preSum[i] = (i - 1 >= 0 ? preSum[i - 1] : 0) + nums[i];
}
}
public int sumRange(int i, int j) {
return preSum[j] - (i - 1 >= 0 ? preSum[i - 1] : 0);
}
}
304. 二维区域和检索 - 矩阵不可变
dp[i][j]代表从(0,0)到(i,j)的矩形的元素总和。
边界:
第一行dp[0][j] = dp[0][j - 1] + matrix[0][j]
第一列dp[i][0] = dp[i - 1][0] + matrix[i][0];
转移方程:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + matrix[i][j];
题目要求(row1,col1)与(row2,col2)围成的矩形的元素总和。
等于 dp[row2][col2] - dp[row1 - 1][col2] - dp[row2][col1 - 1] +dp[row1 - 1][col1 - 1],注意判断边界情况即可。
class NumMatrix {
static int[][] dp;
public NumMatrix(int[][] matrix) {
if (matrix.length != 0) {
int row = matrix.length, col = matrix[0].length;
dp = new int[row][col];
dp[0][0] = matrix[0][0];
for (int i = 1; i < row; i++) {
dp[i][0] = dp[i - 1][0] + matrix[i][0];
}
for (int j = 1; j < col; j++) {
dp[0][j] = dp[0][j - 1] + matrix[0][j];
}
for (int i = 1; i < row; i++) {
for (int j = 1; j < col; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + matrix[i][j];
}
}
}
}
public int sumRegion(int row1, int col1, int row2, int col2) {
if (dp == null) {
return 0;
} else {
return dp[row2][col2] - (row1 - 1 >= 0 ? dp[row1 - 1][col2] : 0) -
(col1 - 1 >= 0 ? dp[row2][col1 - 1] : 0) +
(row1 - 1 >= 0 && col1 - 1 >= 0 ? dp[row1 - 1][col1 - 1] : 0);
}
}
}
306. 累加数
回溯
import java.math.BigDecimal;
class Solution {
boolean res = false;
List temp = new ArrayList<>();
private boolean judge(String sub) {
BigDecimal a = new BigDecimal(temp.get(temp.size() - 2));
BigDecimal b = new BigDecimal(temp.get(temp.size() - 1));
BigDecimal c = new BigDecimal(sub);
return a.add(b).equals(c);
}
private void dfs(int begin, String num) {
if (begin == num.length()) {
if (temp.size() > 2) {
res = true;
}
return;
}
for (int i = begin; i < num.length(); i++) {
String sub = num.substring(begin, i + 1);
if (sub.length() > 1 && sub.charAt(0) == '0') {
break;
}
if (temp.size() < 2 || (temp.size() >= 2 && judge(sub))) {
temp.add(sub);
dfs(i + 1, num);
if (res == true) {
return;
}
temp.remove(temp.size() - 1);
}
}
}
public boolean isAdditiveNumber(String num) {
dfs(0, num);
return res;
}
}
307. 区域和检索 - 数组可修改
class NumArray {
int[] pre, nums;
public NumArray(int[] nums) {
this.nums = nums;
pre = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
pre[i] = (i - 1 >= 0 ? pre[i - 1] : 0) + nums[i];
}
}
public void update(int i, int val) {
int diff = nums[i] - val;
nums[i] = val;
for (int index = i; index < pre.length; index++) {
pre[index] -= diff;
}
}
public int sumRange(int i, int j) {
return pre[j] - (i - 1 >= 0 ? pre[i - 1] : 0);
}
}
309. 最佳买卖股票时机含冷冻期
dp[i][j]代表第i天结束时,手中持有j份股票的最大收益。
边界:
第0天结束时,手中持有0份收益为0;手中持有1份,收益为-price[0]
转移方程:
dp[i][0]等于 前一天休息或者前一天手中持有一份今天卖出,两者收益的较大值。
dp[i][1]等于 前一天休息 或者 前二天手中持有0份今天买入,两者收益的较大值。
最后的答案就是dp[len-1][0]
时间复杂度On,空间复杂度On^2
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
if (len == 0) {
return 0;
}
int[][] dp = new int[len][2];
dp[0][1] = -prices[0];
for (int i = 1; i < len; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], (i - 2 >= 0 ? dp[i - 2][0] : 0) - prices[i]);
}
return dp[len - 1][0];
}
}
310. 最小高度树
暴力bfs,枚举所有的结点,判断以它为根的树的高度,最后一个案例超时。
class Solution {
public List findMinHeightTrees(int n, int[][] edges) {
List res = new ArrayList<>();
List[] G = new ArrayList[n];
for (int i = 0; i < n; i++) {
G[i] = new ArrayList<>();
}
for (int[] edge : edges) {
G[edge[0]].add(edge[1]);
G[edge[1]].add(edge[0]);
}
boolean[] isvisit = new boolean[n];
int min = Integer.MAX_VALUE;
for (int i = 0; i < n; i++) {
Queue q = new LinkedList<>();
q.offer(i);
int count = 0;
while (!q.isEmpty()) {
int size = q.size();
for (int j = 0; j < size; j++) {
int front = q.poll();
isvisit[front] = true;
for (int k = 0; k < G[front].size(); k++) {
if (isvisit[G[front].get(k)] == false) {
q.offer(G[front].get(k));
}
}
}
count++;
}
if (count < min) {
min = count;
res.clear();
res.add(i);
} else if (count == min) {
res.add(i);
}
Arrays.fill(isvisit, false);
}
return res;
}
}
拓扑排序:
degrees[i]代表第i个结点的出度,当出度为1时,它一定是叶子结点。
按照拓扑排序的思想,一层一层的把叶子结点去掉。
当最后只剩一个结点或两个结点的时候,一定是最深的结点。
class Solution {
public List findMinHeightTrees(int n, int[][] edges) {
List res = new ArrayList<>();
if (edges.length == 0) {
res.add(0);
return res;
}
List[] G = new ArrayList[n];
int[] degrees = new int[n];
for (int i = 0; i < n; i++) {
G[i] = new ArrayList<>();
}
for (int[] edge : edges) {
G[edge[0]].add(edge[1]);
G[edge[1]].add(edge[0]);
degrees[edge[0]]++;
degrees[edge[1]]++;
}
Queue q = new LinkedList<>();
for (int i = 0; i < n; i++) {
if (degrees[i] == 1) {
q.offer(i);
}
}
while (n > 2) {
int size = q.size();
for (int i = 0; i < size; i++) {
int front = q.poll();
for (int next : G[front]) {
degrees[next]--;
if (degrees[next] == 1) {
q.offer(next);
}
}
G[front].clear();
}
n -= size;
}
while (!q.isEmpty()) {
res.add(q.poll());
}
return res;
}
}