0%

代码解析

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
package com.demo.s38;
/**
* 外观数列
* 给定一个正整数 n ,输出外观数列的第 n 项。
*
* 「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
*
* 你可以将其视作是由递归公式定义的数字字符串序列:
*
* countAndSay(1) = "1"
* countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/count-and-say
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public String countAndSay(int n) {
if (n == 1) return "1";
else {
String lastStr = countAndSay(n - 1); // 1 2 1 1
StringBuilder ans = new StringBuilder();
int i = 0, j = 1, len = lastStr.length();
while (j < len) {
if (lastStr.charAt(i) != lastStr.charAt(j)) {
ans.append(j - i).append(lastStr.charAt(i));
i = j;
}
j++;
}
ans.append(j - i).append(lastStr.charAt(i));
return ans.toString();
}

}
}

代码解析

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
package com.demo.s37;

import java.util.ArrayList;
import java.util.List;

/**
* 解数独
* 编写一个程序,通过填充空格来解决数独问题。
*
* 数独的解法需 遵循如下规则:
*
* 数字 1-9 在每一行只能出现一次。
* 数字 1-9 在每一列只能出现一次。
* 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
* 数独部分空格内已填入了数字,空白格用 '.' 表示。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/sudoku-solver
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
private boolean[][] line = new boolean[9][9];
private boolean[][] column = new boolean[9][9];
private boolean[][][] block = new boolean[3][3][9];
private boolean valid = false;
private List<int[]> spaces = new ArrayList<int[]>();

public void solveSudoku(char[][] board) {
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 9; ++j) {
if (board[i][j] == '.') {
spaces.add(new int[]{i, j});
} else {
int digit = board[i][j] - '0' - 1;
line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = true;
}
}
}

dfs(board, 0);
}

public void dfs(char[][] board, int pos) {
if (pos == spaces.size()) {
valid = true;
return;
}

int[] space = spaces.get(pos);
int i = space[0], j = space[1];
for (int digit = 0; digit < 9 && !valid; ++digit) {
if (!line[i][digit] && !column[j][digit] && !block[i / 3][j / 3][digit]) {
line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = true;
board[i][j] = (char) (digit + '0' + 1);
dfs(board, pos + 1);
line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = false;
}
}
}

}

代码解析

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
package com.demo.s36;

import java.util.HashMap;

/**
* 有效的数独
* 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
*
* 数字 1-9 在每一行只能出现一次。
* 数字 1-9 在每一列只能出现一次。
* 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
*  
*
* 注意:
*
* 一个有效的数独(部分已被填充)不一定是可解的。
* 只需要根据以上规则,验证已经填入的数字是否有效即可。
* 空白格用 '.' 表示。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/valid-sudoku
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public boolean isValidSudoku(char[][] board) {
// init data
HashMap<Integer, Integer> [] rows = new HashMap[9];
HashMap<Integer, Integer> [] columns = new HashMap[9];
HashMap<Integer, Integer> [] boxes = new HashMap[9];
for (int i = 0; i < 9; i++) {
rows[i] = new HashMap<Integer, Integer>();
columns[i] = new HashMap<Integer, Integer>();
boxes[i] = new HashMap<Integer, Integer>();
}

// validate a board
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char num = board[i][j];
if (num != '.') {
int n = (int)num;
int box_index = (i / 3 ) * 3 + j / 3;

// keep the current cell value
rows[i].put(n, rows[i].getOrDefault(n, 0) + 1);
columns[j].put(n, columns[j].getOrDefault(n, 0) + 1);
boxes[box_index].put(n, boxes[box_index].getOrDefault(n, 0) + 1);

// check if this value has been already seen before
if (rows[i].get(n) > 1 || columns[j].get(n) > 1 || boxes[box_index].get(n) > 1)
return false;
}
}
}

return true;

}
}

代码解析

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
package com.demo.s35;

/**
* 搜索插入位置
* 给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
*
* 请必须使用时间复杂度为 O(log n) 的算法。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/search-insert-position
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {

public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1, ans = n;
while (left <= right) {
int mid = ((right - left) >> 1) + left;
if (target <= nums[mid]) {
ans = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return ans;


}
}

代码解析

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
package com.demo.s34;

/**
* 在排序数组中查找元素的第一个和最后一个位置
* 给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
*
* 如果数组中不存在目标值 target,返回 [-1, -1]。
*
* 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
*
*
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums.length == 0) return new int[]{-1,-1};
int left = 0;
int right = nums.length - 1;
int[] res = {-1,-1};
int mid;
while(left < right && left <= nums.length-1 && 0 <= right) {
mid = left + (right - left)/2;
if(nums[mid] > target) {
right = mid - 1;
}else if (nums[mid] < target) {
left = mid + 1;
}else {
right = mid;
}
}
if(left <= nums.length-1 && nums[left] == target){
res[0] = left;
}
left = 0;
right = nums.length - 1;
while(left < right && left <= nums.length-1 && 0 <= right) {
mid = left + (right - left)/2 + 1;
if(nums[mid] > target) {
right = mid - 1;
}else if (nums[mid] < target) {
left = mid + 1;
}else {
left = mid;
}
}
if(left <= nums.length-1 && nums[left] == target){
res[1] = left;
}
return res;
}
}

代码解析

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
package com.demo.s33;

/**
* 搜索旋转排序数组
* 整数数组 nums 按升序排列,数组中的值 互不相同 。
*
* 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
*
* 给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
*
* 你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/search-in-rotated-sorted-array
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public int search(int[] nums, int target) {
//数组大小判断
if(nums.length == 0) {
return -1;
}
//只有一个值情况判断
if(nums.length == 1) {
if(nums[0] == target) {
return 0;
}
return -1;
}

int i = 0;
int j = nums.length - 1;
int mid = 0;
//二分法 左右边界和中间值
while(i <= j) {
mid = (i + j) /2;
//如果中间值 就是target 则返回mid
if(nums[mid] == target) {
return mid;
}
//如果0 ~mid 为上升,则mid在左升区间
if(nums[0] <= nums[mid]) {
//如果 target 在i ~ mid之间
if(nums[i] <= target && target < nums[mid]) {
j = mid - 1;
} else {
// target 在mid ~ j之间
i = mid + 1;
}
//如果0 ~mid 为下降,则mid在右降区间
} else {
//如果 target 在i ~ mid之间
if(nums[mid] < target && target <= nums[nums.length - 1]) {
i = mid + 1;
} else {
j = mid -1 ;
}
}
}
return -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
package com.demo.s322;

/**
* 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
*
* 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
*
* 你可以认为每种硬币的数量是无限的。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/coin-change
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public int coinChange(int[] coins, int amount) {
if (amount < 1) {
return 0;
}
return coinChange(coins, amount, new int[amount]);
}

int coinChange(int[] coins, int rem, int[] count) {
if(rem < 0) {
return -1;
}

if(rem ==0) {
return 0;
}

if (count[rem - 1] != 0) {
return count[rem - 1];
}
int min = Integer.MAX_VALUE;
for (int coin : coins) {
int res = coinChange(coins, rem - coin, count);
if (res >= 0 && res < min) {
min = 1 + res;
}
}
count[rem - 1] = (min == Integer.MAX_VALUE) ? -1 : min;
return count[rem - 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
package com.demo.s32;

import java.util.Deque;
import java.util.LinkedList;

/**
* 最长有效括号
* 给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
*/
public class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
//新建一个栈 用来从左到右存括号字符串
Deque<Integer> stack = new LinkedList<Integer>();
//压一个初始长度-1
stack.push(-1);
for (int i = 0; i < s.length(); i++) {
//如果是左括号 就压栈
if (s.charAt(i) == '(') {
stack.push(i);
} else {
//右括号就弹出
stack.pop();
//弹出后如果空了就压当前i的值,表示有效子串开始的位置
if (stack.isEmpty()) {
stack.push(i);
} else {
//不为空就比较下 有效子串是否为最长 ,更新maxans
maxans = Math.max(maxans, i - stack.peek());
}
}
}
return maxans;

}
}

