1143. Longest Common Subsequence

Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.

A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.

For example, “ace” is a subsequence of “abcde”.
A common subsequence of two strings is a subsequence that is common to both strings.

动态规划,dp的长和宽分别为两个字符串的长度。
遍历dp字符串的长和宽。(注意在第一层遍历时先保存当前选择的字符可以提升速度。)
当两个字符串相同时,则可以从上一个状态+1。
注意状态转移方程的上一个状态应该是从对角线方向+1。
当两个字符串不同时,则继续保存之前的最大长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int longestCommonSubsequence(String text1, String text2) {
int m = text1.length(), n = text2.length();
int[][] dp = new int[m+1][n+1];
char[] bin1 = text1.toCharArray();
char[] bin2 = text2.toCharArray();

for(int i = 1; i <= m; i++){
int k = bin1[i-1];
for(int j = 1; j <= n ; j++){
if(k == bin2[j-1]){
dp[i][j] = dp[i-1][j-1]+1;
}
else{
dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]);
}
}
}
return dp[m][n];
}
}

103. Binary Tree Zigzag Level Order Traversal

Given the root of a binary tree, return the zigzag level order traversal of its nodes’ values. (i.e., from left to right, then right to left for the next level and alternate between).

BFS搜索,每次遍历挤出整层的节点。
根据真值reverse来判断列表顺序。
当reverse为真,则每次添加元素到列表头。
反之则添加元素到列表尾。

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
/**
* 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>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
if(root == null) return ret;
Deque<TreeNode> q = new LinkedList<>();
q.add(root);
int size = 1;
boolean reverse = false;

while(!q.isEmpty()){
List<Integer> level = new ArrayList<Integer>();
for(int i = 0; i < size; i++){
TreeNode curr = q.poll();
if(reverse){
level.add(0, curr.val);
}
else{
level.add(curr.val);
}
if(curr.left != null) q.add(curr.left);
if(curr.right != null) q.add(curr.right);

}

reverse = !reverse;
ret.add(level);
size = q.size();
}
return ret;
}
}

双端队列,根据真值reverse来判断是取队头还是取队尾。
每层级需要遍历所有的节点。
注意添加节点时也需要根据reverse来决定加入对头还是队尾。
(从一个层级取出时,加入下一个层级元素需要从队列另一边压入。)
同时左右节点的加入顺序也需要调整。

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
/**
* 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>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
if(root == null) return ret;
Deque<TreeNode> dq = new LinkedList<>();
dq.add(root);
int size = 1;
boolean reverse = false;

while(!dq.isEmpty()){
List<Integer> temp = new ArrayList<Integer>();
for(int i = 0; i < size; i++){
if(reverse){
TreeNode curr = dq.removeLast();
if(curr.right != null) dq.offerFirst(curr.right);
if(curr.left != null) dq.offerFirst(curr.left);
temp.add(curr.val);
}
else{
TreeNode curr = dq.removeFirst();
if(curr.left != null) dq.offerLast(curr.left);
if(curr.right != null) dq.offerLast(curr.right);
temp.add(curr.val);
}
}

reverse = !reverse;
ret.add(temp);
size = dq.size();
}
return ret;
}
}

139. Word Break

Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.

Note that the same word in the dictionary may be reused multiple times in the segmentation.

动态规划,创建一个数组dp[],长度为字符串长度+1,记录是否可以达到。
将dp[0]设置为1,表示可以达到。

从可取长度为1开始,遍历直到可取长度为字符串的长度。
用下一个遍历j将字符串分为两部分。(这里j从i-1下降到0会比从0上升到i-1更快。)
当dp[j]可以到达,且当前的哈希表中有(i-j)组成的字符串时,则dp[i]也可以到达。结束第二层循环。
继续搜索更长的字符串是否可以达到。
最后返回dp中的最后一个元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public boolean wordBreak(String s, List<String> wordDict) {
Set<String> set = new HashSet<>();
int n = s.length();
int[] dp = new int[n+1];

for(String word : wordDict){
set.add(word);
}
dp[0] = 1;
int temp = 0;
for(int i = 1; i <= n; i++){
for(int j = i - 1; j >= 0; j--){
if(dp[j] == 1 && set.contains(s.substring(j, i))){
dp[i] = 1;
break;
}
}
}
return dp[n] == 1;
}
}

91. Decode Ways

A message containing letters from A-Z can be encoded into numbers using the following mapping:

‘A’ -> “1”
‘B’ -> “2”

‘Z’ -> “26”
To decode an encoded message, all the digits must be grouped then mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example, “11106” can be mapped into:

  • “AAJF” with the grouping (1 1 10 6)
  • “KJF” with the grouping (11 10 6)
    Note that the grouping (1 11 06) is invalid because “06” cannot be mapped into ‘F’ since “6” is different from “06”.

Given a string s containing only digits, return the number of ways to decode it.

The test cases are generated so that the answer fits in a 32-bit integer.

动态规划。转移方程的考虑比较麻烦。
当当前的字符不为0时,该为可以自己组成有效编码,因此dp[i] = dp[i-1]。
然后考虑上一位可以和当前位组成有效编码的情况,如果上一位和当前位的数字小于26,且上一位不等于0,则可以联合编码。
因此dp需要再加上前一位可以组成的编码数,即dp[i] += dp[i-2]。
当i等于1时,由于会越界因此我们手动给dp[i]++。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int numDecodings(String s) {
int n = s.length();
if(s.charAt(0) == '0') return 0;
int[]dp = new int[n];
dp[0] = 1;

for(int i = 1 ; i < n; i++){
if(s.charAt(i) != '0'){
dp[i] = dp[i-1];
}
if(s.charAt(i-1) != '0' && (s.charAt(i-1) - '0') * 10 + (s.charAt(i) - '0') <= 26 ){
if(i > 1) dp[i] += dp[i-2];
else dp[i]++;
}
}
return dp[n-1];
}
}
300. Longest Increasing Subsequence

300. Longest Increasing Subsequence

Question

Given an integer array nums, return the length of the longest strictly increasing subsequence.

A subsequence is a sequence that can be derived from an array by deleting some or no elements without changing the order of the remaining elements. For example, [3,6,2,7] is a subsequence of the array [0,3,1,6,2,2,7].

Solution

贪心算法

创建一个数组记录达到升序的长度时升序数列的最小末尾值。
如果想要增加数组的长度(即增加最大的升序数列长度),则新的数字必须要大于该数组最后一位的大小。
因此在这种情况下,该数组必然是单调递增的。

二分搜索

将第一个数字填入数组。
然后遍历数组,当新一项大于数组的最后一个值时,最大长度加一,并将其加入数组的尾部。
当新一项小于数组的最后一个值时,该数字需要与之前的数组组成新的升序数列。
我们可以替换掉之前数组中比该数字大的第一个数字,代表新组成的数组。(最长数组之间的数字无所谓,只记录最小的末尾值即可。)
我们可以用二分搜索查到该数字需要加入的index。然后替换掉数组中的数字。
单次二分搜索的时间复杂度为O($logn$)。总的时间复杂度为O($nlogn$)。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int lengthOfLIS(int[] nums) {
List<Integer> ans = new ArrayList<>();
int len = 1;
ans.add(nums[0]);

for(int i = 1; i < nums.length; i++){
if(nums[i] > ans.get(ans.size()-1)){
ans.add(nums[i]);
len++;
}
else{
int index = Collections.binarySearch(ans, nums[i]);
if(index < 0){
ans.set(-(index+1), nums[i]);
}
}
}
return len;
}
}

Solution 2

动态规划,创建一个数组dp[]用来记录最大长度,创建max记录最大值。
遍历,先将数组dp[]上的所有位置都填上1。
从0到i-1遍历j。当nums[i] > nums[j]时,更新dp[i]为dp[i]与dp[j]+1中的较大值。
当更新dp[i]时将其和max比较,如果比max大则更新max。
最后返回max值。时间复杂度为O($n_2$)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
int max = 1;

for(int i = 0; i < n; i++){
dp[i] = 1;
for(int j = i-1; j >= 0; j--){
if(nums[i] > nums[j]){
dp[i] = Math.max(dp[i], dp[j]+1);
if(max < dp[i]){
max = dp[i];
break;
}
}
}
}
return max;
}
}

399. Evaluate Division

You are given an array of variable pairs equations and an array of real numbers values, where equations[i] = [Ai, Bi] and values[i] represent the equation Ai / Bi = values[i]. Each Ai or Bi is a string that represents a single variable.

You are also given some queries, where queries[j] = [Cj, Dj] represents the jth query where you must find the answer for Cj / Dj = ?.

Return the answers to all queries. If a single answer cannot be determined, return -1.0.

Note: The input is always valid. You may assume that evaluating the queries will not result in division by zero and that there is no contradiction.

这道题的核心是边上带权的并查集。
如何在find以及union时计算并更新权重是最重要的。
组合过后,根节点的权重依然为1,以其为标准更新其他节点的权重。

先通过一个哈希表建立字符串与id的映射关系,为每个字符串生成一个单独的id。

然后将一个equation里的两个字符串进行union操作。
并查集初始化时,所有权重为1。

在find时,执行路径压缩时需要用当前的权重weight[id]更新为其自身乘以其上一级的权重weight[origin]。
这里需要注意计算的顺序需要从根上的权重,以递归的形式计算到当前权重。
因此我们需要先单独保存parents[id]的位置。然后find(parents[id]),先计算出其上级的weight。
最后再将weight[id]更新为更新后的上一级的权重乘以自身权重。

1
2
3
int origin = parents[id];
parents[id] = find(parents[id]);
weight[id] *= weight[origin];

在union时,将两个集合中的一个的根指向另一个。
例如当结合a与b两个节点时,我们已经计算过了(a -> b)的权重关系,以及(d -> c)的权重关系。
我们将(b -> d)。这时我们可以通过values[i]得到(a -> d)的权重关系。因此我们可以据此得出以下公式:

weight[(b->c)] = weight[(d->c)] × values[(a->d)]/weight[(a->b)]

1
2
parents[p1] = parents[p2];
weight[p1] = weight[id2]*val/weight[id1];

最后,当所有节点都被合并后,我们可以遍历queries。
从哈希表获得对应字符串的id。
当有字符串未在哈希表中时,返回-1.0。
否则计算两个id对应的权重,并添加到答案中。

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 double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
double[] ret = new double[queries.size()];
HashMap<String, Integer> map = new HashMap<>();
UnionFind uf = new UnionFind(equations.size() * 2);
int id = 0;
for(int i = 0; i < equations.size(); i++){
String var1 = equations.get(i).get(0);
String var2 = equations.get(i).get(1);
if( !map.containsKey( var1 ) ){
map.put( var1, id );
id++;
}
if( !map.containsKey( var2 ) ){
map.put( var2, id );
id++;
}
uf.union(map.get(var1), map.get(var2), values[i]);
}
for(int i = 0 ; i < queries.size(); i++){
String var1 = queries.get(i).get(0);
String var2 = queries.get(i).get(1);
Integer id1 = map.get(var1);
Integer id2 = map.get(var2);
if(id1 == null || id2 == null) ret[i] = -1.0;
else ret[i] = uf.isConnected(id1, id2);
}
return ret;
}

class UnionFind{
int[] parents;
double[] weight;
public UnionFind(int n){
parents = new int[n];
weight = new double[n];
for(int i = 0; i < parents.length; i++){
parents[i] = i;
weight[i] = 1.0;
}
}
public int find(int id){
if (id != parents[id]) {
int origin = parents[id];
parents[id] = find(parents[id]);
weight[id] *= weight[origin];
}
return parents[id];
}
public boolean union(int id1, int id2, double val){
int p1 = find(id1);
int p2 = find(id2);
if(p1 == p2) return false;
parents[p1] = parents[p2];
weight[p1] = weight[id2]*val/weight[id1];
return true;
}
public double isConnected(int id1, int id2){
int p1 = find(id1);
int p2 = find(id2);
if(p1 == p2) return weight[id1] / weight[id2];
else return -1.0;
}
}
}

1631. Path With Minimum Effort

You are a hiker preparing for an upcoming hike. You are given heights, a 2D array of size rows x columns, where heights[row][col] represents the height of cell (row, col). You are situated in the top-left cell, (0, 0), and you hope to travel to the bottom-right cell, (rows-1, columns-1) (i.e., 0-indexed). You can move up, down, left, or right, and you wish to find a route that requires the minimum effort.

A route’s effort is the maximum absolute difference in heights between two consecutive cells of the route.

Return the minimum effort required to travel from the top-left cell to the bottom-right cell.

A*算法,启发式搜索。BFS搜索结合Priority Queue。
采用一个数组储存当前访问点的位置,以及其effort。
采用优先队列,优先搜索effort最小的方向。
每次循环倾倒出队列中所有的元素。
计算上一个节点和当前节点的差值作为nextEffort,并和上一个节点的effort作比较,较大的作为当前节点的effort,
将effort作为权重,优先搜索一个层级内effort较小的路径。
将所有操作加入队列,并排除越界的位置。
当当前节点为最后一个节点时,返回其effort。

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
class Solution {
int min;
public int minimumEffortPath(int[][] heights) {
int[][] operations = {{1,0},{-1,0},{0,1},{0,-1}};
int m = heights.length, n = heights[0].length;

Queue<int[]> q = new PriorityQueue<>((a, b) -> a[2] - b[2]);
int[] point = {0, 0, 0};
int size = 1;
int[][] visited = new int[m][n];

q.add(point);
while(!q.isEmpty()){

for(int k = 0; k < size; k++){
int[] curr = q.poll();
int i = curr[0], j = curr[1], currEffort = curr[2];
if(visited[i][j] == 1) continue;
visited[i][j] = 1;
if(i == m-1 && j == n-1) return currEffort;
for(int[] operation : operations){
int nextX = i + operation[0];
int nextY = j + operation[1];
if(nextX < 0 || nextY < 0 || nextX >= m || nextY >= n) continue;
int nextEffort = Math.max(currEffort, Math.abs(heights[i][j] - heights[nextX][nextY]));
int[] next = {nextX, nextY, nextEffort};

q.add(next);
}
}
size = q.size();
}
return -1;
}
}

45. Jump Game II

Given an array of non-negative integers nums, you are initially positioned at the first index of the array.

Each element in the array represents your maximum jump length at that position.

Your goal is to reach the last index in the minimum number of jumps.

You can assume that you can always reach the last index.

设置一个当前可以访问的最大范围limit,在遍历中对其进行更新。
和当前可访问位置中的最远距离end,每次访问到达end时,计算步数。

遍历数组,比较limit和当前i能访问的最远距离i+nums[i],保留较大值。
当i达到end时,更新end为之前记录的可访问最远距离limit。步数+1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int jump(int[] nums) {
int count = 0;
int limit = 0;
int end = 0;

for(int i = 0; i < nums.length-1; i++){
limit = Math.max(limit, i + nums[i]);
if(i == end){
end = limit;
count++;
}
}

return count;
}
}

707. Design Linked List

Design your implementation of the linked list. You can choose to use a singly or doubly linked list.
A node in a singly linked list should have two attributes: val and next. val is the value of the current node, and next is a pointer/reference to the next node.
If you want to use the doubly linked list, you will need one more attribute prev to indicate the previous node in the linked list. Assume all nodes in the linked list are 0-indexed.

Implement the MyLinkedList class:

  • MyLinkedList() Initializes the MyLinkedList object.
  • int get(int index) Get the value of the indexth node in the linked list. If the index is invalid, return -1.
  • void addAtHead(int val) Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list.
  • void addAtTail(int val) Append a node of value val as the last element of the linked list.
  • void addAtIndex(int index, int val) Add a node of value val before the indexth node in the linked list. If index equals the length of the linked list, the node will be appended to the end of the linked list. If index is greater than the length, the node will not be inserted.
  • void deleteAtIndex(int index) Delete the indexth node in the linked list, if the index is valid.

单链表,设置一个哨兵节点在头部。
设置辅助方法,取得index的前一个节点。
addAtHead和addAtTail都可以采用addAtIndex实现。

采用双链表速度可以更快。

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
class MyLinkedList {
ListNode head;
int size;
public MyLinkedList() {
ListNode dummy = new ListNode(0);
head = dummy;
size = 0;
}

public ListNode getPrev(int index){
ListNode prev = head;
for(int i = 0; i < index; i++){
prev = prev.next;
}
return prev;
}

public int get(int index) {
if(index >= size) return -1;
return getPrev(index).next.val;
}

public void addAtHead(int val) {
addAtIndex(0, val);
}

public void addAtTail(int val) {
addAtIndex(size, val);
}

public void addAtIndex(int index, int val) {
if(index > size) return;
ListNode prev = getPrev(index);
ListNode temp = prev.next;
prev.next = new ListNode(val);
prev.next.next = temp;
size++;
}

public void deleteAtIndex(int index) {
if(index >= size) return;
if(index == size-1){
ListNode prev = getPrev(index);
prev.next = null;
size--;
return;
}
ListNode prev = getPrev(index);
prev.next = prev.next.next;
size--;
}

private void print(){
ListNode root = head.next;
while(root != null){
System.out.print(root.val+" -> ");
root = root.next;
}
System.out.println("Size: "+size);
}

class ListNode{
int val;
ListNode next;
public ListNode(int v){
val = v;
}
}
}

/**
* Your MyLinkedList object will be instantiated and called as such:
* MyLinkedList obj = new MyLinkedList();
* int param_1 = obj.get(index);
* obj.addAtHead(val);
* obj.addAtTail(val);
* obj.addAtIndex(index,val);
* obj.deleteAtIndex(index);
*/

