算法通关村第3关【白银】| 双指针思想

1. 双指针思想

双指针不仅指两个指针,也可以是两个变量,指向两个值。

有三种类型:

  • 快慢型:一前一后
  • 对撞型:从两端向中间靠拢
  • 背向型:从中间向两端分开

2. 删除元素专题

2.1原地移除元素

算法通关村第3关【白银】| 双指针思想_第1张图片

(1)快慢指针

思路:每次找到等于val就移动数组当val值比较多的时候时间复杂度太高,使用fast指针扫描等于val的值连续删除,当找到一段连续val中第一个不等于val的值的时候就覆盖实现批量删除。

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        int fast = 0;
        int len = nums.length;
        while(fast < nums.length){
            if(nums[fast] == val){
                fast++;
                len--;
            }else{
                nums[slow++] = nums[fast++];
            }
        }
        return len;
    }
}

 (2)对撞指针

思路:两个指针一左一右,题目没有规定元素顺序,故当左指针指向要删除的值的时候就将右指针的值替换上来,右指针左移一位,实现删除一位的效果。

class Solution {
    public int removeElement(int[] nums, int val) {
        int right = nums.length - 1;
        int left = 0;
        int len = nums.length;
        while(left <= right){
            if(nums[left] != val){
                left++;
            }else{
                nums[left] = nums[right--];
                len--;
            }
        }
        return len;
    }
}

2. 2删除有序数组中的重复项

算法通关村第3关【白银】| 双指针思想_第2张图片

 思路:双指针,当一直重复fast指针就一直往后扫,直到扫到不重复的值,然后将不重复的值覆盖slow指向的重复的值,注意slow初始化为1,这样第一次进循环一定相等。

两个关键点:slow保留指针什么时候移动、fast指向怎么算符合条件的值

此处slow指针当元素开始不重复,即fast指向新值的时候移动

nums[fast] != nums[fast- 1]代表指向新值

class Solution {
    public int removeDuplicates(int[] nums) {
        int slow = 1;
        int fast = 1;
        while(fast < nums.length){
            if(nums[fast] != nums[fast- 1]){
                nums[slow++] = nums[fast];
            }
            fast++;
           
        }
        return slow;
    }
}

当删除使元素出现k个重复:

前k个不需要管,slow表示需要保留的值

当且仅当 nums[slow−k]=nums[fast]时,当前待检查元素 nums[fast]不应该被保留

(因为此时必然有 nums[slow−k]=nums[slow−(k-1)]=...=nums[fast])

例如:int[] arr = new int[]{1,1,1,2, 2,3,4,5,5,6,7,8,8,8,8,9};

此处slow指针当重复的元素不超过k个时移动

nums[fast] != nums[slow - k]代表fast指向的值在重复的k个元素范围之内

public static int removeDuplicatesk(int[] nums,int k) {
        int slow = k;
        int fast = k;
        while(fast < nums.length){
            if(nums[fast] != nums[slow - k]){
                nums[slow] = nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }

扩展:出现重复的元素都不要

此处slow指针当前后元素都只出现一次时移动

nums[fast] == nums[fast -1]判断元素是否重复了

public static int removeDuplicates0(int[] nums,int k) {
        int slow = 0;
        int fast = 1;
        int count = 0;//用来判断元素是否出现重复
        while(fast < nums.length){
            if (nums[fast] == nums[fast -1]){
                count++;
            } else if(nums[fast] != nums[fast -1]&&count!=0){
                //当元素出现不重复并且fast指向的是新值
                nums[slow] = nums[fast];
                //开始新的值重置
                count = 0;
            } else if (nums[fast] != nums[fast -1]&&count == 0){
                //当元素不重复,并且fast指向前也是不重复的值
                //这时slow保留指针才移动
                nums[++slow] = nums[fast];
            }
            fast++;
        }
        return count == 0 ? slow+1 : slow;
    }

3.元素奇偶移动

算法通关村第3关【白银】| 双指针思想_第3张图片

思路:对撞型双指针,当left指到奇数,right指到偶数就相互交换。需要注意一些边界处理细节,避免数组越界情况。

class Solution {
    public int[] sortArrayByParity(int[] nums) {
        int left = 0;
        int right = nums.length - 1;
        while(left

4.数组轮转

算法通关村第3关【白银】| 双指针思想_第4张图片

思路:

方法一、暴力解法,找到开始轮转的数,然后剪切拼接到新的数组。需要注意k可能比数组长度大得取模

class Solution {
    public void rotate(int[] nums, int k) {
        int[] res = new int[nums.length];
        int left = 0;
        int right = nums.length;
        int kk = k;
        while( k % nums.length>0){
            right--;
            k--;
        }
        int i = 0;
        for(;i

 优化:i+k将当前元素往右移动k,取模避免数组越界

class Solution {
    public void rotate(int[] nums, int k) {
        int n = nums.length;
        int[] newArr = new int[n];
        for (int i = 0; i < n; ++i) {
            newArr[(i + k) % n] = nums[i];
        }
        System.arraycopy(newArr, 0, nums, 0, n);
    }
}

方法二、翻转,往右轮转结果就是数组后面的值移动到了前面,进行一次整体翻转,再在k处分成左右两部分再次翻转就是往右轮转的最终效果了。

class Solution {
    public void rotate(int[] nums, int k) {
        revers(nums,0,nums.length-1);
        revers(nums,0,(k%nums.length)-1);
        revers(nums,(k%nums.length),nums.length-1);
    }

    public void revers(int[] nums,int l,int r){
        while(l

5.数组的区间

算法通关村第3关【白银】| 双指针思想_第5张图片

 思路:题目的意思是说不连续递增的就断开输出

双指针,slow指向区间起始,fast负责寻找到区间结束

class Solution {
    public List summaryRanges(int[] nums) {
        List res = new ArrayList<>();
        int slow = 0;
        int fast = 0;
        while(fast" + nums[fast]);
                }
                slow = fast+1;
            }
            fast++;
        }
        return res;
    }
}

你可能感兴趣的:(算法,数据结构,算法)