原理:将数组进行区间划分,通过指针(下标)的移动实现题目所要求的区间(数组分块)
(实现代码统一是C++)
建议在做题与看题解时要自己反复模拟这个实现的过程,以后在做题做到类似的题才能举一反三!
一移动零
链接:283. 移动零 - 力扣(LeetCode)
思路:将要实现的区间用指针划为不同的区间(非0区间与0区间)再进行指针的搜索。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int cur=-1,des=0;//cur固定,des去进行数组的扫描,即找非0元素
//最后:[0,cur]为非0元素[cur+1,des]为0元素
while(des<nums.size())
{
if(nums[des]!=0)
{
swap(nums[cur+1],nums[des]);
cur++;
}
des++;
}
}
};
二复写零
链接:1089. 复写零 - 力扣(LeetCode)
思路:
确定目标位置:首先,使用两个指针 dest 和 cur 来确定每个位置复制0后应该放置的位置。cur 指针用于遍历原始数组,而 dest 指针则用于确定在复制0后应该放置新元素的位置。
cur 最终的位置就是复写后数组的最后一个数的位置。
再从后往前进行数组值的覆盖(从前向后遍历会导致数组后面元素丢失)
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int cur=0,des=-1,n=arr.size();
while(des<n)
{
//arr[cur+1]==0...如果在cur=0位置上是0呢?
if(arr[cur]==0)
{
des+=2;
}
else
{
des++;
}
if(des>=n-1)
{
break;
}
cur++;
}
//[1,0,2,3,0,4]des走到n位置越界
if(des>n-1)
{
//直接让des在数组的最后一个位置上并把它赋为0(越界情况一定是在复写0的时候越界的!)
des=n-1;
arr[des--]=0;
cur--;
}
//cur的位置是要实现的数组的最终位置
while(cur>=0)
{
//(cur的位置遇到0就让des进行复写0的操作)
if(arr[cur]==0)
{
arr[des--]=0;
arr[des--]=0;
cur--;
}
//从后向左进行值的覆盖
else
{
arr[des--]=arr[cur--];
}
}
}
};
三快乐数
链接:202. 快乐数 - 力扣(LeetCode)
思路:由于鸽巢原理,最后必定会有两个数相等,形成闭环即:在某一时刻,只有两种情况:要么有数字等于1形成闭环,要么会有两个数是相等的!
那么我们可以使用在环形链表中一样使用快慢双指针来进行解答
class Solution {
public:
//函数实现每一位数平方和相加
int bitsum(int n)
{
int sum=0;
while(n)
{
sum+=pow(n%10,2);
n/=10;
}
return sum;
}
//类似环形链表首先想到快慢双指针
//不管最后有没有结果等于1,都一定会有两个数相等形成闭环(重复循环)
//slow与fast一定在某个时刻相等
bool isHappy(int n)
{
int slow=n,fast=n;
while(slow!=1)
{
slow=bitsum(slow);
fast=bitsum(bitsum(fast));
if(slow==fast)
{
break;
}
}
return slow==1;
}
};
四盛最多水的容器
oj链接:11. 盛最多水的容器 - 力扣(LeetCode)
思路:使用两个指针,一个在最左一个在最右,算出结果进行调整移动时,要移动两个中最小的那一个(短板效应),在各种结果中找出最大的容器即为题目所求
class Solution {
public:
int maxArea(vector<int>& height) {
//ret存储的是最多容纳的水
int left=0,right=height.size()-1,ret=INT_MIN;
while(left<right)
{
//更新ret的值始终保持是所有盛水容器当中最大的
ret=max(ret,(right-left)*min(height[left],height[right]));
//要去移动最小的高
//如果是移到最大的:距离在减小(高度是按最小的来算的),结果只会是减不会增
if(height[left]<height[right])
{
left++;
}
else
{
right--;
}
}
return ret;
}
};
五有效三角形的个数
oj链接:611. 有效三角形的个数 - 力扣(LeetCode)
思路:先对数组进行排升序,排序后要先固定最大的那个值,然后就可以用双指针算法来解决问题:(与上面的思路大致相同)
class Solution {
public:
int triangleNumber(vector<int>& nums) {
//先将数组进行排升序
sort(nums.begin(),nums.end());
int count=0,left=0,right=0;
//固定最后一个数(最大的数nums[i])
for(int i=nums.size()-1;i>=0;i--)
{
//一定left,最大的数左边right进行移动
left=0,right=i-1;
while(left<right)
{
//两个数相加<=最大的数则要移动left使得和变大
if(nums[left]+nums[right]<=nums[i])
{
left++;
}
else
{
//left向后无论怎么移动,和一定>nums[i]即满足三角形任意两者之和大于第三边
count+=right-left;
right--;
}
}
}
return count;
}
};
六 寻找总价格为目标值的两个商品
oj链接:LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)
思路与上面大体一致,比较简单这里就不写了
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target) {
vector<int> ans;
int left=0,right=price.size()-1;
while(left<right)
{
if(price[left]+price[right]<target)
{
left++;
}
else if(price[left]+price[right]>target)
{
right--;
}
else
{
ans.push_back(price[left]);
ans.push_back(price[right]);
break;
}
}
return ans;
}
};
七三数之和
oj链接:LCR 007. 三数之和 - 力扣(LeetCode)
思路:与有效三角形的个数类似:先固定一个数,再使用双指针进行移动找到:nums[left]+nums[right]==-固定数。但实现起来要考虑两个细节:
1不漏:在找到符合值后,两个指针不要‘停’,继续靠拢进行查找;
2去重:找到后要判断在接下来移动的时候是否会遇到重复数(遇到就直接跳过)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
//C++直接开辟二维数组
vector<vector<int>> ans;
int left=0,right=0,n=nums.size();
//固定最左边一个数,在利用二数先加的题的思路去实现
//但要考虑:1不漏 2去重
for(int i=0;i<n;)
{
if(nums[i]>0)
{
break;
}
left=i+1,right=n-1;
while(left<right)
{
if(nums[left]+nums[right]>-nums[i])
{
right--;
}
else if(nums[left]+nums[right]<-nums[i])
{
left++;
}
else
{
//C语言要开辟数组,这里直接push_back爽!!
ans.push_back({nums[left],nums[right],nums[i]});
//不漏
left++,right--;
//去重,判断while循环条件先在后面,否则会有越界风险
while(left<right&&nums[left]==nums[left-1])
{
left++;
}
while(left<right&&nums[right]==nums[right+1])
{
right--;
}
}
}
//去重
i++;
while(i<n&&nums[i]==nums[i-1])
{
i++;
}
}
return ans;
}
};
对比用C语言写的:要进行对二维数组的开辟,非常麻烦!
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
int cmp(const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes){
*returnSize=0;//初始化
//排序
qsort(nums,numsSize,sizeof(int),cmp);
//为规定的返回值创建类型相同的数组
int** ans=(int**)malloc(sizeof(int*)*numsSize*numsSize);
//返回数组的数组要malloc
*returnColumnSizes=(int*)malloc(sizeof(int)*numsSize*numsSize);
//参考两数之和利用双指针算法,但要考虑去重,数组越界问题
for(int i=0;i<numsSize;)
{
int begin=i+1,end=numsSize-1,target=nums[i];
//如果固定的值>0,那么三数相加就不可能=0(小优化)
if(target>0) break;
//在固定值后,在[begin,end]的区间内进行查找
while(begin<end)
{
if(nums[begin]+nums[end]==-target)
{
//二级数组ans中malloc存储三元数组的值(类似坐标的方式进行表达)
ans[*returnSize]=(int*)malloc(sizeof(int)*3);
//要返回数量的大小
(*returnColumnSizes)[*returnSize]=3;
ans[*returnSize][0]=nums[begin];
ans[*returnSize][1]=nums[end];
ans[*returnSize][2]=target;
(*returnSize)++;
begin++;
end--;
//[0,0,0,0,0]
while(begin<end && nums[begin]==nums[begin-1]) begin++;
while(begin<end && nums[end]==nums[end+1]) end--;
}
else if(nums[begin]+nums[end]>-target) end--;
else if(nums[begin]+nums[end]<-target) begin++;
}
i++;
while(i<numsSize && nums[i]==nums[i-1]) i++;
}
return ans;
}
八四数之和
oj链接:18. 四数之和 - 力扣(LeetCode)
思路:与三数之和类似,多个固定的数和溢出问题的解决
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
vector<vector<int>> ans;
int left=0,right=0,n=nums.size(),sum=0;
for(int i=0;i<n;)
{
for(int j=i+1;j<n;)
{
left=j+1,right=n-1;
//处理数据溢出
long long aim=(long long)target-nums[i]-nums[j];
while(left<right)
{
sum=nums[left]+nums[right];
if(sum<aim)
{
left++;
}
else if(sum>aim)
{
right--;
}
else
{
ans.push_back({nums[left],nums[right],nums[i],nums[j]});
left++,right--;
while(left<right&&nums[left]==nums[left-1])
{
left++;
}
while(left<right&&nums[right]==nums[right+1])
{
right--;
}
}
}
j++;
while(j<n&&nums[j]==nums[j-1])
{
j++;
}
}
i++;
while(i<n&&nums[i]==nums[i-1])
{
i++;
}
}
return ans;
}
};