LeeCode 字串

一. 数组子串
560. 和为k的子数组

560. 和为 K 的子数组

思路:

  1. 前缀和: 前缀和是一个常用技巧,用于快速查询任意子数组的和。在这里,我们逐步计算数组的累积和,称为前缀和。

  2. 哈希表: 我们使用一个哈希表 preSum 来存储每个前缀和及其出现次数。这样,我们可以在常数时间内查找特定前缀和的出现次数。

  3. 统计和为 k 的子数组: 通过遍历前缀和并查找哈希表中的特定值,我们可以统计和为 k 的子数组数量。

代码解释

  • 初始化: 我们初始化 count 为 0,用于统计和为 k 的子数组的数量。我们还初始化 sum 为 0,用于计算前缀和。哈希表 preSum 用于存储每个前缀和的出现次数,我们将前缀和 0 的初始计数设置为 1,因为前缀和 0 至少出现一次。

  • 遍历数组: 我们遍历数组 nums 中的每个元素 num,并逐步计算前缀和 sum

    • 查询前缀和: 我们查询哈希表中键为 sum - k 的值。如果这个键存在于哈希表中,那么它表示存在一个或多个子数组,其和为 k。我们将这些子数组的数量添加到 count 中。

    • 更新前缀和的计数: 我们使用 preSum.getOrDefault(sum, 0) + 1 来更新当前前缀和的计数。如果当前前缀和已经存在于哈希表中,我们增加其计数;否则,我们将其计数设置为 1

  • 返回结果: 最后,我们返回计数 count,即和为 k 的连续子数组的个数。

示例理解

假设 nums = [1,2,3]k = 3

  1. 首先,初始化 count = 0sum = 0preSum = {0: 1}
  2. 遍历第一个元素 1sum = 1preSum = {0: 1, 1: 1}
  3. 遍历第二个元素 2sum = 3,查找 sum - k = 0preSum 中找到 1 个,count = 1preSum = {0: 1, 1: 1, 3: 1}
  4. 遍历第三个元素 3sum = 6,查找 sum - k = 3preSum 中找到 1 个,count = 2preSum = {0: 1, 1: 1, 3: 1, 6: 1}
  5. 返回 count = 2

此算法的时间复杂度是 O(n),其中 n 是数组的长度,空间复杂度也是 O(n)。

前缀和的概念

前缀和是一种计算数组中前 nnn 个元素和的方式。假设有一个数组 nums 和其前缀和数组 prefixSum,则:

prefixSum[i]=nums[0]+nums[1]+…+nums[i]

public int subarraySum(int[] nums, int k) {
    int count = 0; // 用于计算和为 k 的连续子数组的个数
    int sum = 0; // 用于计算前缀和
    Map<Integer, Integer> preSum = new HashMap<>(); // 哈希表用于存储前缀和的出现次数
    preSum.put(0, 1); // 初始化前缀和为 0 的情况

    for (int num : nums) {
        sum += num; // 计算当前前缀和
        // 如果存在前缀和等于当前前缀和减去 k 的情况,将其次数添加到计数器中
        if (preSum.containsKey(sum - k)) {
            count += preSum.get(sum - k);
        }
        // 更新当前前缀和的出现次数,更新也用put注意。
//getOrDefualt:如果查询的键存在,则正常返回,如果不存在就返回0.
        preSum.put(sum, preSum.getOrDefault(sum, 0) + 1);
    }

    return count;
}

二. 数组操作

数组操作一般定义辅助数组,比如current和next。像下面的56题就定义了current[ ]和next[ ]来辅助。 53题定义了currentSum和maxSum来辅助。

56 合并区间

思路:

  1. 排序: 按区间的开始端点对所有区间进行排序。这样,我们可以确保在遍历过程中,相邻的区间要么重叠要么不重叠。
  2. 合并: 遍历排序后的区间,并将重叠的区间合并为一个新区间。
  3. 返回结果: 返回合并后的区间列表。

class Solution {
    public int[][] merge(int[][] intervals) {
        if (intervals == null || intervals.length <= 1) return intervals;
        
        // 按区间的开始端点排序
        Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));

        List<int[]> mergedList = new ArrayList<>();
        int[] currentInterval = intervals[0]; // 初始化当前区间

        for (int i = 1; i < intervals.length; i++) {
            int[] nextInterval = intervals[i];
            if (currentInterval[1] >= nextInterval[0]) { // 如果当前区间与下一个区间重叠
                // 合并区间
                currentInterval[1] = Math.max(currentInterval[1], nextInterval[1]);
            } else {
                // 将当前区间添加到结果列表,并更新当前区间
                mergedList.add(currentInterval);
                currentInterval = nextInterval;
            }
        }
        // 添加最后一个区间, 注意!!!!!!!别忘
        mergedList.add(currentInterval);

//创建一个新的二维整数数组,其大小与 mergedList 中的元素数量相同,并将 mergedList 中的所有元素(每个元素都是一个整数数组)复制到这个新的二维数组中。
        return mergedList.toArray(new int[mergedList.size()][]);
    }
}

