算法通关村第三关白银挑战——双指针思想解决删除数组元素和元素奇偶移动问题

大家好,我是怒码少年小码。

使用数组时常常会遇到大量移动元素的操作,例如删除和插入,这就很让人头疼了,那么我们该怎么解决呢?这里介绍一个方法——双指针思想

双指针思想

所谓双指针其实就是两个变量,不一定真的是指针。常见的有快慢指针、相撞型指针、背向型指针等等。常用于处理数组、字符串等问题。下面举两个应用实例你就会爱上它了😎。

删除元素专题

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

LeetCode 27: 给一个数组nums和一个值val,原地移除所有等于val的元素,并返回新数组的长度。要求:仅使用O(1)额外空间并原地修改输入数组。元素的顺序可以改变。

示例1:

  • 输入:nums=[3,2,2,3],val=3
  • 输出:2,nums=[2,2]

示例2:

  • 输入:nums=[0,1,2,2,3,0,4,2],val=2
  • 输出:5,nums=[0,1,4,03]
方法一:快慢指针

基本思想就是定义连个指针slow和fast,初始值为0,两指针都从第一个元素开始。用slow来记录有效的不需要删除的部分,fast表示当前要访问的元素。

fast不断向后移动:

  1. 如果nums[fast]!=val,则当前元素是要保存的元素,把nums[fast]移动到nums[slow],然后slow++(更新下一个要保存的元素的位置)和fast++(继续遍历)
  2. 如果nums[fast]==val,则当前元素是要删除的元素,于是继续fast++往后走。

int removeElement(int nums[],int size, int val) {
	int slow = 0;
	for (int fast = 0; fast < size; fast++) {
		if (nums[fast] != val) {
			nums[slow] = nums[fast];
			slow++;
		}
	}
	//输出检测
	cout << slow << endl;
	for (int i = 0; i < slow; i++) {
		cout << nums[i] <<",";
	}
	cout << endl;
	return slow;
}

最后slow的值恰好和数组长度相同,妙啊😊!

方法二:对撞指针

也叫交换移除,基本思想是从右侧找到不是val的值来顶替左侧是val的值。什么意思?看图:

int removeElement02(int nums[],int size, int val) {
	int right = size - 1;
	int left = 0;
	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--;
		}
	}

	//输出检测
	cout << left << endl;
	for (int i = 0; i < left; i++) {
		cout << nums[i] << ",";
	}
	cout << endl;
	return left;
}

首先定义两个指针leftright位于数组的首位。left用于记录不用删除的新数组元素,如果遇到要删除的元素就从右边找到不用删除的元素覆盖,否则就left++继续走,所以这里的left++是有条件的⭐。left==right循环结束后,left 的值即为新数组的长度。

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

LeetCode 26 : 给一个有序数组nums,请原地删除重复出现的元素,使得每个元素只出现一次,返回新数组的长度。

示例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]

这里还是用双指针最方便,一个fast用于负责遍历数组,一个slow用于指向下一个不重复元素应该要插入的位置。

int removeDuplicates(int nums[], int size) {
	int slow = 1;
	int fast ;
	for (fast = 1; fast < size; fast++) {
		if (nums[fast] != nums[slow - 1]) {
			nums[slow] = nums[fast];
			slow++;
		}
	}
	//输出检测
	cout << slow << endl;
	for (int i = 0; i < slow; i++) {
		cout << nums[i] << ",";
	}
	cout << endl;
	return slow;
}

为了减少不必要的操作把slow设置为1,从第二个元素开始,让nums[fast] nums[slow - 1]作比较, nums[slow - 1]表示已经插入的不重复的元素,只有和这个不一样才能插入。因为本题的背景是有序数组,所以不用担心,原数组相同元素不连续的问题

元素奇偶移动专题

LeetCode 905 : 按奇偶排序数组,给定一个非负整数数组nums,返回一个数组,这个数组中nums的所有偶数元素后跟着所有奇数元素。元素之间的顺序不做要求。

示例:

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

最简单的肯定是直接创建一个新数组,然后遍历根据条件放置元素啦。那么如果面试官要求空间复杂度为O(1),阁下又该如何应对😁?

答案还是双指针。维护两个头尾指针leftright,left从0开始检查当前元素是否为偶数,是就继续,不是就停下;right从size-1开始检查当前元素是否为奇数,是就继续,不是就停下;然后交换nums[left]nums[right]。直到left>=right

int* sortArrayParity(int* nums, int size) {
	int left = 0;
	int right = size - 1;
	while (left < right) {

		if (nums[left] % 2 == 1 && nums[right] % 2 == 0) {
			int temp = nums[left];
			nums[left] = nums[right];
			nums[right] = temp;
		}

		if (nums[left] % 2 == 0) {
			left++;
		}
		if (nums[right] % 2 == 1) {
			right--;
		}
	}
	//输出检测
	for (int i = 0; i < size; i++) {
		cout << nums[i] << ",";
	}
	cout << endl;

	return nums;
}

因为这里要返回的是一个数组,所以使用指针传值。

END

这篇下来我真的觉得双指针真香啊~😊

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值