518 零钱兑换2(Medium)

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。

题目数据保证结果符合 32 位带符号整数。

示例 1:

输入:amount = 5, coins = [1, 2, 5]

输出:4

解释:有四种方式可以凑成总金额:

5=5

5=2+2+1

5=2+1+1+1

5=1+1+1+1+1

很愚蠢的暴力解:DFS(8/28)

最搞笑的是我点了一个运行,然后吃完饭都没有算出结果来,我愿称之为最暴力的一集

本意是深度优先搜索,然后将路径上的值保存进哈希表,哈希表记录的是硬币和个数,如果最后能全部找完,就把当前哈希表存到set里面(其实让我意想不到的是这样子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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//hashmap用来放具体找零
private HashMap<Integer,Integer> hashMap = new HashMap<>();
//set去重
private Set<HashMap<Integer,Integer>> set = new HashSet<>();
public int change1(int amount, int[] coins) {
Arrays.sort(coins);
dfs(amount,coins);
return set.size();
}
public void dfs(int amount,int[] coins){
//可以找完,放进set里面
if (amount == 0){
set.add(new HashMap<>(hashMap));
return;
}
//不能用已有的找零了,失败
if (amount < coins[0]){
return;
}
//递归遍历每一种情况
for (int i = 0; i < coins.length; i++) {
//先加入hashmap
if (!hashMap.containsKey(coins[i])){
hashMap.put(coins[i],1);
}
else {
Integer integer = hashMap.get(coins[i]);
hashMap.replace(coins[i],integer+1);
}

dfs(amount-coins[i],coins);
//后续递归再删除回到上一层,保证每个循环都是一样的
int temp = hashMap.get(coins[i]);
if (temp == 1){
hashMap.remove(coins[i]);
}
else {
hashMap.put(coins[i],temp-1);
}
}
}

动态规划(其实就是跳房子游戏)

问题一:我对这道题的主要纠结点在于,比如11,硬币有1,2,5,理应找dp[10],dp[9],dp[6],但是我们不知道这里面是不是可能造成重复,事实上dp[9]里面肯定也会只使用一个5,对于dp[6]的情况来说只是改了顺序

如何解决?我们不再遍历11而遍历硬币,这样就能保证在遍历到当前硬币之前不会有这个路径出现。每一次遍历都是如何通过当前的这个硬币换到现在的钱,
自然而然得到转移方程,dp[j] += dp[j-coins[i]];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Solution {
public int change(int amount, int[] coins) {
int[] dp = new int[amount+1];
dp[0] = 1;
for (int i = 0; i < coins.length; i++) {
for (int j = coins[i]; j < dp.length; j++) {
dp[j] += dp[j-coins[i]];
}

}

return dp[dp.length-1];
}
}

看完后其实是有恍然大雾的感觉,多看多学。

合并K个有序链表(Hard)

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

实在不明白这个题怎么会是hard,给我刷战绩的题

其实还不够快,如果用分治法可以logn

队列

两两合并然后入队,直到队里面只有一个,就是答案

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
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
//判断是否为空
if (lists.length == 0){
return null;
}
Queue<ListNode> queue = new ArrayDeque<>();
for (int i = 0; i < lists.length; i++) {
if (lists[i] != null){
queue.add(lists[i]);
}
}
//这里也被卡了一下,如果长度是0就不能进行下面的步骤
if (queue.isEmpty()){
return null;
}
while (queue.size() != 1){
ListNode listNode1 = queue.poll();
ListNode listNode2 = queue.poll();
if (listNode1 != null && listNode2 != null){
queue.add(merge(listNode1,listNode2));
}
}
return queue.poll();
}

/**
* 两个链表合并,基本方法
* @param listNode1
* @param listNode2
* @return
*/
public ListNode merge(ListNode listNode1,ListNode listNode2){
//用了一个头节点方便保存
ListNode temp = new ListNode();
ListNode ans = temp;
while (listNode1!=null && listNode2!=null){
if (listNode1.val <= listNode2.val){
temp.next = listNode1;
listNode1 = listNode1.next;
}
else {
temp.next = listNode2;
listNode2 = listNode2.next;
}
temp = temp.next;
}
//接上没遍历到的
if (listNode1 != null){
temp.next = listNode1;
}
if (listNode2 != null){
temp.next = listNode2;
}
return ans.next;
}

}

搜索二维矩阵2(Medium)

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。

每列的元素从上到下升序排列。

二维双指针

我们可以观察到,从下往上数每一行第一个元素,如果比目标大就可以删除了,同理每一列从后往前数第一个元素,如果大也可以删除了。
这样就得到了一个新矩阵。