1. 当你调用 toArray 方法时,必须提供一个数组作为参数来指定返回数组的类型。在这个例子中,我们想要一个 int[][] 类型的数组,所以我们传递了一个新的 int[][] 数组作为参数

2. Integer.compare 是 Java 中的一个静态方法,用于比较两个整数值。

3.

`Comparator` 在 Java 中有许多用法,可以用于定义各种自定义的排序逻辑。以下是一些常见的用法:

### 1. 匿名内部类

你可以使用匿名内部类创建一个 `Comparator` 对象:

```java
Comparator<int[]> comparator = new Comparator<int[]>() {
    @Override
    public int compare(int[] a, int[] b) {
        return Integer.compare(a[0], b[0]);
    }
};
Arrays.sort(intervals, comparator);
```
### 2. Lambda 表达式(Java 8+)

如前所述,你可以使用 Lambda 表达式创建一个简洁的 `Comparator`:

Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
### 3. 使用 `Comparator` 的静态方法

Java 8 引入了一些 `Comparator` 的静态方法,使得创建 `Comparator` 变得更加容易:

- `comparing`: 根据提取的键进行比较。

  ```java
  Arrays.sort(intervals, Comparator.comparing(a -> a[0]));
  ```

- `reversed`: 返回一个反向排序的 `Comparator`。

  ```java
  Arrays.sort(intervals, Comparator.comparing(a -> a[0]).reversed());
  ```

- `thenComparing`: 用于链式比较,当主要排序条件相同时,可以使用另一个条件。

  ```java
  Arrays.sort(intervals, Comparator.comparing(a -> a[0]).thenComparing(a -> a[1]));
  ```

- `naturalOrder` 和 `reverseOrder`: 用于基于自然排序或反向自然排序的比较。

  ```java
  Arrays.sort(strings, Comparator.naturalOrder());
  Arrays.sort(strings, Comparator.reverseOrder());
  ```

### 4. 使用方法引用

如果你的排序逻辑可以用现有方法表示,你可以使用方法引用:

```java
Arrays.sort(strings, String::compareToIgnoreCase);
```

总的来说,`Comparator` 提供了丰富的方式来定义自定义的排序逻辑。你可以选择最适合你需求和代码风格的方式。

53. 最大子数组和

思路:

  1. 初始化: 定义两个变量 maxSumcurrentSum 来分别存储最大子数组和和当前子数组和。
  2. 遍历数组: 遍历数组中的每个元素,并对每个元素执行以下操作: a. 将当前元素与当前子数组和的和与当前元素本身比较,选择较大的一个作为新的当前子数组和。这样可以确保如果当前子数组和变为负数,则可以从当前元素开始新的子数组。 b. 将当前子数组和与最大子数组和比较,选择较大的一个作为新的最大子数组和。
  3. 返回结果: 返回最大子数组和 maxSum
class Solution {
    public int maxSubArray(int[] nums) {
        if (nums == null || nums.length == 0) return 0;

        int maxSum = nums[0]; // 初始化最大子数组和
        int currentSum = nums[0]; // 初始化当前子数组和

        for (int i = 1; i < nums.length; i++) {
            // 比较当前元素与当前子数组和的和与当前元素本身,选择较大的一个作为新的当前子数组和,
如果currentSum是负数时,就舍弃之前的,从当前的数开始新的子数组
            currentSum = Math.max(nums[i], currentSum + nums[i]);
            // 更新最大子数组和
            maxSum = Math.max(maxSum, currentSum);
        }

        return maxSum;
    }
}
189. 轮转数组

思路:

  1. 整体反转: 将整个数组反转。
  2. 反转前 k 个元素: 其中 kkk 是旋转的步数。
  3. 反转剩余元素: 反转剩下的元素。
class Solution {
    public void rotate(int[] nums, int k) {
        k %= nums.length; // 如果 k 大于数组长度,取余数
        reverse(nums, 0, nums.length - 1); // 反转整个数组
        reverse(nums, 0, k - 1); // 反转前 k 个元素
        reverse(nums, k, nums.length - 1); // 反转剩余元素
    }

    // 辅助方法:反转数组的一部分
    private void reverse(int[] nums, int start, int end) {
        while (start < end) {
            int temp = nums[start];
            nums[start] = nums[end];
            nums[end] = temp;
            start++;
            end--;
        }
    }
}
 
238. 除自身以外数组的乘积

思路:

  1. 计算左侧乘积: 创建一个新数组 left,其中 left[i] 包括 nums[i] 左侧所有元素的乘积。
  2. 计算右侧乘积: 创建一个新数组 right,其中 right[i] 包括 nums[i] 右侧所有元素的乘积。
  3. 计算结果: 结果数组 answer 的每个元素 answer[i] 等于 left[i] * right[i]
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] left = new int[n];
        int[] right = new int[n];
        int[] answer = new int[n];

        left[0] = 1;
        for (int i = 1; i < n; i++) {
            left[i] = left[i - 1] * nums[i - 1];
        }

        right[n - 1] = 1;
        for (int i = n - 2; i >= 0; i--) {
            right[i] = right[i + 1] * nums[i + 1];
        }

        for (int i = 0; i < n; i++) {
            answer[i] = left[i] * right[i];
        }

        return answer;
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值