120. Triangle

问题
Given a triangle array, return the minimum path sum from top to bottom.

For each step, you may move to an adjacent number of the row below. More formally, if you are on index i on the current row, you may move to either index i or index i + 1 on the next row.

动态规划,先将最左侧一列的值算出。
然后[i+1][j+1]根据[i][j+1]和[i][j]得出。
该动态规划表应是为三角形。
因此当i等于j时,[i+1][i+j]的数值只根据[i][j]得出。

例子:
代码里插入了一个print方法打印动态规划表。
当输入列表 [[2],[3,4],[6,5,7],[4,1,8,3]] 时:
其动态规划表为:

  • 2, 0, 0, 0,
  • 5, 6, 0, 0,
  • 11, 10, 13, 0,
  • 15, 11, 18, 16,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int[][] dp = new int[triangle.size()][triangle.size()];
int min = Integer.MAX_VALUE;
dp[0][0] = triangle.get(0).get(0);

for (int i = 0; i < triangle.size()-1; i++){
dp[i+1][0] = dp[i][0] + triangle.get(i+1).get(0);
}

for (int i = 0; i < triangle.size()-1; i++){
for (int j = 0; j <= i; j++){

if ( i == j ){
dp[i+1][j+1] = dp[i][j] + triangle.get(i+1).get(j+1);
}
else{
dp[i+1][j+1] = Math.min(dp[i][j] + triangle.get(i+1).get(j+1),
dp[i][j+1] + triangle.get(i+1).get(j+1));
}

}
}
//print(dp);
for (int k = 0; k < triangle.size(); k++){
min = Math.min(dp[triangle.size()-1][k],min);
}
return min;
}

private void print(int[][] text){
for (int i = 0; i <text.length; i++){
for (int j = 0; j < text[0].length; j++){
System.out.print(text[i][j]+", ");
}
System.out.println();
}
}

}

198. House Robber

问题
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.

动态规划。
dp数组记录经过i个房子后可以获得的最大值。
dp[i+1]的值等于dp[i-1]加上现有房子的钱(抢这个房子)与dp[i]的值(不抢这个房子)中的较大值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public int rob(int[] nums) {

int[] money = new int[nums.length+1];
money[0] = 0;
money[1] = nums[0];

for (int i = 1; i < nums.length; i++){
money[i+1] = Math.max(money[i-1]+nums[i],money[i]);

}

return money[nums.length];

}
}

784. Letter Case Permutation

答案
Given a string s, you can transform every letter individually to be lowercase or uppercase to create another string.

Return a list of all possible strings we could create. Return the output in any order.

当前字符如果为数字,则直接添加并递归。(将字符隐式转换为整数判断是否为数字,可提升速度。)
当前字符如果为字母,则大小写分别添加到递归。(类似于回溯。)
当字符串长度与搜寻字符串相等时,添加到列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
List<String> ans;
String _s;
public List<String> letterCasePermutation(String s) {
ans = new ArrayList();
_s = s;

backTrack(new StringBuilder(),0);
return ans;

}

private void backTrack(StringBuilder sb, int i){
if(i == _s.length()){
ans.add(sb.toString());
return;
}
char curr = _s.charAt(i);
if ( isNums(curr) ){
sb.append(curr);
backTrack(sb, i+1);
}
else{
StringBuilder sb2 = new StringBuilder(sb);
sb.append(Character.toLowerCase(curr));
backTrack(sb, i+1);
sb2.append(Character.toUpperCase(curr));
backTrack(sb2, i+1);
}
}

private boolean isNums(char c){
if ( (int)c >= (int)'0' && (int)c <= (int)'9' ){
return true;
}
return false;
}
}

46. Permutations

问题
Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.

回溯,建立搜索树。
每次遍历nums中的元素。
如果未遍历过该元素,则向链表及set中添加。
向下递归,链表长度达到nums的长度时返回。
然后从set和链表中移除上一个值,回溯到上一个节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
List<List<Integer>> ans;
HashSet<Integer> set;
public List<List<Integer>> permute(int[] nums) {
ans = new ArrayList();
set = new HashSet();
backTrack(new LinkedList(), nums, nums.length, nums.length);
return ans;
}

private void backTrack(LinkedList<Integer> list,int[] nums, int n, int k){
if(k == 0){
ans.add(new ArrayList(list));
return;
}
for(int i = 0; i < n ; i++){
if(!set.contains(nums[i])){
list.add(nums[i]);
set.add(nums[i]);
backTrack(list, nums , n, k-1);
set.remove(nums[i]);
list.removeLast();
}
}
}
}

102. Binary Tree Level Order Traversal

Question

Given the root of a binary tree, return the level order traversal of its nodes’ values. (i.e., from left to right, level by level).

Solution