1202. Smallest String With Swaps

You are given a string s, and an array of pairs of indices in the string pairs where pairs[i] = [a, b] indicates 2 indices(0-indexed) of the string.

You can swap the characters at any pair of indices in the given pairs any number of times.

Return the lexicographically smallest string that s can be changed to after using the swaps.

并查集,注意要使用路径压缩否则会超时。
先将所有配对联通。然后创建一个数组group记录每个字符串位置的分组情况。
根据分组,借助优先队列保存字符串。
通过哈希表创建group和PriorityQueue的映射。
按当前位置的分组情况和优先队列的顺序来添加字符串。
最后返回字符串。

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
class Solution {
public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) {
StringBuffer sb = new StringBuffer();
UnionFind uf = new UnionFind(s.length());

for(List<Integer> pair : pairs){
uf.union(pair.get(0), pair.get(1));
}

int[] group = new int[s.length()];
for(int i = 0; i < s.length(); i++){
group[i] = uf.find(i);
}

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

for(int i = 0; i < s.length(); i++){
PriorityQueue<Character> bin = map.getOrDefault(group[i], new PriorityQueue<Character>());
bin.add(s.charAt(i));
map.put(group[i], bin);
}
for(int i = 0; i < s.length(); i++){
sb.append(map.get(group[i]).poll());
}
return sb.toString();
}

