双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的 。
双指针是很多算法的基础,如归并排序、滑动窗口、字符匹配(左右括号匹配)
等。滑动窗口即维持一段区间。
在很多情况下,双指针能帮助我们找到空间或是时间复杂度更低的解。比单个指针来回移动的效率从o(n2)提高到o(n)。
算法框架
for(int i = 0, j = x; i < n; i++)
{
// step1: j范围有效 & 设计某个性质,使的j具有某种单调性
while(j < m && check(i, j)) jxx
// step2:这道题目的具体逻辑
// step3: 这里注意i,j的更新!!
}
可以参考滑动窗口类型集合题目一起学习,两者有重叠
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1 == nullptr)
return l2;
if(l2 == nullptr)
return l1;
ListNode* pTemp = new ListNode(0);
ListNode* pHead = pTemp;
while(l1 != nullptr && l2 != nullptr)
{
if(l1->val > l2->val)
{
pTemp->next = l2;
pTemp = l2;
l2 = l2->next;
}
else
{
pTemp->next = l1;
pTemp = l1;
l1 = l1->next;
}
}
if(l1 == nullptr)
pTemp->next = l2;
if(l2 == nullptr)
pTemp->next = l1;
return pHead->next;
}
};
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(!head)
return nullptr;
ListNode* pre = head;
ListNode* cur = head->next;
while(cur)
{
if(cur->val != head->val)
{
head->next = cur;
head = cur;
}
cur = cur->next;
}
if(head->next)
head->next = nullptr;
return pre;
}
};
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,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。
你不需要考虑数组中超出新长度后面的元素。
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
if(nums.empty())
return 0;
int index = 0;
int count = 1;
for(int i = 1; i< nums.size();i++)
{
if(nums[index] != nums[i])
{
nums[++index] = nums[i];
count++;
}
}
nums.resize(count);
return count;
}
};
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
/**
* 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)
return false;
ListNode* slow = head;
ListNode* fast = head;
while(slow && fast)
{
slow = slow->next;
fast = fast->next;
if(fast)
fast = fast->next;
else
return false;
if(fast == slow)
return true;
}
return false;
}
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
**说明:**不允许修改给定的链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
/**
* 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)
return nullptr;
ListNode* slow = head;
ListNode* fast = head;
while(slow && fast)
{
slow = slow->next;
fast = fast->next;
if(fast)
fast =fast->next;
else
break;
if(fast == slow)
break;
}
if(!fast)
return nullptr;
slow = head;
while(slow && fast && slow != fast)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明:
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。
class Solution {
public:
void moveZeroes(vector<int>& nums) {
if(nums.empty())
return ;
int index = 0;
for(int i = 0; i< nums.size();i++)
{
if(nums[i] != 0)
{
nums[index++] = nums[i];
}
}
for(;index < nums.size();index++)
nums[index] = 0;
}
};
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
for(int i=m,j=0;i<m+n ;i++)
nums1[i] = nums2[j++];
vector<int> vec(m + n,0);
int l = 0;
int r = m;
int cnt = 0;
while(l < m && r < m + n)
{
vec[cnt++] = nums1[l] < nums1[r] ? nums1[l++] : nums1[r++];
}
while(l < m)
vec[cnt++] = nums1[l++];
while(r < m + n)
vec[cnt++] = nums1[r++];
copy(vec.begin(),vec.end(),nums1.begin());
}
};
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
class Solution {
public:
vector<vector<int> > threeSum(vector<int>& nums) {
vector<vector<int> > ret;
vector<int > vtemp;
int len = nums.size();
sort(nums.begin(),nums.end());//sort the input
for(int i=0;i<len-2;i++){
//find the tripe for each nums[i]
// j1 and j2 log the index of the other two numbers
if(i ==0 ||(i>0 && nums[i] != nums[i-1])){
int p1 = i+1, p2 = len-1; // set two pointers
while(p1 < p2){
if(nums[p1] + nums[p2] < -nums[i]){
p1++;
}else if(nums[p1] + nums[p2] == -nums[i]){
if(p1 == i+1){
vector<int > vtemp{nums[i], nums[p1], nums[p2]};
ret.push_back(vtemp);
vtemp.clear();
}else if(nums[p1] != nums[p1-1]){
vector<int > vtemp{nums[i], nums[p1], nums[p2]};
ret.push_back(vtemp);
vtemp.clear();
}
p1++,p2--;
}else{
p2--;
}
}
}
}
return ret;
}
};
9、盛水最多的容器 --困难
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
class Solution {
public:
int maxArea(vector<int>& height) {
if(height.empty())
return -1;
int maxval = INT_MIN;
int left = 0;
int right = height.size() -1;
int temp;
while(left < right )
{
if(height[left] > height [right])
{
temp = height[right] * (right - left);
right--;
}
else
{
temp = height[left] * (right - left);
left++;
}
if(maxval < temp)
maxval = temp;
}
return maxval;
}
};