Skip to content

Commit

Permalink
数组双指针内容优化
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Sep 6, 2023
1 parent abf7596 commit 30e9ffc
Showing 1 changed file with 74 additions and 52 deletions.
126 changes: 74 additions & 52 deletions Contents/01.Array/04.Array-Two-Pointers/01.Array-Two-Pointers.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@

## 2. 对撞指针

> **对撞指针**:指的是两个指针 `left``right` 分别指向序列第一个元素和最后一个元素,然后 `left` 指针不断递增,`right` 不断递减,直到两个指针的值相撞(即 `left == right`),或者满足其他要求的特殊条件为止。
> **对撞指针**:指的是两个指针 $left$、$right$ 分别指向序列第一个元素和最后一个元素,然后 $left$ 指针不断递增,$right$ 不断递减,直到两个指针的值相撞(即 $left == right$),或者满足其他要求的特殊条件为止。
![对撞指针](https://qcdn.itcharge.cn/images/20230906165407.png)

### 2.1 对撞指针求解步骤

1. 使用两个指针 `left``right``left` 指向序列第一个元素,即:`left = 0``right` 指向序列最后一个元素,即:`right = len(nums) - 1`
2. 在循环体中将左右指针相向移动,当满足一定条件时,将左指针右移,`left += 1`。当满足另外一定条件时,将右指针左移,`right -= 1`
3. 直到两指针相撞(即 `left == right`),或者满足其他要求的特殊条件时,跳出循环体。
1. 使用两个指针 $left$,$right$。$left$ 指向序列第一个元素,即:$left = 0$,$right$ 指向序列最后一个元素,即:$right = len(nums) - 1$
2. 在循环体中将左右指针相向移动,当满足一定条件时,将左指针右移,$left += 1$。当满足另外一定条件时,将右指针左移,$right -= 1$
3. 直到两指针相撞(即 $left == right$),或者满足其他要求的特殊条件时,跳出循环体。

### 2.2 对撞指针伪代码模板

Expand Down Expand Up @@ -47,9 +49,9 @@ return 没找到 或 找到对应值

#### 2.4.2 题目大意

**描述**:给定一个下标从 `1` 开始计数、升序排列的整数数组:`numbers` 和一个目标值 `target`
**描述**:给定一个下标从 $1$ 开始计数、升序排列的整数数组:$numbers$ 和一个目标值 $target$

**要求**:从数组中找出满足相加之和等于 `target` 的两个数,并返回两个数在数组中下的标值。
**要求**:从数组中找出满足相加之和等于 $target$ 的两个数,并返回两个数在数组中下的标值。

**说明**

Expand All @@ -61,20 +63,25 @@ return 没找到 或 找到对应值

**示例**

- 示例 1:

```python
输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:27 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
```

- 示例 2:

```python
输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:24 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
```

#### 2.4.3 解题思路

这道题如果暴力遍历数组,从中找到相加之和等于 `target` 的两个数,时间复杂度为 $O(n^2)$,可以尝试一下。
这道题如果暴力遍历数组,从中找到相加之和等于 $target$ 的两个数,时间复杂度为 $O(n^2)$,可以尝试一下。

```python
class Solution:
Expand All @@ -93,13 +100,13 @@ class Solution:

可以考虑使用对撞指针来减少时间复杂度。具体做法如下:

1. 使用两个指针 `left``right``left` 指向数组第一个值最小的元素位置,`right` 指向数组值最大元素位置。
1. 使用两个指针 $left$,$right$。$left$ 指向数组第一个值最小的元素位置,$right$ 指向数组值最大元素位置。
2. 判断两个位置上的元素的和与目标值的关系。
1. 如果元素和等于目标值,则返回两个元素位置。
2. 如果元素和大于目标值,则让 `right` 左移,继续检测。
3. 如果元素和小于目标值,则让 `left` 右移,继续检测。
3. 直到 `left``right` 移动到相同位置停止检测。
4. 如果最终仍没找到,则返回 `[-1, -1]`
2. 如果元素和大于目标值,则让 $right$ 左移,继续检测。
3. 如果元素和小于目标值,则让 $left$ 右移,继续检测。
3. 直到 $left$$right$ 移动到相同位置停止检测。
4. 如果最终仍没找到,则返回 $[-1, -1]$

##### 思路 1:代码

Expand Down Expand Up @@ -132,15 +139,15 @@ class Solution:

#### 2.5.2 题目大意

**描述**:给定一个字符串 `s`
**描述**:给定一个字符串 $s$

**要求**:判断是否为回文串(只考虑字符串中的字母和数字字符,并且忽略字母的大小写)。

**说明**

- 回文串:正着读和反着读都一样的字符串。
- $1 \le s.length \le 2 * 10^5$。
- `s` 仅由可打印的 ASCII 字符组成。
- $s$ 仅由可打印的 ASCII 字符组成。

**示例**

Expand All @@ -159,12 +166,12 @@ class Solution:

##### 思路 1:对撞指针

1. 使用两个指针 `left``right``left` 指向字符串开始位置,`right` 指向字符串结束位置。
2. 判断两个指针对应字符是否是字母或数字。 通过 `left` 右移、`right` 左移的方式过滤掉字母和数字以外的字符。
3. 然后判断 `s[start]` 是否和 `s[end]` 相等(注意大小写)。
1. 如果相等,则将 `left` 右移、`right` 左移,继续进行下一次过滤和判断。
2. 如果不相等,则说明不是回文串,直接返回 `False`
4. 如果遇到 `left == right`,跳出循环,则说明该字符串是回文串,返回 `True`
1. 使用两个指针 $left$,$right$。$left$ 指向字符串开始位置,$right$ 指向字符串结束位置。
2. 判断两个指针对应字符是否是字母或数字。 通过 $left$ 右移、$right$ 左移的方式过滤掉字母和数字以外的字符。
3. 然后判断 $s[start]$ 是否和 $s[end]$ 相等(注意大小写)。
1. 如果相等,则将 $left$ 右移、$right$ 左移,继续进行下一次过滤和判断。
2. 如果不相等,则说明不是回文串,直接返回 $False$
4. 如果遇到 $left == right$,跳出循环,则说明该字符串是回文串,返回 $True$

##### 思路 1:代码

Expand Down Expand Up @@ -227,16 +234,16 @@ class Solution:

##### 思路 1:对撞指针

从示例中可以看出,如果确定好左右两端的直线,容纳的水量是由 `左右两端直线中较低直线的高度 * 两端直线之间的距离 ` 所决定的。所以我们应该使得 **较低直线的高度尽可能的高**,这样才能使盛水面积尽可能的大。
从示例中可以看出,如果确定好左右两端的直线,容纳的水量是由左右两端直线中较低直线的高度 * 两端直线之间的距离所决定的。所以我们应该使得「」,这样才能使盛水面积尽可能的大。

可以使用对撞指针求解。移动较低直线所在的指针位置,从而得到不同的高度和面积,最终获取其中最大的面积。具体做法如下:

1. 使用两个指针 `left``right``left` 指向数组开始位置,`right` 指向数组结束位置。
2. 计算 `left``right` 所构成的面积值,同时维护更新最大面积值。
3. 判断 `left``right` 的高度值大小。
1. 如果 `left` 指向的直线高度比较低,则将 `left` 指针右移。
2. 如果 `right` 指向的直线高度比较低,则将 `right` 指针左移。
4. 如果遇到 `left == right`,跳出循环,最后返回最大的面积。
1. 使用两个指针 $left$,$right$。$left$ 指向数组开始位置,$right$ 指向数组结束位置。
2. 计算 $left$$right$ 所构成的面积值,同时维护更新最大面积值。
3. 判断 $left$$right$ 的高度值大小。
1. 如果 $left$ 指向的直线高度比较低,则将 $left$ 指针右移。
2. 如果 $right$ 指向的直线高度比较低,则将 $right$ 指针左移。
4. 如果遇到 $left == right$,跳出循环,最后返回最大的面积。

##### 思路 1:代码

Expand Down Expand Up @@ -265,11 +272,13 @@ class Solution:

> **快慢指针**:指的是两个指针从同一侧开始遍历序列,且移动的步长一个快一个慢。移动快的指针被称为 「快指针(fast)」,移动慢的指针被称为「慢指针(slow)」。两个指针以不同速度、不同策略移动,直到快指针移动到数组尾端,或者两指针相交,或者满足其他特殊条件时为止。
![快慢指针](https://qcdn.itcharge.cn/images/20230906173808.png)

### 3.1 快慢指针求解步骤

1. 使用两个指针 `slow``fast``slow` 一般指向序列第一个元素,即:`slow = 0``fast` 一般指向序列第二个元素,即:`fast = 1`
2. 在循环体中将左右指针向右移动。当满足一定条件时,将慢指针右移,即 `slow += 1`。当满足另外一定条件时(也可能不需要满足条件),将快指针右移,即 `fast += 1`
3. 到快指针移动到数组尾端(即 `fast == len(nums) - 1`),或者两指针相交,或者满足其他特殊条件时跳出循环体。
1. 使用两个指针 $slow$、$fast$。$slow$ 一般指向序列第一个元素,即:$slow = 0$,$fast$ 一般指向序列第二个元素,即:$fast = 1$
2. 在循环体中将左右指针向右移动。当满足一定条件时,将慢指针右移,即 $slow += 1$。当满足另外一定条件时(也可能不需要满足条件),将快指针右移,即 $fast += 1$
3. 到快指针移动到数组尾端(即 $fast == len(nums) - 1$),或者两指针相交,或者满足其他特殊条件时跳出循环体。

### 3.2 快慢指针伪代码模板

Expand Down Expand Up @@ -297,22 +306,27 @@ return 合适的值

#### 3.4.2 题目大意

**描述**:给定一个有序数组 `nums`
**描述**:给定一个有序数组 $nums$

**要求**:删除数组 `nums` 中的重复元素,使每个元素只出现一次。并输出去除重复元素之后数组的长度。
**要求**:删除数组 $nums$ 中的重复元素,使每个元素只出现一次。并输出去除重复元素之后数组的长度。

**说明**

- 不能使用额外的数组空间,在原地修改数组,并在使用 $O(1)$ 额外空间的条件下完成。

**示例**

- 示例 1:

```python
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
```

- 示例 2:

```python
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
Expand All @@ -326,13 +340,13 @@ return 合适的值

删除重复元素,实际上就是将不重复的元素移到数组左侧。考虑使用双指针。具体算法如下:

1. 定义两个快慢指针 `slow``fast`。其中 `slow` 指向去除重复元素后的数组的末尾位置。`fast` 指向当前元素。
2.`slow` 在后, `fast` 在前。令 `slow = 0``fast = 1`
3. 比较 `slow` 位置上元素值和 `fast` 位置上元素值是否相等。
- 如果不相等,则将 `slow` 右移一位,将 `fast` 指向位置的元素复制到 `slow` 位置上。
4.`fast` 右移 `1` 位。
5. 重复上述 3 ~ 4 步,直到 `fast` 等于数组长度。
6. 返回 `slow + 1` 即为新数组长度。
1. 定义两个快慢指针 $slow$,$fast$。其中 $slow$ 指向去除重复元素后的数组的末尾位置。$fast$ 指向当前元素。
2.$slow$ 在后, $fast$ 在前。令 $slow = 0$,$fast = 1$
3. 比较 $slow$ 位置上元素值和 $fast$ 位置上元素值是否相等。
- 如果不相等,则将 $slow$ 右移一位,将 $fast$ 指向位置的元素复制到 $slow$ 位置上。
4.$fast$ 右移 $1$ 位。
5. 重复上述 $3 \sim 4$ 步,直到 $fast$ 等于数组长度。
6. 返回 $slow + 1$ 即为新数组长度。

##### 思路 1:代码

Expand Down Expand Up @@ -360,15 +374,17 @@ class Solution:

## 4. 分离双指针

> **分离双指针**:两个指针分别属于不同的数组 / 链表,两个指针分别在两个数组 / 链表中移动。
> **分离双指针**:两个指针分别属于不同的数组,两个指针分别在两个数组中移动。
![分离双指针](https://qcdn.itcharge.cn/images/20230906180852.png)

### 4.1 分离双指针求解步骤

1. 使用两个指针 `left_1``left_2``left_1` 指向第一个数组 / 链表的第一个元素,即:`left_1 = 0``left_2` 指向第二个数组 / 链表的第一个元素,即:`left_2 = 0`
2. 当满足一定条件时,两个指针同时右移,即 `left_1 += 1``left_2 += 1`
3. 当满足另外一定条件时,将 `left_1` 指针右移,即 `left_1 += 1`
4. 当满足其他一定条件时,将 `left_2` 指针右移,即 `left_2 += 1`
5. 当其中一个数组 / 链表遍历完时或者满足其他特殊条件时跳出循环体
1. 使用两个指针 $left\underline{}1$、$left\underline{}2$。$left\underline{}1$ 指向第一个数组的第一个元素,即:$left\underline{}1 = 0$,$left\underline{}2$ 指向第二个数组的第一个元素,即:$left\underline{}2 = 0$
2. 当满足一定条件时,两个指针同时右移,即 $left\underline{}1 += 1$、$left\underline{}2 += 1$
3. 当满足另外一定条件时,将 $left\underline{}1$ 指针右移,即 $left\underline{}1 += 1$
4. 当满足其他一定条件时,将 $left\underline{}2$ 指针右移,即 $left\underline{}2 += 1$
5. 当其中一个数组遍历完时或者满足其他特殊条件时跳出循环体

### 4.2 分离双指针伪代码模板

Expand Down Expand Up @@ -400,7 +416,7 @@ while left_1 < len(nums1) and left_2 < len(nums2):

#### 4.4.2 题目大意

**描述**:给定两个数组 `nums1``nums2`
**描述**:给定两个数组 $nums1$$nums2$

**要求**:返回两个数组的交集。重复元素只计算一次。

Expand All @@ -411,11 +427,17 @@ while left_1 < len(nums1) and left_2 < len(nums2):

**示例**

- 示例 1:

```python
输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]
示例 2
```

- 示例 2:

```python
输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的
Expand All @@ -425,12 +447,12 @@ while left_1 < len(nums1) and left_2 < len(nums2):

##### 思路 1:分离双指针

1. 对数组 `nums1``nums2` 先排序。
2. 使用两个指针 `left_1``left_2``left_1` 指向第一个数组的第一个元素,即:`left_1 = 0``left_2` 指向第二个数组的第一个元素,即:`left_2 = 0`
3. 如果 `nums1[left_1]` 等于 `nums2[left_2]`,则将其加入答案数组(注意去重),并将 `left_1``left_2` 右移。
4. 如果 `nums1[left_1]` 小于 `nums2[left_2]`,则将 `left_1` 右移。
5. 如果 `nums1[left_1]` 大于 `nums2[left_2]`,则将 `left_2` 右移。
6. 最后输出答案数组
1. 对数组 $nums1$、$nums2$ 先排序。
2. 使用两个指针 $left\underline{}1$、$left\underline{}2$。$left\underline{}1$ 指向第一个数组的第一个元素,即:$left\underline{}1 = 0$,$left\underline{}2$ 指向第二个数组的第一个元素,即:$left\underline{}2 = 0$
3. 如果 $nums1[left\underline{}1] == nums2[left\underline{}2]$,则将其加入答案数组(注意去重),并将 $left\underline{}1$$left\underline{}2$ 右移。
4. 如果 $nums1[left\underline{}1] < nums2[left\underline{}2]$,则将 $left\underline{}1$ 右移。
5. 如果 $nums1[left\underline{}1] > nums2[left\underline{}2]$,则将 $left\underline{}2$ 右移。
6. 最后返回答案数组

##### 思路 1:代码

Expand Down

0 comments on commit 30e9ffc

Please sign in to comment.