接着找新矩阵最后一行的元素,从前往后数如果比目标小也可以删除了,然后最后一列,从上到下比目标小的也可以删除。

这样完成一次循环,减少了很大的搜索范围。一直收敛到只有一行或者一列。

这里用了小技巧,对于找不到的元素肯定是会数组下标抛异常的,catch到了肯定是没有的,返回假

还有有一个小问题,当我们面对[[5,6,9],[9,10,11],[11,14,18]]这样的数组,有两个9,这时候我们就永远满足不了循环条件出不来了,为了解决需要看循环里面四个指针是否变化,如果没变就说明发生了这种情况。直接跳出循环。而出现这种情况的原因就是因为有两个一样的目标值,那肯定返回真。

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
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int row_top = 0,row_bottom = matrix.length-1,col_left = 0,col_right = matrix[0].length-1;
boolean flag = false;

try {
while (row_top < row_bottom && col_left < col_right){
//判断是否指针有变化
int temp = 0;
while (matrix[row_bottom][col_left] > target){
temp++;
row_bottom--;
}
while (matrix[row_top][col_right] > target){
temp++;
col_right--;
}
while (matrix[row_bottom][col_left] < target){
temp++;
col_left++;
}
while (matrix[row_top][col_right] <target){
temp++;
row_top++;
}
if (temp == 0){
flag = true;
break;
}

}
//System.out.println(row_top+" "+row_bottom+" "+col_left+" "+col_right);
if (row_top == row_bottom){
for (int i = col_left; i <=col_right; i++) {
if (matrix[row_bottom][i] == target){
flag = true;
break;
}
}
}
if (col_left == col_right){
for (int i = row_top; i <= row_bottom; i++) {
if (matrix[i][col_right] == target){
flag = true;
break;
}
}
}
return flag;
}
//小技巧
catch (ArrayIndexOutOfBoundsException e){
return false;
}
}
}

这道题做出来其实有点侥幸,很多情况都是错了以后试验出来的。据他们所说这样还不如遍历整体找结果。

54 螺旋矩阵(Medium)

给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。

非常好理解的一道题,根据上一题的思路很快就能写出,但是这里不用频繁维护四个指针,只用在循环末尾使用就可以。

就跟洋葱一样,每次都剥掉最外一层

双指针

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
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> ans = new ArrayList<>();
int row_top = 0,row_bottom = matrix.length-1,col_left = 0,col_right = matrix[0].length-1;
while (row_top < row_bottom && col_left < col_right){
for (int i = col_left; i < col_right; i++) {
ans.add(matrix[row_top][i]);
}
for (int i = row_top; i < row_bottom; i++) {
ans.add(matrix[i][col_right]);
}
for (int i = col_right; i > col_left ; i--) {
ans.add(matrix[row_bottom][i]);
}
for (int i = row_bottom; i > row_top; i--) {
ans.add(matrix[i][col_left]);
}
//换到内层
row_top++;
row_bottom--;
col_left++;
col_right--;
}
//特判。如果都相等,说明中间只有一个元素
if (row_top == row_bottom && col_left == col_right){
ans.add(matrix[row_top][col_right]);
}
else {
//一行
if (row_top == row_bottom){
for (int i = col_left; i <= col_right; i++) {
ans.add(matrix[row_top][i]);
}
}
//一列
if (col_left == col_right){
for (int i = row_top; i <= row_bottom; i++) {
ans.add(matrix[i][col_left]);
}
}
}

return ans;
}
}

59 螺旋矩阵2(Medium)

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

这个比上面的更简单了,一个计数器,然后根据上面的逻辑转圈,每次都+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
class Solution {
public int[][] generateMatrix(int n) {
int[][] matrix = new int[n][n];
int count = 1;
int row_top = 0,row_bottom = matrix.length-1,col_left = 0,col_right = matrix[0].length-1;
while (row_top < row_bottom && col_left < col_right){
for (int i = col_left; i < col_right; i++) {
matrix[row_top][i] = count++;
}
for (int i = row_top; i < row_bottom; i++) {
matrix[i][col_right]= count++;
}
for (int i = col_right; i > col_left ; i--) {
matrix[row_bottom][i]= count++;
}
for (int i = row_bottom; i > row_top; i--) {
matrix[i][col_left]= count++;
}
row_top++;
row_bottom--;
col_left++;
col_right--;
}
if (row_top == row_bottom && col_left == col_right){
matrix[row_top][col_right]= count++;
}
else {
if (row_top == row_bottom){
for (int i = col_left; i <= col_right; i++) {
matrix[row_top][i]= count++;
}
}
if (col_left == col_right){
for (int i = row_top; i <= row_bottom; i++) {
matrix[i][col_left]= count++;
}
}
}
return matrix;
}
}