leetcode赏碗饭吃——双指针专题

作为“冰川世纪”中,渴望有一份不错工作的学生,开始记录一下学习经历,来激励自己。
深知算法题算企业考察的重点,故现在开始系统性的刷一刷,争取常常更新。

介绍

我们会发现,当涉及到数组,链表,字符串……时常常会遇到双指针题目。让我们从这些数据结构中找找相同点,你会发现它们可以算是“线性”的,可以按一个接一个单元的读取。那么,你会好奇为什么是双指针呢?因为如果我们使用双指针,不断移动,可以使整个数组分成三份,[0, i), [i, j), [j, array.length),从而达到我们的目的。比如分成三份,【处理好】,【检测完】,【待检测】。

常见类型

同向双指针

在同向双指针套路中,两个指针朝相同方向移动,但是快慢不同。其三个区域会被分割为下图所示:

在这里插入图片描述
其中 [0, i) 的数据代表处理好的数据,[i, j) 中的数据是那些处理过但不需要的数据,[j, array.length) 区间的数据为接下来待处理的数据

反向双指针

反向双指针中的两个指针反向而行,同样分为三个区间:
在这里插入图片描述
其中 [0, i) 和 (j, array.length) 内的数据均为处理好的数据,[i, j] 中的数据待处理。用此方法处理过的数组不会保留原来元素的相对位置。

实践应用

我会介绍一些相关经典题目,当然解法不一定是最优。你可以在leetcode上看八仙过海,各显神通。我只是提供一种思路,为什么可以使用双指针来解题,为什么会想到双指针解题!

同向指针题目

238.移动零
在这里插入图片描述
由于不可以进行复制,我们只能在原数组上进行操作。
1.首先,问自己一个问题,怎么样将数组元素移动呢?就是根据下标来交换值。
2.下标?我们该如何找到合适的下标呢。如果根据题干中的“末尾”,我们很容易想到遍历数组,遇到0就让它和未交换的最后一位进行交换。这种想法是可行的,但是题干中还提到约束条件——保持非零元素的顺序。所以,这个方法行不通。
3.要将”0“放到数组末尾,我们可以不可以换个角度,将非0元素放到数组前呢?试一试!但是又遇到一个问题,如果我们只是遍历数组,遇到一个非0元素就移动到最前吗?这样还是出现排序问题,还需要用一个数字记住我们移动了几次,这个非0元素该放到哪个位置。
4.但是,如果我们将这个数字抽象出来,这不就是一根指针!用于表示已经处理过的数据,这样我们很容易想到同向双指针,将数组分为【处理好】,【不需要】,【待检测】。

具体代码:

class Solution {
    
    
    public void moveZeroes(int[] nums) {
    
    
        int n = nums.length, left = 0, right = 0;
        while (right < n) {
    
    
            if (nums[right] != 0) {
    
    
                int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
                left++;
            }
            right++;
        }
    }
}

80.删除有序数组中的重复项 II
在这里插入图片描述
理一下思路,又是一个在原数组上操作的题目。由于它是有序数组,故一个元素最多出现两次,即连续2次,之后的数便要删除。值得注意的是,这里的删除,是指将元素放到数组末尾,然后返回有效值的长度。
1.”连续“——这是个关键词,我们很容易想到利用两根指针,一前一后来判断重复问题。
2.好,那现在使用两根指针,距离差为1,开始遍历。
3.如果值相等,那么要小心,让后指针移动1位,再进行判断
3.问题来了,如果确实又重复了,我们该怎么办?难道将这个值不管一切,就和最后一个值进行交换?不可以,它会破坏顺序。
4.我们要想到双指针重要目的,即分组,如同向指针的图片。
5.当一切检验完了后,无效值自然排到了数组末尾,而有效值也未改变相对顺序。
代码如下:

class Solution {
    
    
    public int removeDuplicates(int[] nums) {
    
    
        int pre = 1;
        int aft = 2;
        int temp;
        while(aft < nums.length){
    
    
            if(nums[aft] == nums[pre]){
    
    
                if(nums[aft] == nums[(pre - 1)]){
    
    
                    aft++;
                    continue;
                }
            }
            temp = nums[aft];
            nums[aft] = nums[++pre];
            nums[pre] = temp;
            aft++;
        }
       return nums.length <= 2 ? nums.length : (pre + 1);
    }
}

反向指针题目

先来个简单的字符串反转(认为是char[]数组),反转就是后半部分与前半部分进行对调。
那么,我们怎么同时得到前后部分呢?只要使用双指针,一个开端,一个结尾,都向数组中间移动即可。
好,简单的介绍完了,来个颇具挑战的题目:
11. 盛最多水的容器
在这里插入图片描述
我们先思考一下怎么算盛水面积,即长乘高,即数组下标之差乘两者的较小值。
1.现在知道怎么算了,那我们怎么用最少的遍历得到最大值呢?根据逻辑,应该从较大的值再一步步比较得到最大值。
2.较大的值?如果我们从数组两端开始,岂不是在不知道高度的情况下,保证了长度最大,可以称为较大值。
3.数组两端不就是我们的反向双指针!
4.问题又来了,指针该如何移动呢?我们要明白,长度只会越来越小,所以一定要找到更高值!
如果移动的是较大值,无论如何面积都会缩小,而移动较小值,有机会面积增大。
具体代码:

扫描二维码关注公众号,回复: 15387411 查看本文章
class Solution {
    
    
    public int maxArea(int[] height) {
    
    
        int max = 0;
        int start = 0;
        int row;
        int column;
        int end = height.length - 1;
        while (start < end) {
    
    
            row = ( end - start ) ;
            if (height[start] < height[end]) {
    
    
                column = height[start];
                start++;
            } else {
    
    
                column = height[end];
                end--;
            }
            max = max > (row * column) ? max : (row * column);
        }
        return max;
    }
}

本次专题结束,认为有些帮助的,请“高抬贵手”,点个免费的赞和关注,爱你

猜你喜欢

转载自blog.csdn.net/AkinanCZ/article/details/131167490