数组中的双指针

在之前我们讨论过了双指针在链表中的妙用,今天我们来更进一步的探究一下双指针在数组中有那些巧妙的用法吧!

我们先回忆一下链表中双指针是如何使用的,我们可以通过定义快慢指针,一个一次走一格,一个一次走两格来找到中间节点,还可以通过控制快慢指针的差距来获得某个节点,例如获取倒数第K个节点。而今天我们来讨论双指针在数组中的另一个用法,根据题干条件来控制slow节点的运动,这种用法会更灵活,没有之间限定死slow和fast的关系,而时再遍历中通过是否达成if条件来决定slow是否移动。

原地移除所有数值等于val的元素

例子1:
输入:nums=[3,2,2,3],va1=3
输出:2,nums=[2,2]
例子2:
输入:nums=[0,1,2,2,3,0,4,2],va1=2
输出:5,nums=[0,1,3,0,4]

题干内容很简单,我们先看代码在来一步步分析:

public 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;
}

在数组中删除元素不同于链表,我们要用每一个数值依次覆盖他前一个数值。题干中fast指针起到遍历数组的作用,将slow的起始值等于0,而移动的条件是当前遍历的值不等于val。由于我们要删除值为val的节点,通过遍历过程就会发现当前节点为待删除节点时,slow会停在删除节点,直到fast移动到非删除节点时才进行覆盖操作。

 理解了这原理后续我们会反复用到这个操作。

删除有序数组中的重复项

和上一题一样都是删除操作,我们来简单的分析一下:由于我们要删除掉重复的项,此时肯定要两两对比。这里我想让slow=0,fast从1开始,这样遍历时对比nums[slow]和nums[fast]的值即可判断是否是重复项。如果不重复肯定要让slow移动继续比较呀,显然slow的移动条件就是nums[slow]!=nums[fast]。好!主体运行框架已经分析好了,若nums[slow]!=nums[fast]时slow和fast一起移动,否则只有fast移动,直到nums[slow]再次不等于nums[fast]时停止,此时就是覆盖操作了。用于覆盖操作的条件也是nums[slow]!=nums[fast],所以在这里覆盖操作要同时确保slow和fast一起移动时不会破坏原先结构,同时在覆盖重复项时不会出错。

public int removeDuplicates(int[] nums) {
	int slow =0;
	for(int fast =1;fast<nums.length;fast++){
		if(nums[slow]!=nums[fast]){
			slow++;
			nums[slow]=nums[fast];
		}
	}
	return slow+1;
}

这里我的操作是先将slow++(先移动了slow)此时slow=fast的,再执行nums[slow]=nums[fast]时相当于将自身赋值给自身,保证了slow和fast一起移动时不会破坏原先结构。同时在覆盖时由于slow先移动,fast覆盖了下一项,保证了连续的重复节点只会保留一个。

这道题还可进行拓展,我可以删除重复的节点并不保留重复项,或者保留两个重复项时又该怎么做?(这里展示保留两重复项的代码)

public int removeDuplicates(int[] nums) {
	int slow = 0;
	for(int fast=2;fast<nums.length;fast++){
		if(nums[slow]!=nums[fast]){
			slow++;
			nums[slow+1]=nums[fast];
		}
	}
	return slow+2;
}

元素的奇偶移动

 同样这个问题也可以用双指针解决。将slow停在奇数项,fast找最近的偶数项。两者值互换即可。

public int[] sortArrayByParity(int[] nums) {
	int slow=0; 
	for (int fast=0;fast<nums.length;fast++){
		if(nums[fast]%2==0){
			//交换两者的值
			int temp = nums[slow];
			nums[slow]=nums[fast];
			nums[fast] = temp;
			slow++;
		}
	}
	return nums;
}

求数组的区间

同样类似的有求数组的区间问题:

简单理解就是求一个有序数组中递增值为1的区间即数组中[0,1,2,4,5,7]中有区间[0,1,2],[4,5]和[7]。

分析:同样将slow停在区间开始的位置,fast找到区间结束的位置。当nums[fast]+1!=nums[fast+1]时slow再移动。

public List<String> summaryRanges(int[] nums) {
	List<String> res=new ArrayList<>();
	int slow =0;
	for(int fast=0;fast<nums.length;fast++){
		if(fast+1==nums.length || nums[fast]+1!=nums[fast+1]){
			StringBuilder sb=new StringBuilder();
			sb.append(nums[slow]);
			if(slow!=fast){
				sb.append("->").append(nums[fast]);
			}
			res.add(sb.toString());
			slow=fast+1;
		}
	}
	return res;
}

不知道大家通过这几个题是否掌握了这种双指针的用法,当我们需要通过某个条件删除或移动数组中的数据时都可以考虑到双指针的这种用法。同样的方法在链表中也使用,但要注意链表和数组之间的区别哦,希望这次的分享对大家有所帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值