双指针从广义上来说,是指用两个变量在线性结构上遍历而解决的问题。狭义上说,
对于数组,指两个变量在数组上相向移动解决的问题;
对于链表,指两个变量在链表上同向移动解决的问题,也称为「快慢指针」问题。
1.左右指针
模版
//左闭右开[left,right)
int left = 0, right = nums.size(), middle = left + (right-left)/2;
while(left < right){
if(nums[middle]>target)
right = middle;
else if(nums[middle]<target)
left = middle + 1;
else
return middle;
}
//左闭右闭[left,right]
int left = 0, right = nums.size()-1, middle = left + (right-left)/2;
while(left <= right){
if(nums[middle]>target)
right = middle-1;
else if(nums[middle]<target)
left = middle + 1;
else
return middle;
}
leetcode42
/*
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
*/
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
int left = 1, right = n-2;
int lmax = height[0], rmax = height[n-1];
int ans = 0;
while(left <= right){
if(lmax <= rmax){
ans += max(lmax-height[left],0);
lmax = max(lmax,height[left]);
left++;
}
else{
ans += max(rmax-height[right],0);
rmax = max(rmax,height[right]);
right--;
}
}
return ans;
}
};
leetcode41
/*
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
输入:nums = [3,4,-1,1]
输出:2
*/
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
int left=0, right = nums.size();
while(left < right){
if(nums[left] == left+1)
left++;
else if(nums[left]<=left||nums[left]>right||nums[nums[left]-1]==nums[left]){
right--;
swap(nums[left],nums[right]);
}
else
swap(nums[left],nums[nums[left]-1]);
}
return left + 1;
}
};
2.快慢指针
leetcode27
/*
给你一个数组nums和一个值val,你需要原地移除所有数值等于val的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(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],也会被视作正确答案
*/
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n = nums.size();
int fast=0, slow=0;
while(fast<n){
if(nums[fast] == val)
fast++;
else{
nums[slow]=nums[fast];
fast++;
slow++;
}
}
return slow;
}
};
leetcode283
/*
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
*/
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int n = nums.size();
int fast=0, slow=0;
while(fast<n){
if(nums[fast]==0)
fast++;
else{
nums[slow]=nums[fast];
fast++;
slow++;
}
}
for(int i = slow; i < n; i++)
nums[i] = 0;
}
};
leetcode287
/*
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
输入:nums = [1,3,4,2,2]
输出:2
*/
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int n = nums.size();
if(n<2)
return -1;
int fast = nums[nums[0]];
int slow = nums[0];
while(fast != slow){
fast = nums[nums[fast]];
slow = nums[slow];
}
fast = 0;//第一次相遇了,fast回到开头
while(fast != slow){
fast = nums[fast];
slow = nums[slow];
}
return fast;
}
};
leetcode136
/*
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
输入:nums = [4,1,2,1,2]
输出:4
*/
class Solution {
public:
int singleNumber(vector<int>& nums) {
int n = nums.size();
if(n<2)
return nums[0];
sort(nums.begin(),nums.end());
int fast = 1, slow = 0;
while(fast < n-1){
if(nums[fast] == nums[slow]){
fast += 2;
slow += 2;
}
else{
return nums[slow];
}
}
return nums[n-1];
}
};
leetcode141
/*
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL || head->next == NULL || head->next->next == NULL)
return false;
ListNode* fast = head->next->next;
ListNode* slow = head->next;
while(fast != slow){
if(fast->next == NULL || fast->next->next == NULL)
return false;
fast = fast->next->next;
slow = slow->next;
}
return true;
}
};
leetcode142
/*
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
*/
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head == NULL || head->next == NULL || head->next->next == NULL) {
return NULL;
}
ListNode* slow = head->next;
ListNode* fast = head->next->next;
while(fast != slow){
if(fast->next == NULL || fast->next->next == NULL)
return NULL;
fast = fast->next->next;
slow = slow->next;
}
fast = head;
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
参考:
https://leetcode.cn/tag/two-pointers/problemset/
https://www.bilibili.com/video/BV1V841167Rg/?spm_id_from=333.337.search-card.all.click&vd_source=f1c65f89fc3b6299ff0fcd42c1a8d57b