双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多个数组的多个指针。
若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索。
若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。
对于 C++ 语言,指针还可以玩出很多新的花样。一些常见的关于指针的操作如下。
题解
代码
class Solution {
public:vector twoSum(vector& numbers, int target) {int n = numbers.size();int p = 0, q = n - 1;while(numbers[p] + numbers[q] != target){if(numbers[p] + numbers[q] < target) ++ p; else -- q;}return vector{p + 1, q + 1};}
};
收获
return vector{p + 1, q + 1};
题解
代码
class Solution {
public:void merge(vector& nums1, int m, vector& nums2, int n) {int end = m-- + n-- - 1;while(m>=0 && n>=0){nums1[end--] = nums1[m] >= nums2[n] ? nums1[m--] : nums2[n--];}while(n>=0){nums1[end--] = nums2[n--];}}
};
收获
题解
代码
/*** 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 *slow = head, *fast = head;do{if(!fast || !fast->next) return nullptr;slow = slow -> next;fast = fast -> next -> next;}while(fast != slow);// 如果存在环路,寻找环路的起始点fast = head;while(fast != slow){slow = slow -> next;fast = fast -> next;};return fast;}
};
收获
do{ } while();
题解
l
和 r
都是从最左端向最右端移动,且 l
的位置一定在 r
的左边或重合。ch
表示 t
中的字符数量,flag
表示每个字符是否在 t
中存在;s[r]
在 t
中存在,那么就使 --ch[s[r]]
,并判断此时 ch[s[r]]
是否仍然大于等于 0 ,如果是的话,cnt ++
,其中 cnt 统计已经包含的字符个数;t
覆盖,我们可以移动 l
指针,使得范围缩小。++l
,如果当前字符 s[l]
会在 t 中出现,那么说明左边界越界了,会破坏覆盖字符串 t
的条件。l
指针,且 l
指针只会从左到右移动一次,因此总时间复杂度仍然是 n 。代码
class Solution {
public:string minWindow(string s, string t) {vector ch(128, 0);vector flag(128, false);string ans = "";// 统计 t 中的字符个数for(char c : t){ch[c] ++;flag[c] = true;}// cnt保存已经匹配的字符个数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(--ch[s[r]] >= 0){cnt ++;}}while(cnt == t.size()){// 此时已经覆盖子串// 向右移动左边界if(r - l + 1 < min_size){// 保存能够覆盖子串的情况min_l = l;min_size = r - l + 1;}// s[l] 字符在子串里,cnt--,条件被破坏,不能向右移动了if(flag[s[l]] && ++ch[s[l]] > 0){-- cnt;}++ l;}}return min_size > s.size() ? "": s.substr(min_l, min_size);}
};
收获
题解
代码
class Solution {
public:bool judgeSquareSum(int c) {long left = 0, right = (long)sqrt(c);while(left <= right){long sum = left * left + right * right;if(sum == c) return true;if(sum < c) left ++;else right --; }return false;}
};
收获
题解
validPalindrome
来实现。对于删掉字符后的回文子串,由于 left 左边的字符串和 right 右边的字符串已经一一对应了,不需要再次判断,因此只需要考虑 s.substr(left, right - left)
和 s.substr(left + 1, right - left)
两个字符串是否能够形成回文子串。代码
class Solution {
public:int cnt = 0;bool validPalindrome(string s) {int left = 0, right = s.size() - 1;while(left <= right){if(s[left] == s[right]){left ++, right --;}else{cnt ++;if(cnt <= 1){return validPalindrome(s.substr(left, right - left)) || validPalindrome(s.substr(left + 1, right - left));}else return false; }}return true;}
};
收获
题解
代码
class Solution {
public:string findLongestWord(string s, vector& dictionary) {// 将 dictionary 各元素按照长度降序排序// 如果长度一致 按照字典序升序sort(dictionary.begin(), dictionary.end(), [](string s1, string s2){if(s1.size() == s2.size()) return s1 < s2; return s1.size() > s2.size();});for(string d : dictionary){int p = 0, q = 0;while(p < s.size() && q < d.size()){if(s[p] == d[q]){++p, ++q;}else ++p;}if(q == d.size()){return d;}}return "";}
};
收获
题目类型:
练习题 340 是VIP 题,没做;三道练习题都不算难,滑动窗口的题我比较不熟悉。