慢刷Leetcode算法题

2024-06-24

Ideas

  • mergeTrees方法:
    • 如果 root1root2null,直接返回另一棵树。
    • 否则,先将 root1root2 对应节点的值相加,并将结果存储在 root1 中。
    • 递归地合并左子树和右子树。
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 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 TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
// 如果root1为空,则直接返回root2,表示第一棵树已经被完全合并到第二棵树中
if (root1 == null) {
return root2;
}
// 如果root2为空,则直接返回root1,表示第二棵树已经被完全合并到第一棵树中
if (root2 == null) {
return root1;
}
// 创建一个新的节点,其值为root1和root2的值之和,然后递归合并左右子树
return new TreeNode( root1.val + root2.val ,
mergeTrees(root1.left,root2.left),
mergeTrees(root1.right,root2.right));

}
}

result

503. 下一个更大元素 II

Ideas

  1. 初始化
    • 创建一个 result 数组,初始值全部设为 -1,表示如果某个元素没有找到下一个更大元素,则默认值为 -1
    • 创建一个栈 stack 用于存储元素的索引。
  2. 遍历数组
    • 使用 for 循环遍历 2 * n 次,利用 i % n 模拟循环数组。
    • 每次从 nums 数组中取出当前元素 num
  3. 更新结果数组
    • 使用 while 循环,当栈不为空且当前元素 num 大于栈顶元素对应的元素时,更新结果数组中栈顶元素对应的下一个更大元素为当前元素,并弹出栈顶。
  4. 压入栈
    • 如果当前索引 i 小于数组长度 n,则将当前索引压入栈中。
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
class Solution {
public int[] nextGreaterElements(int[] nums) {

// 数组长度
int n = nums.length;
// 结果数组,初始化为-1,表示没有下一个更大元素
int[] result = new int[n];
Arrays.fill(result, -1);
// 使用栈来辅助寻找下一个更大元素
Stack<Integer> stack = new Stack<>();

// 遍历数组两次,以处理环形数组的特性
for (int i = 0; i < 2 * n; i++) {
// 当前遍历到的元素
int num = nums[i % n];
// 如果栈不为空且栈顶元素小于当前元素,则栈顶元素的下一个更大元素为当前元素
while (!stack.isEmpty() && nums[stack.peek()] < num) {
result[stack.pop()] = num;
}
// 如果当前遍历位置小于原数组长度,则将位置入栈,用于后续寻找下一个更大元素
if (i < n) {
stack.push(i);
}
}

// 返回结果数组
return result;
}
}

result

2024-06-23

456. 132 模式

Ideas

  1. 初始化一个空栈和一个 third 变量为 Integer.MIN_VALUE

  2. 从后往前遍历数组:

    • 如果 nums[i] < third,说明存在 132 模式的子序列,返回 true
    • 如果 nums[i] > stack.peek(),不断弹出栈顶元素并更新 third,直到栈顶元素不再小于当前元素。
    • 将当前元素压入栈。
  3. 如果遍历完数组后没有找到符合条件的子序列,返回 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
class Solution {
public boolean find132pattern(int[] nums) {
if (nums == null || nums.length < 3) {
return false;
}

Stack<Integer> stack = new Stack<>();
int third = Integer.MIN_VALUE;

// 从后往前遍历数组
for (int i = nums.length - 1; i >= 0; i--) {
if (nums[i] < third) {
return true;
}
// 维护单调递减栈
while (!stack.isEmpty() && nums[i] > stack.peek()) {
third = stack.pop();
}
stack.push(nums[i]);
}

return false;
}
}

result

  • 时间复杂度: O(n)。每个元素最多被压入和弹出栈一次,因此总时间复杂度是线性的。
  • 空间复杂度: O(n)。在最坏情况下(严格递增的序列),栈需要存储所有的元素。

520. 检测大写字母

Ideas

  1. 首先检查单词是否为空或只有一个字符,如果是,直接返回 true

  2. 使用 equals 方法检查整个单词是否全部大写或全部小写。

  3. 使用 Character.isUpperCase 方法检查首字母是否大写,然后使用 substringequals 方法检查剩余部分是否全部小写。

  4. 如果都不符合,返回 false