代码解析

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
package com.demo.s31;

/**
* 下一个排列
* 整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。
*
* 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
* 整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
*
* 例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
* 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
* 而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
* 给你一个整数数组 nums ,找出 nums 的下一个排列。
*
* 必须 原地 修改,只允许使用额外常数空间。
*
*
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/next-permutation
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public void nextPermutation(int[] nums) {

int i = nums.length - 2;
//从右到左找出开始递增的位置 1612354 中 3 就是
while (i >= 0 && nums[i] >= nums[i + 1]) {
i--;
}
//如果找到了,再从右到左找出第一个大于nums[i]的值 1612354 中 4 就是
if (i >= 0) {
int j = nums.length - 1;
while (j >= 0 && nums[i] >= nums[j]) {
j--;
}
//交换两个数字位置 1612453
swap(nums, i, j);
}
//翻转 i + 1 到 结束 也就是 53 变成 35
reverse(nums, i + 1);
}

public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}

public void reverse(int[] nums, int start) {
int left = start, right = nums.length - 1;
while (left < right) {
swap(nums, left, right);
left++;
right--;
}
}

}

代码解析

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
package com.demo.s300;


/**
*给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
*
* 子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
*
*  
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/longest-increasing-subsequence
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public int lengthOfLIS(int[] nums) {
if(nums.length == 0) {
return 0;
}
int size = 1;
//严格递增子序列数组
int[] sub = new int[nums.length+1];
//初始化第0个节点
sub[size] = nums[0];
int pos = 0;
//遍历数组nums
for(int i= 1; i< nums.length; i++) {
//前一个小于后一个节点时,记录到sub数组
if(sub[size] < nums[i]) {
sub[++size] = nums[i];
} else {
//重置sub数组,分左右两个指针判断
int l = 1;
int r = size;
//如果sub中找不到比nums[i]小的元素则从0开始替换
pos = 0;
//移动两边指针
while(l <= r) {
//取中间点
int mid = (l + r) >> 1;
//因为sub已经有序,所以二分法,找到满足升序的中间点pos,更新sub数组
if(sub[mid] < nums[i]) {
l = mid + 1;
pos = mid;
} else {
r = mid - 1;
}
}
//更新sub数组
sub[pos + 1] = nums[i];
}
}
return size;
}

}