LeetCode刷题—数组:双指针

数组题目中有两种双指针的应用:左右指针快慢指针
目录如下:
双指针(左右)
双指针(快慢)
相关题目有:
167,两数之和Ⅱ
344,反转字符串
26,删除排序数组中的重复项
27,移除元素
283,移动零
485,最大连续1的个数
540,有序数组中的单一元素
209,长度最小的子数组

双指针(左右)

左右指针在数组中实际是指两个索引值,一般初始化为left = 0, right = nums.length - 1。循环条件为 while(left < right)

在上篇文章二分查找中,凸出了双指针特性。在下面题目中体会。

看到题目要求有【原地修改】的要求,一般都采用双指针。

167,两数之和Ⅱ,easy

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

返回的下标值(index1index2)不是从零开始的。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:

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

题解

只要数组有序,就应该想到双指针技巧。这道题的解法有点类似二分查找,通过调节leftright可以调整sum的大小

代码

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int n = numbers.length;
        int left = 0;
        int right = n - 1;        
		while(left < right){
            if(numbers[left] + numbers[right] == target)
                return new int[]{left + 1, right + 1};
            else if(numbers[left] + numbers[right] < target)
                left++;
            else
                right--;
        }
        return new int[]{0,0};
    }
}
344,反转字符串,easy

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

示例 1:

输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:

输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

代码

class Solution {
    public void reverseString(char[] s) {
        if(s == null) return;
        int left = 0;
        int right = s.length - 1;
        while(left < right){
          char temp = s[left];  
          s[left] = s[right];
          s[right] = temp;
          left++;
          right--;
        }
    }
}

双指针(快慢)

快慢指针在数组中实际是指两个索引值,快指针始终在慢指针的前面,一般初始化为 slow = 0, fast = 0 或 1。循环条件为 while(fast < nums.length)

框架
public int f&s(int[] nums) {
        if(nums.length == 0 || nums == null) return 0;
        int slow = 0;
        int fast = 01;
        while(fast < nums.length){
            //当前元素值不是所找的
            if(nums[fast] != ...){
                //看slow、fast具体含义
                ...
				slow = ...
            }
            //否则,fast继续向前找
            fast++;
        }
        return ...;
    }
26,删除排序数组中的重复项,easy

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例 1:

给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。

示例 2:

给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

题解

快慢指针初始指向索引0和1,用快指针探路,慢指针保存没有重复元素的数组。

快指针向前,直到不和 当前慢指针指向元素 重复,慢指针索引+1,指向元素为新的数字。

动画演示

代码

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums.length == 0) return 0;
        int slow = 0;
        int fast = 1;
        while(fast < nums.length){
            if(nums[slow] != nums[fast]){
                slow++;
                // 维护 nums[0..slow] 无重复
                nums[slow] = nums[fast];
            }
            fast++;
        }
        // 数组长度为索引 + 1
        return slow + 1;
    }
}

**注:**类似题目:83.删除排序链表中的重复元素

27,移除元素,easy

你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

给定 nums = [3,2,2,3], val = 3,

函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

示例 2:

给定 nums = [0,1,2,2,3,0,4,2], val = 2,

函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

注意这五个元素可为任意顺序。

题解

快慢指针初始都指向首个元素,快指针用于探路,如果不等于 val,则可以进行覆盖;如果等于 val,继续向前。

代码

class Solution {
    public int removeElement(int[] nums, int val) {
        if(nums.length == 0 || nums == null) return 0;
        int slow = 0;
        int fast = 0;
        while(fast < nums.length){
            //当前元素值不等于val
            if(nums[fast] != val){
                //建立新数组(覆盖在原数组上)
                nums[slow] = nums[fast];
                slow++;
            }
            //否则,fast继续向前找等于val的元素
            fast++;
        }
        return slow;
    }
}

细节

上一题要求是删除重复元素,fast 移到下一个不重复元素时,索引slow++,并将fast 指向的值赋给 nums[slow],最后得到数组长度为 索引数slow + 1。

这里是先给nums[slow]赋值然后再给slow++,这样可以保证nums[0..slow-1]是不包含值为val的元素的,最后的结果数组长度就是slow