title
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
class Solution {
public boolean detectCapitalUse(String word) {
// 如果单词为空或只有一个字符,直接返回 true
if (word == null || word.length() <= 1) {
return true;
}

// 检查是否全部大写
if (word.equals(word.toUpperCase())) {
return true;
}

// 检查是否全部小写
if (word.equals(word.toLowerCase())) {
return true;
}

// 检查是否只有首字母大写
if (Character.isUpperCase(word.charAt(0)) &&
word.substring(1).equals(word.substring(1).toLowerCase())) {
return true;
}

// 如果都不符合,返回 false
return false;
}
}
  • 时间复杂度: O(n),其中 n 是字符串的长度。
  • 空间复杂度: O(n),主要来自于字符串操作 toUpperCase()toLowerCase()

result

519. 随机翻转矩阵

Ideas

1: 初始化和数据表示

  • 使用一个字典 map 记录已经翻转的坐标(避免使用额外的二维数组)。
  • 使用一个变量 total 记录剩余可翻转的位置数量。

2: 执行 flip 操作

  • 使用随机数生成器在剩余可翻转的位置中选取一个随机下标 x
  • 判断下标x是否已经被翻转过:
    • 如果已被翻转,使用映射字典找到当前 x 实际对应的位置。
  • 更新映射字典,将当前 x 位置映射到末尾(减少多次随机选择重复位置)。
  • 返回二维矩阵中的对应坐标 [i, j]

3: 重置操作

  • 清空映射字典,并将 total 重置为 m * n
title
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
class Solution {

private int m, n, total;
private Map<Integer, Integer> map;
private Random rand;

public Solution(int m, int n) {
this.m = m;
this.n = n;
this.total = m * n;
this.map = new HashMap<>();
this.rand = new Random();
}

public int[] flip() {

int x = rand.nextInt(total);
total--;

// 查找 x 是否已经映射
int idx = map.getOrDefault(x, x);
// 将 x 映射到总计数末尾
map.put(x, map.getOrDefault(total, total));

// 计算出二维矩阵的坐标
return new int[]{idx / n, idx % n};
}

public void reset() {
map.clear();
total = m * n;
}
}

/**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(m, n);
* int[] param_1 = obj.flip();
* obj.reset();
*/

result

2024-06-21

LCP 61. 气温变化趋势

Ideas

  • 计算气温变化趋势:对于每个地区,计算每一天与下一天的气温变化趋势。我们可以用一个数组来存储这些变化趋势。
  • 比较两个地区的气温变化趋势:遍历两个变化趋势数组,找到连续相同的最大天数。
title
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
public class Solution {

/**
* 计算两个地区气温变化趋势相同的最大连续天数
*
* @param temperatureA 地区A的气温数组
* @param temperatureB 地区B的气温数组
* @return 两地气温变化趋势相同的最大连续天数
*/
public int temperatureTrend(int[] temperatureA, int[] temperatureB) {
int maxLength = 0; // 最大连续天数
int currentLength = 0; // 当前连续天数

// 遍历两个温度数组
for (int i = 0; i < temperatureA.length - 1; i++) {
// 计算第i天和第i+1天之间的气温变化趋势
int trendA = getTrends(temperatureA[i], temperatureA[i + 1]);
int trendB = getTrends(temperatureB[i], temperatureB[i + 1]);

// 如果两个地区的气温变化趋势相同
if (trendA == trendB) {
currentLength++; // 当前连续天数加1
maxLength = Math.max(maxLength, currentLength); // 更新最大连续天数
} else {
currentLength = 0; // 重置当前连续天数
}
}

return maxLength; // 返回最大连续天数
}

/**
* 计算两个温度之间的变化趋势
*
* @param A 第一天的气温
* @param B 第二天的气温
* @return 变化趋势:0表示平稳,-1表示下降,1表示上升
*/
private int getTrends(int A, int B) {
if (A == B) {
return 0; // 平稳趋势
}
return A < B ? -1 : 1; // 上升趋势或下降趋势
}

public static void main(String[] args) {
Solution solution = new Solution();
int[] temperatureA = {21, 18, 18, 18, 31}; // 地区A的气温数组
int[] temperatureB = {34, 32, 16, 16, 17}; // 地区B的气温数组

// 输出两地气温变化趋势相同的最大连续天数
System.out.println(solution.temperatureTrend(temperatureA, temperatureB)); // 输出:2
}
}

result

思考

随着不断深入学习,发现算法才是代码的唯一解,或者说是他的最优解。从而我开始逐步的进行力扣的刷题之路。

随着公司压力的逐渐增大,我所承担的任务在难度和复杂度上都明显高于同组其他任务。然而,我并未获得应有的激励奖励,这让我开始思考这份工作的付出与收益是否值得,于是我开始有了以快速提升自己从而得到更高的回报的内容。