目录
对撞指针(左右指针)双指针移动
左右指针对于一个有序数组来说可以实现更复杂的数组相加数等操作。
两数之和
- 给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
- 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
给定 nums = [1,2,4,7,11,15] ,target = 9
nums[1] + nums[3] = 2+7 = 9 ,所以返回[1,3]
#include<stdio.h>
int main()
{
int numssize;
scanf("%d",&numssize);
int i,j;
j=0;
int nums[numssize];
for(i=0;i<numssize;i++)
{
scanf("%d",&nums[i]);
}
int target;
scanf("%d",&target);
int right=0;
int left=numssize-1;
//-----------------------------------------
while(right!=left) //算法实现
{
if(nums[right]+nums[left]>target) left--;
if(nums[right]+nums[left]<target) right++;
if(nums[right]+nums[left]==target) break;
}
printf("%d %d",right,left);
return 0;
}
三数之和
- 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
- 注意:答案中不可以包含重复的三元组
给定数组 nums = [-1, 0, 1, 2, -1, -4]
满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
排好序可得nums=[-4,-1,-1,0,1,2]
借了一下别人的图用来理解(水印如图右下方某乎所示)
#include<stdio.h> //用c语言的写法 用c++的sort
#include<algorithm> //sort函数
using namespace std;
int main()
{
int numssize;
scanf("%d",&numssize);
int nums[numssize];
int i;
for(i=0;i<numssize;i++)
{
scanf("%d",&nums[i]);
}
sort(nums,nums+numssize);
//-----------------------------------------------------------------
for(i=0;i<numssize;i++) //算法实现部分
{
if(nums[i]>0) return 0;
if(nums[i-1]==nums[i]) i++;
int left=i+1; //在i的下一位
int right=numssize-1; //末位
while(left<right)
{
if(nums[left-1]==nums[left] && left>i+1) //防止重复了
{
left++;
continue;
}
if(nums[i]+nums[right]+nums[left]<0) left++;
else if(nums[i]+nums[right]+nums[left]>0) right--;
else if(nums[i]+nums[right]+nums[left]==0 )
{
printf("%d %d %d\n",nums[i],nums[left],nums[right]);
left++;
right--; //注意右指针也要移动,等于之后如果只有左指针移动是无法满足条件的
}
}
}
return 0;
}
同向双指针移动
同向指针可以实现数组移除等操作
首先对于合并两个有序数组我们的思路有两种方式:
1.用一个新数组将他们直接存进去,再排序
2.在题目说nums1数组空间足够大的情况下,我们直接操作nums1数组
那么我们采用第二种方式,如果先存后排序,排序算法会增加其复杂度,我们可以利用双指针的方法,边移动边排序。
题目里提到是递增顺序,那么我们从尾部开始排,大的数就排到n+m-1的位置,因为是对nums1操作,假设nums1大于nums2的对应元素,就先让最后一个元素等于nums1的对应元素,让nums1的指针移动,因为这个元素排完了所以前移。
合并两个有序数组
给你两个按 非递减顺序 排列的整数数组 nums1
和 nums2
,另有两个整数 m
和 n
,分别表示 nums1
和 nums2
中的元素数目。
请你 合并 nums2
到 nums1
中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1
中。为了应对这种情况,nums1
的初始长度为 m + n
,其中前 m
个元素表示应合并的元素,后 n
个元素为 0
,应忽略。nums2
的长度为 n
。
示例 1:
输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3 输出:[1,2,2,3,5,6] 解释:需要合并 [1,2,3] 和 [2,5,6] 。 合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。
示例 2:
输入:nums1 = [1], m = 1, nums2 = [], n = 0 输出:[1] 解释:需要合并 [1] 和 [] 。 合并结果是 [1] 。
示例 3:
输入:nums1 = [0], m = 0, nums2 = [1], n = 1 输出:[1] 解释:需要合并的数组是 [] 和 [1] 。 合并结果是 [1] 。 注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。
#include<stdio.h>
int main()
{
int m,n;
scanf("%d %d",&m,&n);
int k=m+n-1;
int nums1[m+n],nums2[n];
int i;
for(i=0;i<m;i++) scanf("%d",&nums1[i]);
for(i=0;i<n;i++) scanf("%d",&nums2[i]); //完成前面的输入数据
//---------------------------------------------------------------------
while(n>=0 || m>=0) //开始算法实现
{
if(nums1[m-1]>nums2[n-1])
{
nums1[m+n-1]=nums1[--m]; //如果nums1要大,让最后一个元素等于该元素
} //--m是顺便移动了m指针,节省了一步
else
nums1[m+n-1]=nums2[--n];
}
while(n>=0) nums1[m+n-1]=nums2[n--]; //最后可能会有一个指针没有遍历完
while(m>=0) nums1[n+m-1]=nums1[m--];
for(i=0;i<=k;i++)
{
printf("%d",nums1[i]);
}
return 0;
}
在一个数组中,我们要移除相应的元素,则:
1.可以用一个指针遍历,一个指针停留在要操作的数上,
2.对这题来说,当指向的这个数不是val的数时,我们让操作数的指针移动。如果是的话该指针不移动,然后让指向的这个数等于另一个指针指向的数
移除元素
给你一个数组 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],也会被视作正确答案。
示例 2:
输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,3,0,4] 解释:函数应该返回新的长度 5 , 并且 nums 中的前五个元素为 0 , 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
#include<stdio.h>
int main()
{
int numssize;
int val;
scanf("%d",&numssize);
int i,j;
j=0;
int p=0;
int nums[numssize];
scanf("%d",&val);
for(i=0;i<numssize;i++)
{
scanf("%d",&nums[i]);
} //输入数据部分
//------------------------------------------------------------
for(i=0;i<numssize;i++) //算法部分
{
if(nums[i]!=val)
{
nums[j]=nums[i]; //nums[j++]=nums[i]同理
j++;
}
else
p++; //用来计数的
}
printf("函数返回的新长度为%d\n",numssize-p);
printf("nums数组里面的值有:");
for(i=0;i<numssize-p;i++)
{
printf("%d",nums[i]);
}
return 0;
}
删除数组的重复项其实和上一题有异曲同工之处,都是一个指针遍历,另外一个指针停留在操作数上,为了让后遍历的数覆盖操作数。
由于是删除重复的数,让一个指针初始值为0,另一个初始值为1;
让遍历的指针(i)初始值为1,然后开始比较,不相同的时候才移动j指针(操作数指针),然后指针是一直移动的,当移动到不同的数的时候,就可以替换了,如:0 0 1 2,j指针还在nums[0],i指针移动到1了,让nums[++j]=nums[i]即可。
删除有序数组中的重复项
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。 - 返回
k
。
示例 1:
输入:nums = [1,1,2] 输出:2, nums = [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] 解释:函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
#include<stdio.h>
int main()
{
int numssize;
scanf("%d",&numssize);
int i,j;
int nums[numssize];
for(i=0;i<numssize;i++)
{
scanf("%d",&nums[i]);
}
j=0;
//----------------------------------------
for(i=1;i<numssize;i++) //算法实现
{
if(nums[j]!=nums[i])
{
nums[++j]=nums[i];
}
}
for(i=0;i<j+1;i++)
{
printf("%d",nums[i]);
}
return 0;
}