1. 算法思想
- 通过指针位置和移动规则,将问题转化为区间扫描或状态转移。
- 利用有序性(如排序数组)或问题特性(如单调性)减少不必要的遍历。
2. 例题
2.1 移动零
void moveZeroes(vector<int>& nums) {
int left = 0;
int right = 1;
while(left < nums.size() && right < nums.size()){
if(nums[left] == 0){
while(!nums[right] && right < nums.size() - 1) ++right;
swap(nums[left], nums[right]);
}
++left;
++right;
}
}
2.2 复写零
void duplicateZeros(vector<int>& arr) {
int left = 0;
while(left < arr.size() - 1)
{
if(arr[left] == 0)
{
int right = arr.size() - 1;
while(right > left + 1)
{
arr[right] = arr[right - 1];
--right;
}
arr[left + 1] = 0;
++left;
}
++left;
}
}
2.3 快乐数
bool isHappy(int n) {
int ret = n;
unordered_set<int> myset;
while(ret != 1 && myset.count(ret) == 0)
{
myset.insert(ret);
int add = 0;
while(ret != 0)
{
int a = ret % 10;
ret /= 10;
add += a * a;
}
ret = add;
}
if(ret == 1) return true;
return false;
}
2.4 盛最多水的容器
//取双指针从槽两端开始向内移动短板,因为短板的高度
//和长短板的距离决定容器盛水面积,移动长板的话容器的高度
//不变或者减小,两板之间的距离会减少一格,容器的面积必然减小。
int maxArea(vector<int>& height) {
int i = 0, j = height.size() - 1, res = 0;
while(i < j) {
res = height[i] < height[j] ?
max(res, (j - i) * height[i++]):
max(res, (j - i) * height[j--]);
}
return res;
}
2.5 有效三角形个数
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;
while(right > left)
{
//可以组成三角形的条件是:两条小边之和大于最大边
//改变两条小边
//符合条件则减小两条小边之和即right向左移动
//不符合条件则增大两条小边之和即left向右移动
if(nums[left] + nums[right] > nums[i])
{
ret += right - left;
--right;
}
else
{
++left;
}
}
}
return ret;
}
2.6 和为s的两个数
LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)
//取双指针从数组price的两端开始
//双指针相加比target大,right左移
//双指针相加比target小,left右移
vector<int> twoSum(vector<int>& price, int target) {
int left = 0, right = price.size() - 1;
vector<int> ret;
while(right > left)
{
if(price[right] + price[left] == target)
{
ret.push_back(price[left]);
ret.push_back(price[right]);
break;
}
else if(price[right] + price[left] > target) --right;
else ++left;
}
return ret;
}
2.7 三数之和
// 1.排序
// 2.固定一个数a
// 3.用“双指针算法”快速找到两个数,使其的相加等于-a
// 即可判断三数之和等于0
// 4.去重:
// (1)遍历基准值a时,跳过与前一个元素相同的值
// (2)找到有效解后,左右指针需跳过所有重复元素
// (3)双指针移动过程中,同样跳过相邻的重复元素
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> ret;
for (int i = nums.size() - 1; i >= 2; --i)
{
while (i < nums.size() - 1 && i >= 2 && nums[i + 1] == nums[i])
--i;
if (i < 2 || nums[i] < 0) break;
int left = 0, right = i - 1;
while (right > left)
{
if (nums[left] + nums[right] == nums[i] * -1)
{
ret.push_back({ nums[left], nums[right], nums[i] });
--right;
++left;
while (right > left && nums[right + 1] == nums[right])
--right;
while (left > 0 && right > left && nums[left - 1] == nums[right])
++left;
}
else if (nums[left] + nums[right] > nums[i] * -1) --right;
else ++left;
}
}
return ret;
}
2.8 四数之和
//依次固定一个数a;
//在a后面的区间内再依次固定一个数b
//利用双指针在b后面的区间内利用“双指针”找到两个数c, d
//使得(long)a + b + c + d == target
//测试用例中有四数之和超过整形范围的情况所以需要转为long类型
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
vector<vector<int>> ret;
for (int i = 0; i < nums.size(); ++i)
{
for(int j = i + 1; j < nums.size(); ++j)
{
int left = j + 1, right = nums.size() - 1;
while (right > left)
{
if ((long)nums[left] + nums[right] + nums[i] + nums[j] == target)
{
ret.push_back({ nums[i], nums[j], nums[left], nums[right] });
--right;
++left;
while (right > left && nums[right + 1] == nums[right])
--right;
while (left > 0 && right > left && nums[left - 1] == nums[right])
++left;
}
else if ((long)nums[left] + nums[right] + nums[i] + nums[j] > target) --right;
else ++left;
}
//跳过相同的数
while(j < nums.size() - 1 && nums[j] == nums[j + 1]) ++j;
}
//跳过相同的数
while(i < nums.size() - 1 && nums[i] == nums[i + 1]) ++i;
}
return ret;
}