BFS搜索,层序遍历,记录每个层级的节点数量size。
在遍历时挤出size个节点,并将其子节点加入队列。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* Definition for a binary tree node.
* 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;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
if(root == null) return ret;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);

int size = 1;
while(!q.isEmpty()){
List<Integer> list = new ArrayList<>();
TreeNode curr = null;
for(int i = 0; i < size; i++){
curr = q.poll();
if(curr.left != null) q.offer(curr.left);
if(curr.right != null) q.offer(curr.right);
list.add(curr.val);
}
ret.add(list);
size = q.size();
}
return ret;
}
}

77. Combinations

问题
Given two integers n and k, return all possible combinations of k numbers out of the range [1, n].

You may return the answer in any order.

回溯,构建搜索树。
子节点取出的数值应大于父节点中取出的数值。
直到树高度达到k后返回。

返回时,要new一个List,将原有list传入。
否则添加到ans的值只是list的内存地址。
ArrayList换成LinkedList可以优化一些速度,因为可以直接removeLast。(22ms -> 16ms)
i的范围限制在start到n-k+1,后面的限制容易被忽略,可以大幅度减枝,优化速度。(16ms -> 1ms)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
List<List<Integer>> ans;
public List<List<Integer>> combine(int n, int k) {
ans = new ArrayList<>();
backTrack(new LinkedList(),1,n,k);
return ans;
}

private void backTrack(LinkedList<Integer> list, int start, int n, int k){
if (k == 0){
ans.add(new ArrayList(list));
return;
}

for (int i = start; i <= n-k+1; i++){
list.add(i);
backTrack(list, i+1, n, k-1);
list.removeLast();
}
}
}

59. Spiral Matrix II

问题
Given a positive integer n, generate an n x n matrix filled with elements from 1 to n2 in spiral order.

循环,创建一个上界和一个下界。
当达到界限时,改变方向。
更新上界和下界的数值。
当上界小于下界时返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Solution {
public int[][] generateMatrix(int n) {
int[][] ans = new int[n][n];
int upperBound = n;
int lowerBound = 0;
int i = 0;
int j = 0;
int count = 1;
ans[0][0] = 1;

while ( lowerBound < upperBound ){
while ( j < upperBound-1 ){
j++;
count++;
ans[i][j] = count;
}
while ( i < upperBound-1 ){
i++;
count++;
ans[i][j] = count;
}
while ( j > lowerBound ){
j--;
count++;
ans[i][j] = count;
}
upperBound--;
lowerBound++;
while ( i > lowerBound ){
i--;
count++;
ans[i][j] = count;
}
}
return ans;
}
}

289. Game of Life

问题
According to Wikipedia’s article: “The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.”

The board is made up of an m x n grid of cells, where each cell has an initial state: live (represented by a 1) or dead (represented by a 0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):

  1. Any live cell with fewer than two live neighbors dies as if caused by under-population.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by over-population.
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

The next state is created by applying the above rules simultaneously to every cell in the current state, where births and deaths occur simultaneously. Given the current state of the m x n grid board, return the next state.

辅助方法,计算每个位置四周有生命的总和。
根据规则填写到新数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Solution {
public void gameOfLife(int[][] board) {
int[][] ans = new int[board.length][board[0].length];
for (int i = 0; i < board.length; i++){
for (int j = 0; j < board[0].length; j++){
int neighbors = countNeighbors(board, i, j);
if ( neighbors < 2 && board[i][j] == 1 ){
ans[i][j] = 0;
}
else if( neighbors <= 3 && board[i][j] == 1 ){
ans[i][j] = 1;
}
else if( neighbors > 3 && board[i][j] == 1 ){
ans[i][j] = 0;
}
else if( neighbors == 3 && board[i][j] == 0){
ans[i][j] = 1;
}

}
}

for (int i = 0; i < board.length; i++){
for (int j = 0; j < board[0].length; j++){
board[i][j] = ans[i][j];
}
}
}


private int countNeighbors(int[][] board, int r, int c){
int neighbors = 0;
int row = board.length;
int col = board[0].length;

if(r + 1 < row && board[r+1][c] == 1){ neighbors++; }
if(r - 1 >= 0 && board[r-1][c] == 1){ neighbors++; }
if(c + 1 < col && board[r][c+1] == 1){ neighbors++; }
if(c - 1 >= 0 && board[r][c-1] == 1){ neighbors++; }

if(r + 1 < row && c + 1 < col && board[r+1][c+1] == 1){ neighbors++; }
if(r + 1 < row && c - 1 >= 0 && board[r+1][c-1] == 1 ){ neighbors++; }
if(r - 1 >= 0 && c + 1 < col && board[r-1][c+1] == 1 ){ neighbors++; }
if(r - 1 >= 0 && c - 1 >= 0 && board[r-1][c-1] == 1 ){ neighbors++; }

return neighbors;
}
}

