作者简介:非科班在读,纯技术博客分享,致力于C/C++,涉及Python、C/C++、Linux,数据结构,git企业级开发,Mysql等。
本文收录于算法指南,旨在帮助读者应对各大互联网大厂笔试题,构建完整的算法体系!
相关专栏C语言、C++、数据结构、Linux、前言科技喜欢C/C++/Linux/算法的朋友们可以关注一下哦!
————————————————
版权声明:本文为CSDN博主「小宇成长录」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://editor.csdn.net/md/?not_checkout=1&spm=1011.2124.3001.6183
常⻅的双指针有两种形式,⼀种是对撞指针,⼀种是左右指针。
对撞指针:⼀般⽤于顺序结构中,也称左右指针。
。对撞指针从两端向中间移动。⼀个指针从最左端开始,另⼀个从最右端开始,然后逐渐往中间逼近。
。对撞指针的终⽌条件⼀般是两个指针相遇或者错开(也可能在循环内部找到结果直接跳出循环),也就是:
◦ left == right (两个指针指向同⼀个位置)
◦ left > right (两个指针错开)
快慢指针:⼜称为⻳兔赛跑算法,其基本思想就是使⽤两个移动速度不同的指针在数组或链表等序列结构上移动。
这种⽅法对于处理环形链表或数组⾮常有⽤。其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使⽤快慢指针的思想。
快慢指针的实现⽅式有很多种,最常⽤的⼀种就是:
。 在⼀次循环中,每次让慢的指针向后移动⼀位,⽽快的指针往后移动两位,实现⼀快⼀慢。
话不多说,现在来看看大厂们比较有代表性的面试题吧!
先将数组排序。
我们可以固定⼀个「最⻓边」,然后在⽐这条边⼩的有序数组中找出⼀个⼆元组,使这个⼆元组之和⼤于这个最⻓边。由于数组是有序的,我们可以利⽤「对撞指针」来优化。
class Solution {
public:
int triangleNumber(vector<int>& nums) {
sort(nums.begin(), nums.end());//排序
int ret=0;
for(int i=nums.size()-1; i>=2; i--)
{
int left=0, right=i-1, target=nums[i];
while(left<right)
{
if(nums[left] + nums[right] > target)
{
ret += right-left;
right--;
}
else
{
left++;
}
}
}
return ret;
}
};
(https://leetcode.cn/problems/he-wei-sde-liang-ge-shu-zi-lcof/)
注意到本题是升序的数组,因此可以⽤「对撞指针」优化时间复杂度。
算法流程如下:
1.当 nums[left] + nums[right] == target 时,说明找到结果,记录结果,并且返回;
2.当 nums[left] + nums[right] < target 时:
当 nums[left] + nums[right] > target 时,同理我们可以舍去nums[right] (最⼩的数都满⾜不了你,你也没救了)。让 right-- ,继续⽐较下⼀组数据,⽽ left 指针不变(因为他还是可以去匹配⽐ nums[right] 更⼩的数)。
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target) {
int left=0, right=price.size()-1;
while(left<right)
{
if(price[left] + price[right] > target)
right--;
else if(price[left] + price[right] < target)
left++;
else
return {price[left], price[right]};
}
return {-1, -1};
}
};
本题与两数之和类似,是⾮常经典的⾯试题。
与两数之和稍微不同的是,题⽬中要求找到所有「不重复」的三元组。那我们可以利⽤在两数之和那⾥⽤的双指针思想,来对我们的暴⼒枚举做优化:
但是要注意的是,这道题⾥⾯需要有「去重」操作~
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ret;
//1. 排序
sort(nums.begin(), nums.end());
//2. 双指针思路
for(int i=nums.size()-1; i>=2; i--)
{
if(nums[i]<0)
break;
int left =0, right=i-1;
while(left<right)
{
if(nums[left] + nums[right] + nums[i] > 0)
right--;
else if(nums[left] + nums[right] + nums[i] < 0)
left++;
else
{
//相等,记录数据;处理相同元素
ret.push_back({nums[left], nums[right], nums[i]});
left++, right--;
//处理相同元素
while(left<right && nums[left-1] == nums[left])
{
left++;
}
while(left<right && nums[right+1] == nums[right])
{
right--;
}
}
//处理相同元素,防止重叠
while(i >=2 && nums[i] == nums[i-1])
{
i--;
}
}
}
return ret;
}
};
a. 依次固定⼀个数 a ;
b. 在这个数 a 的后⾯区间上,利⽤「三数之和」找到三个数,使这三个数的和等于 target - a 即可。
lass Solution
{
public:
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
vector<vector<int>> ret;
// 1. 排序
sort(nums.begin(), nums.end());
// 2. 利⽤双指针解决问题
int n = nums.size();
for(int i = 0; i < n; ) // 固定数 a
{
// 利⽤ 三数之和
for(int j = i + 1; j < n; ) // 固定数 b
{
// 双指针
int left = j + 1, right = n - 1;
long long aim = (long long)target - nums[i] - nums[j];
while(left < right)
{
int sum = nums[left] + nums[right];
if(sum < aim)
left++;
else if(sum > aim)
right--;
else
{
ret.push_back({nums[i], nums[j], nums[left++], nums[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 ret;
}
};