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

/**
* 给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/reverse-linked-list-ii
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
//哑结点,方便返回结果
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
//记录翻转链表区间的前置节点
ListNode pre = dummyNode;
//指针移动到left-1位置,记为pre
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
// left位置节点
ListNode start = pre.next;
// cur节点
ListNode cur = pre.next;
//记录翻转链表区间的前置节点
ListNode p = null;
// 从left 到 right 范围链表翻转
for(int i = left; i <= right; i++) {
ListNode tmp = cur.next;
//当前节点指向前置节点
cur.next = p;
p = cur;
//翻转链表区间的前置节点 指向当前的节点,随着当前节点后移
pre.next=cur;
//遍历时移动到下一个节点
cur = tmp;
}
//当前节点不为空时,也就是没有遍历到最后一个节点
if(cur != null) {
//原先left位置节点指向下一个节点
start.next = cur;
}

return dummyNode.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
33
34
35
36
37
38
39
40
41
42
43
package com.demo.s91;

/**
* 解码方法
* 一条包含字母 A-Z 的消息通过以下映射进行了 编码 :
*
* 'A' -> "1"
* 'B' -> "2"
* ...
* 'Z' -> "26"
* 要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,"11106" 可以映射为:
*
* "AAJF" ,将消息分组为 (1 1 10 6)
* "KJF" ,将消息分组为 (11 10 6)
* 注意,消息不能分组为  (1 11 06) ,因为 "06" 不能映射为 "F" ,这是由于 "6" 和 "06" 在映射中并不等价。
*
* 给你一个只含数字的 非空 字符串 s ,请计算并返回 解码 方法的 总数 。
*
* 题目数据保证答案肯定是一个 32 位 的整数。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/decode-ways
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public int numDecodings(String s) {
int n = s.length();
// a = f[i-2], b = f[i-1], c=f[i]
int a = 0, b = 1, c = 0;
for (int i = 1; i <= n; ++i) {
c = 0;
if (s.charAt(i - 1) != '0') {
c += b;
}
if (i > 1 && s.charAt(i - 2) != '0' && ((s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0') <= 26)) {
c += a;
}
a = b;
b = c;
}
return 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
36
37
38
39
40
41
42
package com.demo.s90;

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

/**
* 子集 II
* 给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
*
* 解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/subsets-ii
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
List<Integer> t = new ArrayList<Integer>();
List<List<Integer>> ans = new ArrayList<List<Integer>>();

public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
int n = nums.length;
for (int mask = 0; mask < (1 << n); ++mask) {
t.clear();
boolean flag = true;
for (int i = 0; i < n; ++i) {
if ((mask & (1 << i)) != 0) {
if (i > 0 && (mask >> (i - 1) & 1) == 0 && nums[i] == nums[i - 1]) {
flag = false;
break;
}
t.add(nums[i]);
}
}
if (flag) {
ans.add(new ArrayList<Integer>(t));
}
}
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
package com.demo.s9;

/**
* 回文数
* 给你一个整数 x ,如果 x 是一个回文整数,返回 true ;否则,返回 false 。
*
* 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
*
* 例如,121 是回文,而 123 不是。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/palindrome-number
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public boolean isPalindrome(int x) {
// 特殊情况:
// 如上所述,当 x < 0 时,x 不是回文数。
// 同样地,如果数字的最后一位是 0,为了使该数字为回文,
// 则其第一位数字也应该是 0
// 只有 0 满足这一属性
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}

int revertedNumber = 0;
while (x > revertedNumber) {
revertedNumber = revertedNumber * 10 + x % 10;
x /= 10;
}

// 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。
// 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123,
// 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。
return x == revertedNumber || x == revertedNumber / 10;
}
}

代码解析

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.s89;

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

/**
* 格雷编码
* n 位格雷码序列 是一个由 2n 个整数组成的序列,其中:
* 每个整数都在范围 [0, 2n - 1] 内(含 0 和 2n - 1)
* 第一个整数是 0
* 一个整数在序列中出现 不超过一次
* 每对 相邻 整数的二进制表示 恰好一位不同 ,且
* 第一个 和 最后一个 整数的二进制表示 恰好一位不同
* 给你一个整数 n ,返回任一有效的 n 位格雷码序列 。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/gray-code
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public List<Integer> grayCode(int n) {
List<Integer> res = new ArrayList<Integer>() {{ add(0); }};
int head = 1;
for (int i = 0; i < n; i++) {
for (int j = res.size() - 1; j >= 0; j--)
res.add(head + res.get(j));
head <<= 1;
}
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
package com.demo.s88;

/**
* 合并两个有序数组
* 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。
*
* 请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。
*
* 注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
*
*
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/merge-sorted-array
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
//定义新数组
int[] all = new int[m + n];
int i = 0;
int j = 0;
while(i< m || j < n) {
int cur;
//num1遍历完了,遍历num2
if(i == m) {
cur = nums2[j++];
//num2遍历完了,遍历num1
} else if(j == n) {
cur = nums1[i++];
//小的元素先排上
} else if(nums1[i] <= nums2[j]) {
cur = nums1[i++];
} else {
cur = nums2[j++];
}
all[i+j-1] = cur;
}
//新数组元素再放到num1中
for(int t = 0; t< n + m; t++) {
nums1[t] = all[t];
}
}
}

代码解析

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
80
81
82
83
84
85
86
87
88
package com.demo.s87;

import java.util.HashMap;
import java.util.Map;

/**
* 扰乱字符串
* 使用下面描述的算法可以扰乱字符串 s 得到字符串 t :
* 如果字符串的长度为 1 ,算法停止
* 如果字符串的长度 > 1 ,执行下述步骤:
* 在一个随机下标处将字符串分割成两个非空的子字符串。即,如果已知字符串 s ,则可以将其分成两个子字符串 x 和 y ,且满足 s = x + y 。
* 随机 决定是要「交换两个子字符串」还是要「保持这两个子字符串的顺序不变」。即,在执行这一步骤之后,s 可能是 s = x + y 或者 s = y + x 。
* 在 x 和 y 这两个子字符串上继续从步骤 1 开始递归执行此算法。
* 给你两个 长度相等 的字符串 s1 和 s2,判断 s2 是否是 s1 的扰乱字符串。如果是,返回 true ;否则,返回 false 。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/scramble-string
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
public class Solution {
// 记忆化搜索存储状态的数组
// -1 表示 false,1 表示 true,0 表示未计算
int[][][] memo;
String s1, s2;

public boolean isScramble(String s1, String s2) {
int length = s1.length();
this.memo = new int[length][length][length + 1];
this.s1 = s1;
this.s2 = s2;
return dfs(0, 0, length);
}

// 第一个字符串从 i1 开始,第二个字符串从 i2 开始,子串的长度为 length,是否和谐
public boolean dfs(int i1, int i2, int length) {
if (memo[i1][i2][length] != 0) {
return memo[i1][i2][length] == 1;
}

// 判断两个子串是否相等
if (s1.substring(i1, i1 + length).equals(s2.substring(i2, i2 + length))) {
memo[i1][i2][length] = 1;
return true;
}

// 判断是否存在字符 c 在两个子串中出现的次数不同
if (!checkIfSimilar(i1, i2, length)) {
memo[i1][i2][length] = -1;
return false;
}

// 枚举分割位置
for (int i = 1; i < length; ++i) {
// 不交换的情况
if (dfs(i1, i2, i) && dfs(i1 + i, i2 + i, length - i)) {
memo[i1][i2][length] = 1;
return true;
}
// 交换的情况
if (dfs(i1, i2 + length - i, i) && dfs(i1 + i, i2, length - i)) {
memo[i1][i2][length] = 1;
return true;
}
}

memo[i1][i2][length] = -1;
return false;
}

public boolean checkIfSimilar(int i1, int i2, int length) {
Map<Character, Integer> freq = new HashMap<Character, Integer>();
for (int i = i1; i < i1 + length; ++i) {
char c = s1.charAt(i);
freq.put(c, freq.getOrDefault(c, 0) + 1);
}
for (int i = i2; i < i2 + length; ++i) {
char c = s2.charAt(i);
freq.put(c, freq.getOrDefault(c, 0) - 1);
}
for (Map.Entry<Character, Integer> entry : freq.entrySet()) {
int value = entry.getValue();
if (value != 0) {
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.demo.s86;

/**
* 分隔链表
* 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
*
* 你应当 保留 两个分区中每个节点的初始相对位置。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode.cn/problems/partition-list
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*/
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public class Solution {

public ListNode partition(ListNode head, int x) {
ListNode lessHead = new ListNode(0);
ListNode moreHead = new ListNode(0);

if(head == null) return null;
ListNode cur = head;
ListNode curLess = lessHead;
ListNode curMore = moreHead;

while(cur != null)
{
if(cur.val < x)
{
curLess.next = new ListNode(cur.val);
curLess = curLess.next;
cur = cur.next;
}
else
{
curMore.next = new ListNode(cur.val);
curMore = curMore.next;
cur = cur.next;
}
}
curLess.next = moreHead.next;
return lessHead.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
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.s85;

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

/**
* 最大矩形
* 给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
*/
public class Solution {
public int maximalRectangle(char[][] matrix) {
int m = matrix.length;
if (m == 0) {
return 0;
}
int n = matrix[0].length;
int[][] left = new int[m][n];

for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == '1') {
left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1;
}
}
}

int ret = 0;
for (int j = 0; j < n; j++) { // 对于每一列,使用基于柱状图的方法
int[] up = new int[m];
int[] down = new int[m];

Deque<Integer> stack = new LinkedList<Integer>();
for (int i = 0; i < m; i++) {
while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) {
stack.pop();
}
up[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
for (int i = m - 1; i >= 0; i--) {
while (!stack.isEmpty() && left[stack.peek()][j] >= left[i][j]) {
stack.pop();
}
down[i] = stack.isEmpty() ? m : stack.peek();
stack.push(i);
}

for (int i = 0; i < m; i++) {
int height = down[i] - up[i] - 1;
int area = height * left[i][j];
ret = Math.max(ret, area);
}
}
return ret;
}
}

代码解析

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

import java.util.Stack;

/**
* 柱状图中最大的矩形
* 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
*
* 求在该柱状图中,能够勾勒出来的矩形的最大面积。
*/
public class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int[] left = new int[n];
int[] right = new int[n];

Stack<Integer> mono_stack = new Stack<Integer>();
for (int i = 0; i < n; ++i) {
while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
mono_stack.pop();
}
left[i] = (mono_stack.isEmpty() ? -1 : mono_stack.peek());
mono_stack.push(i);
}

mono_stack.clear();
for (int i = n - 1; i >= 0; --i) {
while (!mono_stack.isEmpty() && heights[mono_stack.peek()] >= heights[i]) {
mono_stack.pop();
}
right[i] = (mono_stack.isEmpty() ? n : mono_stack.peek());
mono_stack.push(i);
}

int ans = 0;
for (int i = 0; i < n; ++i) {
ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;

}
}
0%