994. Rotting Oranges

问题
You are given an m x n grid where each cell can have one of three values:

  • 0 representing an empty cell,
  • 1 representing a fresh orange, or
  • 2 representing a rotten orange.
    Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten.

Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1.

由于腐烂的橘子每次都只能影响周围的橘子,因此采用BFS。
将所有腐烂的橘子加入队列。
如果没有新鲜的橘子,则返回0。
每次出队列,如果周围有新鲜的橘子存在,则将新鲜的橘子替换为腐烂并加入队列。
每个level结束后,time+1。

最后遍历一遍,如果还有新鲜的橘子,返回-1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class Solution {
public int orangesRotting(int[][] grid) {
Queue<Integer> q = new LinkedList();
int row = grid.length;
int col = grid[0].length;
int fresh = 0;
for (int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
if ( grid[i][j] == 2){
q.add(i * col + j);
}
else if (grid[i][j] == 1){
fresh++;
}
}
}
if(fresh == 0){return 0;}

int count = q.size()-1;
int temp = 0;
int time = -1;
while(!q.isEmpty()){
int i = q.peek() / col;
int j = q.poll() % col;

if(i-1>=0 && grid[i-1][j] == 1){
grid[i-1][j] = 2;
q.add((i-1)*col+j);
temp++;
}
if(j-1>=0 && grid[i][j-1] == 1){
grid[i][j-1] = 2;
q.add(i*col+(j-1));
temp++;
}
if(i+1<row && grid[i+1][j] == 1){
grid[i+1][j] = 2;
q.add((i+1)*col+j);
temp++;
}
if(j+1<col && grid[i][j+1] == 1){
grid[i][j+1] = 2;
q.add(i*col+(j+1));
temp++;
}

if(count == 0){
count = temp;
temp = 0;
time++;
}
count--;
}

for (int i = 0; i < row; i++){
for(int j = 0; j < col; j++){
if ( grid[i][j] == 1){
return -1;
}
}
}
return time;

}
}

116. Populating Next Right Pointers in Each Node

问题
You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has the following definition:

1
2
3
4
5
6
 struct Node {
int val;
Node *left;
Node *right;
Node *next;
>}

Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL.

Initially, all next pointers are set to NULL.

递归,当root为null时返回。
如果root有右节点,则左节点next指向右节点。
如果root有右节点同时next已经指向了一个节点,则将其右节点next指向该节点的左子节点。
递归左右子节点,并返回root。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*
// Definition for a Node.
class Node {
public int val;
public Node left;
public Node right;
public Node next;

public Node() {}

public Node(int _val) {
val = _val;
}

public Node(int _val, Node _left, Node _right, Node _next) {
val = _val;
left = _left;
right = _right;
next = _next;
}
};
*/

class Solution {
public Node connect(Node root) {
if (root==null){return root;}
if (root.right!=null){
root.left.next = root.right;
if (root.next!=null){
root.right.next = root.next.left;
}
}
connect(root.left);
connect(root.right);
return root;
}
}

BFS搜索每一个节点,将节点指向队列中next下一个节点。
当计数器达到2的指数时,将节点指向null。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Solution {
public Node connect(Node root) {
if (root == null){
return root;
}

int count = 1;
Queue<Node> q = new LinkedList();
q.offer(root);
while (!q.isEmpty()){
count++;
Node curr = q.poll();
if ( isPow(count) ){
curr.next = null;
}
else{
curr.next = q.peek();
}
if(curr.left!=null){
q.add(curr.left);
}
if(curr.right!=null){
q.add(curr.right);
}

}
return root;
}

private boolean isPow(int val){
if(val == 0 || val == 1){
return false;
}
while ( val % 2 == 0 ){
val = val / 2;
}
if (val == 1){
return true;
}
return false;
}
}
695. Max Area of Island

695. Max Area of Island

Question

You are given an m x n binary matrix grid. An island is a group of 1’s (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water.

The area of an island is the number of cells with a value 1 in the island.

Return the maximum area of an island in grid. If there is no island, return 0.

Solution

DFS搜索, 维护一个变量count,为连续执行DFS搜索的次数。
遍历地图上所有为1的位置。

运行BFS时每次将全局变量count增加1。
执行完毕后BFS时将全局变量count清零。

遍历每个等于1的地图块。
递归周围四个地图块,当超越数组范围,或者当前位置不为1时返回。
进行搜索时将搜索过的地图块标记为0,不再重复搜索。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
int[][] visited, directions;
int count;
public int maxAreaOfIsland(int[][] grid) {
int res = 0;
directions = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
for(int x = 0; x < grid.length; x++){
for(int y = 0; y < grid[0].length; y++){
if(grid[x][y] == 1){
count = 0;
dfs(grid, x, y);
res = Math.max(res, count);
}
}
}
return res;
}

private void dfs(int[][] 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;
count++;
for(int[] direction : directions){
int i = x + direction[0];
int j = y + direction[1];
dfs(grid, i, j);
}
}
}