class UnionFind {
int[] parent;
int[] count;
public UnionFind(int n){
parent = new int[n];
count = new int[n];
for(int i = 0; i < n; i++){
parent[i] = i;
count[i] = 1;
}
}

private int find(int id){
if(parent[id] == id) return id;
parent[id] = find(parent[id]);
return parent[id];
}

private boolean union(int id1, int id2){
int p1 = find(id1);
int p2 = find(id2);
if(p1 == p2) return false;
if(count[p1] > count[p2]) parent[p2] = p1;
else parent[p1] = p2;
return true;
}
}
}

55. Jump Game

You are given an integer array nums. You are initially positioned at the array’s first index, and each element in the array represents your maximum jump length at that position.

Return true if you can reach the last index, or false otherwise.

反向查找,动态规划。
到达目标点前必须到达上一个节点,且上一个节点能前进的步数必须大于目标点。
设置目标点goal,反向遍历goal以外的点。
如果该点能前进的步数加上自身的位置i大于目标点,则可以从上一个点到达goal。
更新goal的位置到上一个点。
如果遍历结束后,能返回到起始点0,则可以到达终点。

1
2
3
4
5
6
7
8
9
class Solution {
public boolean canJump(int[] nums) {
int goal = nums.length-1;
for(int i = nums.length-2; i >=0; i--){
if(nums[i] >= goal - i) goal = i;
}
return goal == 0;
}
}

