白银挑战——双指针思想以及应用

1.双指针思想

这里介绍一种简单但非常有效的方式——双指针。所谓的双指针其实就是两个变量,不一定真的是指针。双指针思想简单好用,在处理数组、字符串等场景下很常见。看个例子,从下面序列中删除重复元素[1,2,2,2,3,3,3,5,5,7,8],重复元素只保留一个。删除之后的结果应该为[1,2,3,5,7,8]。我们可以在删除第一个2时将将其后面的元素整体向前移动一次,删除第二个2时再将其后的元素整体向前移动一次,处理后面的3和5都一样的情况,这就导致我们需要执行5次大量移动才能完成,效率太低。如果使用双指针可以方便的解决这个问题,如图:
image.png
首先我们定义两个指针slow、fast。slow表示当前位置之前的元素都是不重复的,而fast则一直向后找,直到找到与slow位置不一样的 ,找到之后就将slow向后移动一个位置,并将arr[fast]复制给arr[slow],之后fast继续向后找,循环执行。找完之后slow以及之前的元素就都是单一的了。这样就可以只用一轮移动解决问题。
上面这种一个在前一个在后的方式也称为快慢指针,有些场景需要从两端向中间走,这种就称为对撞型指针或者相向指针,很多题目也会用到,我们接下来会看到很多相关的算法题。
还有一种比较少见的背向型,就是从中间向两边走。这三种类型其实非常简单,看的只是两个指针是一起向前走(相亲相爱一起走),还是从两头向中间走(冲破千难万险来爱你),还是从中间向两头走(缘分已尽,就此拜拜)。

2.移除元素

题目:

1.移除元素

给你一个数组 nums_ 和一个值 val,你需要 原地 移除所有数值等于 val _的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地修改输入数组
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2] 解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

解法:
1.快慢指针
定义两个指针,slow,fast,都从0开始遍历当fast指向的不为val就将其赋值给slow slow++

public static int removeElement(int[] nums, int val) {
        int slow = 0;
        for (int fast = 0; fast < nums.length; fast++) {
            if (nums[fast] != val) {
                   nums[slow] = nums[fast];
                   slow++;
            }
        }
        return slow;
    }

2.对撞指针
定义两个指针,left,right,left = 0,left 从头开始遍历,right从尾开始,但left为val时并且right不为val时进行交换,当right为val时right–,left不为val++
image.png

 public static int removeElement2(int[] nums, int val) {
        int left = 0, right = nums.length - 1;
        for (left = 0; left <= right;) {
            if (nums[left] == val && nums[right] != val) {
                int temp = nums[left];
                nums[left] = nums[right];
                nums[right] = temp;
            }
            if (nums[left] != val) {
                left++;
            }
            if (nums[right] == val) {
                right--;
            }
        }
        return left;
    }

也可以直接进行覆盖,当left为val时将right赋值给left,消除一个元素长度减1,right–,不为val时left++
image.png

 public static int removeElement(int[] nums, int val) {
        int left = 0, right = nums.length - 1;
        for (left = 0; left <= right ;) {
            if (nums[left] == val) {
                nums[left] = nums[right];
                right--;
            } else {
                left++;
            }
        }
        return right + 1;
    }
2.删除有序数组中的重复项

题目:
给你一个 升序排列 的数组 nums ,请你原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
示例 1:
输入:nums = [1,1,2] 输出:2, nums = [1,2,_] 解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
解法:
使用快慢指针,定义两个指针slow,fast,fast遍历数组,slow指向可以插入的部分,当fast指向的值不等slow将fast赋值给slow
image.png

public int removeDuplicates(int[] nums) {
        int slow = 1, fast = 0;
        for (fast = 0; fast < nums.length; fast++) {
            if (nums[fast] != nums[slow - 1]) {
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值