347. Top K Frequent Elements

Question

Given an integer array nums and an integer k, return the k most frequent elements. You may return the answer in any order.

Solution

遍历,使用哈希表保存遍历次数。
再次遍历,根据元素出现的次数将其填入优先级队列实现的大根堆。
遍历取出k个最大值。

  • getOrDefault():
    方便的遍历并生成哈希表。
  • lambda:
    ()内表示传入的数值。
    -> 后表示返回值。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int[] ans = new int[k];
HashMap<Integer,Integer> map = new HashMap();
for (int num : nums){
map.put( num, map.getOrDefault(num , 0) + 1 );
}
PriorityQueue<Integer> pq = new PriorityQueue((a,b) -> map.get(b) - map.get(a));
for (int key : map.keySet()){
pq.add(key);
}
for (int i = 0; i < k ; i++){
ans[i] = pq.poll();
}
return ans;
}
}

567. Permutation in String

问题
Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise.

In other words, return true if one of s1’s permutations is the substring of s2.

将要查找的组合加入数组,数值为字符出现的次数。
滑动窗口,入窗口对应的元素数值-1,出窗口对应的元素数值+1。
每次移动窗口都检验一次数组的数值是否全部为0,如果是真,则返回真。
小技巧:直接用数组来记录字符出现的次数,用字符减去与’a’的差作为下标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public boolean checkInclusion(String s1, String s2) {
if (s1.length() > s2.length()){
return false;
}

int[] dic = new int[26];
for (int i = 0; i < s1.length(); i++){
dic[s1.charAt(i)-'a']++;
dic[s2.charAt(i)-'a']--;
}

int i = 0;
int j = s1.length();

while( j < s2.length() ){
if ( allZero(dic) ){
return true;
}
dic[s2.charAt(i)-'a']++;
dic[s2.charAt(j)-'a']--;
i++;
j++;
}
return allZero(dic);
}

private boolean allZero(int[] dic){
for (int num : dic){
if ( num != 0 ){
return false;
}
}
return true;
}
}
3. Longest Substring Without Repeating Characters

3. Longest Substring Without Repeating Characters

Question

Given a string s, find the length of the longest substring without repeating characters.

Solution 1

滑动窗口加数组统计。
用一个数组bin[]记录各个字符的访问情况。(数组尺寸由字符串的总数决定)

当指针j第一次遍历到一个字符时,如果该字符对应的位置为0,则将其设置为1。
否则对前面的i指针进行遍历,并将i经过的位置重新设置为0。如果i的字符与当前j字符相等,则将bin[index]设置为0,计算当前的长度j-i并更新最大值best。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public int lengthOfLongestSubstring(String s) {
int best = 0, i = 0, j = 0;
char[] word = s.toCharArray();
int[] bin = new int[128];
while(j < s.length()){
int index = word[j] - ' ';
if(bin[index] == 0){
bin[index] = 1;
j++;
}
else{
while(i < j){
int temp = word[i] - ' ';
if(temp == index && bin[index] == 1){
bin[index] = 0;
best = Math.max(best, j - i);
i++;
break;
}
bin[temp] = 0;
i++;
}
}
best = Math.max(best, j - i);
}
return best;
}
}

Solution 2

滑动窗口,哈希表记录访问过的字符的元素。
如果重复,则放弃前一个重复的字符,更新左指针。
注意:只有在新指针大于现有指针时才更新!

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public int lengthOfLongestSubstring(String s) {
int best = 0;
int i = 0;
int j = 0;

HashMap<Character, Integer> map = new HashMap<Character, Integer>();

while ( j < s.length() ){
char curChar = s.charAt(j);
if ( !map.containsKey(curChar) ){
map.put( curChar, j );
}
else{
if ( map.get(curChar) + 1 > i){
i = map.get(curChar) + 1;
}

map.put( curChar, j );
}

if ((j - i + 1) > best){
best = (j - i + 1);
}
j++;
}
return best;
}
}

19. Remove Nth Node From End of List

问题
Given the head of a linked list, remove the nth node from the end of the list and return its head.


双指针,同时记录前n个节点和当前节点。
当前指针到链表尾部时,删除前面的指针,注意处理edge cases。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
* 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 preNode = null;
ListNode removedNode = head;
ListNode fastNode = head;
for ( int i = 0; i < n; i++ ){
fastNode = fastNode.next;
}

while ( fastNode != null ){
fastNode = fastNode.next;
preNode = removedNode;
removedNode = removedNode.next;
}

if ( removedNode == head ){
head = head.next;
}
else if ( removedNode.next == null){
preNode.next = null;
}
else{
preNode.next = removedNode.next;
}

return head;
}
}