贪心算法,储存一个到i时可以到达的最远值maxJump。
遍历数组,如果i大于maxJump,则无法到达下一个点,返回false。
在当前点可以到达的最大范围为nums[i]+i,如果大于maxJump则更新该值。
遍历完毕返回true。

1
2
3
4
5
6
7
8
9
10
class Solution {
public boolean canJump(int[] nums) {
int maxJump = 0;
for(int i = 0; i < nums.length; i++){
if(i > maxJump) return false;
maxJump = Math.max(maxJump, nums[i] + i);
}
return maxJump >= nums.length-1;
}
}

设置一个数组记录可访问的范围。
当遍历时,如果可以访问,则将当前位置可以进一步访问的位置变为1。
如果访问范围大于等于数组末尾,则返回真。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public boolean canJump(int[] nums) {
int[] reach = new int[nums.length];
reach[0] = 1;

for(int i = 0; i < nums.length; i++){
if(reach[i] == 1){
if(i == nums.length-1) return true;
for(int j = 0; j < nums[i]; j++){
if(i+j+1 >= nums.length) return true;
reach[i+j+1] = 1;
}
}
}
return false;
}
}

213. House Robber II

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system 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.

动态规划,和普通的动态规划不同的是这道题是首尾相连的。
因此分两种情况讨论——如果选择了头,就不选择尾。反之亦然。
建立两个动态规划表,分别选择第一个和第二个数字作为第一次抢劫的目标。
前一个最多抢劫到倒数第一个房子,后一个最多抢劫到最后一个房子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int rob(int[] nums) {
if(nums.length == 1) return nums[0];

int max = 0;
int[] dp = new int[nums.length+1];
int[] dp2 = new int[nums.length+1];
dp[1] = nums[0];

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

dp2[2] = nums[1];

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

return Math.max(dp[dp.length-2], dp2[dp2.length-1]);
}
}