283,移动零,easy

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

说明:

必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。

题解

与上一题类似,只是删除操作改成了移动操作。

代码

class Solution {
    public void moveZeroes(int[] nums) {
        if(nums.length == 0 || nums == null) return;
        int slow = 0;
        int fast = 0;
        while(fast < nums.length){
            if(nums[fast] != 0){    
                //交换
                int temp = nums[fast];
                nums[fast] = nums[slow];
                nums[slow] = temp;
                slow++;
            }
            fast++;
        }
    }
}

另一种解法

遍历此数组,如果当前元素不为0,就赋给慢指针所在位置的元素,遍历结束再将慢指针之后的元素赋为 0。可以看成建立了新的数组,但是是在原数组上改变的。

class Solution {
    public void moveZeroes(int[] nums) {
        if(nums.length == 0) return;
        int index = 0;
        for(int i = 0; i < nums.length; i++){
            if(nums[i] != 0)
                nums[index++] = nums[i];           
        }
        for(int i = index; i < nums.length; i++){
            nums[i] = 0;
        }
    }
}
485,最大连续1的个数,easy

给定一个二进制数组, 计算其中最大连续1的个数。

示例 1:

输入: [1,1,0,1,1,1]
输出: 3
解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3.

注意:

输入的数组只包含 0 和1。

输入数组的长度是正整数,且不超过 10,000。

题解

快慢指针初始都指向首个元素,快指针移至最后一个非 0 元素,fast - slow 即为当前连续1 的个数。当 nums[fast] = 0 时,slow 移至 fast 之后再开始统计。重复上述步骤直至 fast 遍历到最后一个元素。

代码

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        if(nums.length == 0 || nums == null) return 0;
        int slow = 0;
        int fast = 0;
        int res = 0;
        while(fast < nums.length){
            if(nums[fast] != 1){
                res = Math.max(res, fast - slow);
                slow = fast + 1;
            }
            fast++;  
        }
        //数组全1的情况
        return Math.max(res, fast - slow);
    }
}

细节

注意考虑数组全为 1 的情况,返回值为 fast - slow

另一种解法

一次遍历。两个计数器 countmaxCount作为计数指针 和 当前连续1 的最大个数。

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        if(nums.length == 0 || nums == null) return 0;
        int count = 0;
        int maxCount = 0;
        for(int i = 0; i < nums.length; i++){
            if(nums[i] != 0){
                count++;
                maxCount = Math.max(count, maxCount);
            }else{
                count = 0;
            }           
        }
        return maxCount;
    }
}

下面两题稍有不同,但总体思想大致一致。

540,有序数组中的单一元素,medium

给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

示例 1:

输入: [1,1,2,3,3,4,4,8,8]
输出: 2

示例 2:

输入: [3,3,7,7,10,11,11]
输出: 10

代码:

class Solution {
    public int singleNonDuplicate(int[] nums) {
        int slow = 0;
        int fast = 1;
        while(fast < nums.length){           
            //检查这一对数,如果不同返回第一个数
            if(nums[slow] < nums[fast])
                return nums[slow];
            //否则,检查下一对数
            if(nums[slow] == nums[fast]){
                slow = slow + 2;
                fast = fast + 2;               
            }
        }
        //nums仅有一个元素的特殊情况
        return nums[slow];
    }
}
209,长度最小的子数组,medium

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

示例:

输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

题解

快慢指针来维护一个滑动窗口,相当于数组元素入队出队。具体过程可以用队列来理解,但代码实现用快慢指针。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

代码

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int slow = 0;
        int fast = 0;
        int sum = 0;
        int min = Integer.MAX_VALUE;
        while(fast < nums.length){
            //当前窗口中元素和 小于 s,加入元素
            sum += nums[fast]; 
            fast++;      
            //窗口内的元素和 大于等于 s 时,更新最小长度,并缩短窗口
            while(sum >= s){                
                min = Math.min(min, fast - slow);
                sum -= nums[slow];
                slow++;                
            }
        }
        return min == Integer.MAX_VALUE ? 0 : min;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值