赞
踩
双指针主要用于遍历数组,通过两个指针指向不同的元素,从而可以协同完成任务。也可以多个数组多个指针。
若两个指针遍历一个数组,遍历方向相同并且不会相交(两个指针包围的区域)称为滑动窗口,用于区间搜索。
若两个指针遍历一个数组,遍历方向相反,可以用来搜索,待搜索的数组往往是排序好的(例如:二分查找)
对于C++,指针和常量,指针函数和函数指针也是常见操作。
题目描述
在一个增序的整数数组里找到两个数,使它们的和为给定值。已知有且只有一对解。
输入输出样例
输入是一个数组(numbers)和一个给定值(target)。输出是两个数的位置,从 1 开始计数。
Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
在这个样例中,第一个数字(2)和第二个数字(7)的和等于给定值(9)。
题解
因为数组已经排好序,我们可以采用方向相反的双指针来寻找这两个数字,一个初始指向最小的元素,即数组最左边,向右遍历;一个初始指向最大的元素,即数组最右边,向左遍历。
如果两个指针指向元素的和等于给定值,那么它们就是我们要的结果。如果两个指针指向元素的和小于给定值,我们把左边的指针右移一位,使得当前的和增加一点。如果两个指针指向元素的和大于给定值,我们把右边的指针左移一位,使得当前的和减少一点。
证明
对于排好序且有解的数组,双指针一定能遍历到最优解。证明方法如下:假设最优解的两个数的位置分别是 l 和 r。我们假设在左指针在 l 左边的时候,右指针已经移动到了 r;此时两个指针指向值的和小于给定值,因此左指针会一直右移直到到达 l。同理,如果我们假设在右指针在 r 右边的时候,左指针已经移动到了 l;此时两个指针指向值的和大于给定值,因此右指针会一直左移直到到达 r。所以双指针在任何时候都不可能处于 (l,r) 之间,又因为不满足条件时指针必须移动一个,所以最终一定会收敛在 l 和 r。
代码
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int left=0,right=numbers.size()-1;
while(left<right){
int sum=numbers[left]+numbers[right];
if(sum==target) return {left+1,right+1};
else if(sum<target) left++;
else right--;
}
return {-1,-1};
}
};
题目描述
给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c 。
输入输出样例
Input: c=5
Output: true
1* 1+2* 2=5
代码
class Solution {
public:
bool judgeSquareSum(int c) {
int l=0,r=sqrt(c);
while(l<=r){
long long sum=(long long)l*l+(long long)r*r;
if(sum==c) return true;
else if(sum<c) l++;
else r--;
}
return false;
}
};
题目描述
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
输入输出样例
Input: s = "aba"
Output: true
代码
class Solution { public: bool check(const string& s,int low,int high){ for(int i=low,j=high;i<j;i++,j--){ if(s[i]!=s[j]) return false; } return true; } bool validPalindrome(string s) { int low=0,high=s.size()-1; while(low<high){ if(s[low]==s[high]){ low++; high--; } else return check(s,low,high-1)||check(s,low+1,high); } return true; } };
题目描述
给定两个有序数组,把两个数组合并为一个。
输入输出样例
输入是两个数组和它们分别的长度 m 和 n。其中第一个数组的长度被延长至 m + n,多出的
n 位被 0 填补。题目要求把第二个数组归并到第一个数组上,不需要开辟额外空间。
Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
Output: nums1 = [1,2,2,3,5,6]
题解
因为这两个数组已经排好序,我们可以把两个指针分别放在两个数组的末尾,即 nums1 的m − 1 位和 nums2 的 n − 1 位。每次将较大的那个数字复制到 nums1 的后边,然后向前移动一位。因为我们也要定位 nums1 的末尾,所以我们还需要第三个指针pos,以便复制。如果 nums1的数字已经复制完,把 nums2 的数字继续复制;如果 nums2 的数字已经复制完,剩余nums1 的数字不需要改变,因为它们已经被排好序。
本题用了一个逆向思维,从后向前遍历。
代码
class Solution { public: void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) { int pos=m+n-1; m--; n--; while(m>=0&&n>=0){ if(nums1[m]>=nums2[n]){ nums1[pos]=nums1[m]; --m; } else{ nums1[pos]=nums2[n]; n--; } --pos; } while(n>=0){ nums1[pos]=nums2[n]; pos--; n--; } } };
题目描述
给你一个字符串 s 和一个字符串数组 dictionary ,找出并返回 dictionary 中最长的字符串,该字符串可以通过删除 s 中的某些字符得到。
输入输出样例
Input: s = "abpcplea", dictionary = ["ale","apple","monkey","plea"]
Onput: "apple"
代码
class Solution { public: string findLongestWord(string s, vector<string>& dictionary) { string res=""; for(string& t:dictionary){ int i=0,j=0; while(i<t.size()&&j<s.size()){ if(t[i]==s[j]){ ++i; } ++j; } if(i==t.size()){ if(t.size()>res.size()||(t.size()==res.size()&&t<res)){ res=t; } } } return res; } };
题目描述
给定一个链表,如果有环路,找出环路的开始点。
输入输出样例
输入是一个链表,输出是链表的一个节点。如果没有环路,返回一个空指针。
题解
给定两个指针,分别为fast和slow,从起始位置开始,fast每次前进两步,slow每次前进一步。如果fast为空或者next为空,则说明没有环路。否则fast会一直走下去,说明有环路,两者一定会相遇。当fast和slow相遇时,再将fast重新移动到head,并让fast和slow每次都前进一步,当两者再次相遇时,相遇节点即为环路开始点。
证明
代码
/** * 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) { ListNode* fast=head; ListNode* slow=head; while(true){ if(fast==nullptr||fast->next==nullptr) return nullptr; fast=fast->next->next; slow=slow->next; if(fast==slow) break; } fast=head; //fast重新移动到head while(fast!=slow){ fast=fast->next; slow=slow->next; } return fast; } };
题目描述
给定两个字符串 S 和 T,求 S 中包含 T 所有字符的最短连续子字符串的长度,同时要求时间
复杂度不得超过 O(n)。
输入输出样例
输入是两个字符串 S 和 T,输出是一个 S 字符串的子串。
Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
在这个样例中,S 中同时包含一个 A、一个 B、一个 C 的最短子字符串是“BANC”。
题解
代码
class Solution { public: string minWindow(string s, string t) { vector<int> chars(128, 0); vector<bool> flag(128, false); // 先统计t中的字符情况 for(int i = 0; i < t.size(); ++i) { flag[t[i]] = true; ++chars[t[i]]; } // 移动滑动窗口,不断更改统计数据 int cnt = 0, l = 0, min_l = 0, min_size = s.size() + 1; for (int r = 0; r < s.size(); ++r) { if (flag[s[r]]) { if (--chars[s[r]] >= 0) { ++cnt; } // 若目前滑动窗口已包含t中全部字符, // 则尝试将l右移,在不影响结果的情况下获得最短子字符串 while (cnt == t.size()) { if (r - l + 1 < min_size) { min_l = l; min_size = r - l + 1; } //如果l位置的字符是t中的,数量加1,总数减1 if (flag[s[l]] && ++chars[s[l]] > 0) { --cnt; } ++l; } } } return min_size > s.size()? "": s.substr(min_l, min_size); } };
注:
substr 复制字符串,从指定位置开始,并有指定长度
string a = s.substr(0,5); //获得字符串s中从第0位开始的长度为5的字符串
++i>0 该语句先进行++运算,再进行比较运算
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。