24. Swap Nodes in Pairs

Given a linked list, swap every two adjacent nodes and return its head. You must solve the problem without modifying the values in the list’s nodes (i.e., only nodes themselves may be changed.)

先建立一个哨兵节点,next指向head。
交换时同时传入前一个节点pre和当前节点root。
当当前节点非null且下一个节点非null,则可以交换两个节点。
首先保存当前节点的下一个节点next和下一个节点的下一个节点last。

  • 1.将pre指向next。
  • 2.将next指向root。
  • 3.将root指向last。
  • 4.处理下一组节点,root作为pre,root.next作为root递归。
    最后返回哨兵节点的next。
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
/**
* 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 swapPairs(ListNode head) {
ListNode dummy = new ListNode(0, head);
swap(dummy, head);
return dummy.next;
}

private void swap(ListNode pre, ListNode root){
if(root == null || root.next == null){
return;
}
else if(root != null && root.next != null){

ListNode last = root.next.next;
ListNode next = root.next;
pre.next = next;
next.next = root;
root.next = last;
}
swap(root, root.next);
}
}

1584. Min Cost to Connect All Points

You are given an array points representing integer coordinates of some points on a 2D-plane, where points[i] = [xi, yi].

The cost of connecting two points [xi, yi] and [xj, yj] is the manhattan distance between them: |xi - xj| + |yi - yj|, where |val| denotes the absolute value of val.

Return the minimum cost to make all points connected. All points are connected if there is exactly one simple path between any two points.

并查集,最小生成树(Minimum spanning tree),Kruskal算法。
Edge辅助类,保存并计算两点的id和其曼哈顿距离。
将edges根据其距离排序。

遍历所有的edge,如果edge的两点没有合并,则合并两点,并取这条边作为结果。
加和所有的结果,答案就是最小值。

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
66
67
68
class Solution {
public int minCostConnectPoints(int[][] points) {
List<Edge> edges = new ArrayList<Edge>();
int min = 0;
for(int i = 0; i < points.length; i++){
for(int j = i+1; j < points.length; j++){
edges.add(new Edge(points, i, j));
}
}

Collections.sort(edges, (a, b) -> a.length - b.length);
UnionFind uf = new UnionFind(points.length);

for(Edge e : edges){
if(uf.union(e.id1, e.id2)){
min += e.length;
}
}
return min;
}

class UnionFind{
private int[] parents;
private int[] size;

public UnionFind(int n){
size = new int[n];
parents = new int[n];
for(int i = 0; i < n; i++){
parents[i] = i;
size[i] = 1;
}
}

public int find(int id){
if(parents[id] == id) return id;
return find(parents[id]);
}

public boolean union(int id1, int id2){
int p1 = find(id1);
int p2 = find(id2);

if(p1 == p2) return false;

if(size[p1] < size[p2]){
parents[p1] = p2;
size[p2] += size[p1];
}
else{
parents[p2] = p1;
size[p1] += size[p2];
}
return true;
}
}

class Edge{
int id1;
int id2;
int length;
public Edge(int[][] points, int _id1, int _id2){
id1 = _id1;
id2 = _id2;
length = Math.abs(points[id1][0] - points[id2][0]) + Math.abs(points[id1][1] - points[id2][1]);
}
}
}

142. Linked List Cycle II

Given the head of a linked list, return the node where the cycle begins. If there is no cycle, return null.

There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. Internally, pos is used to denote the index of the node that tail’s next pointer is connected to (0-indexed). It is -1 if there is no cycle. Note that pos is not passed as a parameter.

Do not modify the linked list.

快慢指针。快指针的移动速度是慢指针的两倍。
设环外长度为a,b是快指针和慢指针相遇的位置,c是环中剩余位置。
可以由此得到公式a + (n + 1)b + nc = 2(a + b),也就是a = c + (n - 1)(b + c)
由于(b + c)是环的长度。因此,当两个指针相遇时,在头部设置一个新节点。慢指针和新指针将在循环入口处相遇,此时返回节点。

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
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
if( head == null || head.next == null) return null;
ListNode slow = head;
ListNode fast = head;

while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow.equals(fast)) break;
}

if(fast == null || fast.next == null){
return null;
}

ListNode root = head;
while(!root.equals(slow)){
slow = slow.next;
root = root.next;
}
return root;
}
}

哈希表,递归并将节点加入哈希集合,如果重复则返回节点,反之返回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
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
HashSet<ListNode> set;
public ListNode detectCycle(ListNode head) {
set = new HashSet<>();
return findCycle(head, 0);
}

private ListNode findCycle(ListNode root, int count){
if(root == null){
return null;
}
if(set.contains(root)){
return root;
}
set.add(root);
count++;
return findCycle(root.next, count);
}
}