Qi

Cogito ergo sum

代码解析

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


/**
*给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
*
* 你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
*
*  
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/add-strings
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public String addStrings(String num1, String num2) {
//字符串拼接 StringBuilder
StringBuilder builder = new StringBuilder();
int m = num1.length();
int n = num2.length();
//num1 遍历 下标
int i =1;
//num2 遍历 下标
int j =1;
int carray = 0;

while(i + j <= m + n + 1) {
int numa = 0;
//补0对齐位数
if(i > m) {
numa = 0;
} else {
//字符转数字 从右向左 取值
numa = num1.charAt(m - i++) - '0';
}
int numb = 0;
//补0对齐位数
if(j > n) {
numb = 0;
} else {
//字符转数字 从右向左 取值
numb = num2.charAt(n - j++) - '0';
}
//从右向左 逐位数字相加
int sum = numa + numb + carray;
//满10 进一位
carray = sum / 10;
builder.append(sum % 10);
}
//最后一次相加后 如果有进位 则拼接到字符串
if(carray != 0) {
builder.append(carray);
}
//翻转字符串
return builder.reverse().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
package com.demo.s41;

/**
* 缺失的第一个正数
* 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
*
* 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
*/
public class Solution {
public int firstMissingPositive(int[] nums) {

int n = nums.length;
//处理下负的值,把对应的值改成一个大于n的数字,因为找第一个正数,所以n+1 后面遍历会被忽略掉
for (int i = 0; i < n; ++i) {
if (nums[i] <= 0) {
nums[i] = n + 1;
}
}
// 把存在于 0 -n 之间的值 填到对应的下标处 也变成负值(表示未缺失),后面统计正的数就可以了
for (int i = 0; i < n; ++i) {
int num = Math.abs(nums[i]);
if (num <= n) {
nums[num - 1] = -Math.abs(nums[num - 1]);
}
}
//找到第一个>0 数字的下标就是 第一个缺失的正数了,因为上一步已经把存在的值标记到对应下标的位置了(负的值表示存在)
for (int i = 0; i < n; ++i) {
if (nums[i] > 0) {
return i + 1;
}
}
return n + 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
package com.demo.s40;

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

/**
* 组合总和 II
* 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
*
* candidates 中的每个数字在每个组合中只能使用 一次 。
*
* 注意:解集不能包含重复的组合。 
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/combination-sum-ii
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
List<int[]> freq = new ArrayList<int[]>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> sequence = new ArrayList<Integer>();


public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
for (int num : candidates) {
int size = freq.size();
if (freq.isEmpty() || num != freq.get(size - 1)[0]) {
freq.add(new int[]{num, 1});
} else {
++freq.get(size - 1)[1];
}
}
dfs(0, target);
return ans;
}

public void dfs(int pos, int rest) {
if (rest == 0) {
ans.add(new ArrayList<Integer>(sequence));
return;
}
if (pos == freq.size() || rest < freq.get(pos)[0]) {
return;
}

dfs(pos + 1, rest);

int most = Math.min(rest / freq.get(pos)[0], freq.get(pos)[1]);
for (int i = 1; i <= most; ++i) {
sequence.add(freq.get(pos)[0]);
dfs(pos + 1, rest - i * freq.get(pos)[0]);
}
for (int i = 1; i <= most; ++i) {
sequence.remove(sequence.size() - 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
package com.demo.s4;

/**
* 寻找两个正序数组的中位数
* 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
*
* 算法的时间复杂度应该为 O(log (m+n)) 。
*
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/median-of-two-sorted-arrays
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if(nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
}
int m = nums1.length;
int n = nums2.length;
int left = 0;
int right = m;
// i + j = m-i + n -j -1 j = m+n+1/2 -i m<n
int median1 = 0;
int median2 = 0;
while(left <= right) {
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
int mediana1 = (i == 0 ? Integer.MIN_VALUE : nums1[i-1]);
int mediana2 = (i == m ? Integer.MAX_VALUE : nums1[i]);
int medianb1 = (j == 0 ? Integer.MIN_VALUE : nums2[j-1]);
int medianb2 = (j == n ? Integer.MAX_VALUE : nums2[j]);
if(mediana1 <= medianb2) {
median1 = Math.max(mediana1, medianb1);
median2 = Math.min(mediana2, medianb2);
left = i + 1;
} else {
right = i - 1;
}
}
return ( m + n) %2==0? (median1 + median2) / 2.0 : median1;
}

}

代码解析

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

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

/**
* 组合总和
* 给你一个 无重复元素 的整数数组candidates 和一个目标整数target,找出candidates中可以使数字和为目标数target 的 所有不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
*
* candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
*
* 对于给定的输入,保证和为target 的不同组合数少于 150 个。
*
*
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/combination-sum
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {

List<List<Integer>> ans = new ArrayList<List<Integer>>();
List<Integer> combine = new ArrayList<Integer>();
dfs(candidates, target, ans, combine, 0);
return ans;
}

public void dfs(int[] candidates, int target, List<List<Integer>> ans, List<Integer> combine, int idx) {
if (idx == candidates.length) {
return;
}
if (target == 0) {
ans.add(new ArrayList<Integer>(combine));
return;
}
// 直接跳过
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
combine.add(candidates[idx]);
dfs(candidates, target - candidates[idx], ans, combine, idx);
combine.remove(combine.size() - 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
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;
}
}
0%