双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多个数组的多个指针。
若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索。
若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。
题目描述
输入输出样例
输入是一个数组(numbers)和一个给定值(target)。输出是两个数的位置,从 1 开始计数。
Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
题解
因为数组已经排好序,我们可以采用方向相反的双指针来寻找这两个数字,一个初始指向最小的元素,即数组最左边,向右遍历;一个初始指向最大的元素,即数组最右边,向左遍历。如果两个指针指向元素的和等于给定值,那么它们就是我们要的结果。
如果两个指针指向元素的和小于给定值,我们把左边的指针右移一位,使得当前的和增加一点。如果两个指针指向元素的和大于给定值,我们把右边的指针左移一位,使得当前的和减少一点。
vector twoSum(vector& numbers, int target) {
int l = 0, r = numbers.size() - 1, sum;
while (l < r) {
sum = numbers[l] + numbers[r];
if (sum == target) break;
if (sum < target) ++l;
else --r;
}
return vector{l + 1, r + 1};
}
class Solution {
public:
vector twoSum(vector& numbers, int target) {
unordered_map mm;
vector v;
for(int i = 0 ; i
题目描述
输入输出样例
输入是两个数组和它们分别的长度 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 的末尾,所以我们还需要第三个指针,以便复制。
在以下的代码里,我们直接利用 m 和 n 当作两个数组的指针,再额外创立一个 pos 指针,起始位置为 m +n−1。每次向前移动 m 或 n 的时候,也要向前移动 pos。这里需要注意,如果 nums1的数字已经复制完,不要忘记把 nums2 的数字继续复制;如果 nums2 的数字已经复制完,剩余nums1 的数字不需要改变,因为它们已经被排好序。
注意
这里我们使用了 ++ 和–的小技巧:a++ 和 ++a 都是将 a 加 1,但是 a++ 返回值为 a,而++a 返回值为 a+1。如果只是希望增加 a 的值,而不需要返回值,则推荐使用 ++a,其运行速度会略快一些
错误代码:
class Solution {
public:
void merge(vector& nums1, int m, vector& nums2, int n) {
int pos = m-- + n-- - 1;
//在这里 while(m>=0 && n>=0),m条件必须在前面,如果n条件在前,
//有一种错误情况,m初始就为0,导致第一个while循环和第二个循环都运行不了;
while(n >= 0 && m >= 0){
nums1[pos--] = nums1[m]>nums2[n]?nums1[m--]:nums2[n--];
}
while(m>=0){
nums1[pos--] = nums1[m--];
}
}
};
正确代码:
class Solution {
public:
void merge(vector& nums1, int m, vector& nums2, int n) {
/*
这个方法,太好解了,可能不是面试官想要的解法
for(int i = 0 ; i =0 && n>=0){
nums1[pos--] = nums1[m] > nums2[n] ? nums1[m--] : nums2[n--];
}
while(n >= 0){
nums1[pos--] = nums2[n--];
}
}
};
题目描述
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
题解
对于链表找环路的问题,有一个通用的解法——快慢指针(Floyd 判圈法)。给定两个指针,分别命名为 slow 和 fast,起始位置在链表的开头。每次 fast 前进两步,slow 前进一步。如果 fast可以走到尽头,那么说明没有环路;如果 fast 可以无限走下去,那么说明一定有环路,且一定存在一个时刻 slow 和 fast 相遇。
/**
* 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)return false;
ListNode * slow = head , * fast = head;
do
{
//因为快指针跑的快,如果有指针指向NULL代表不是环形链表
//如果有一个为NULL,后续走两步也会报错.
if(fast==NULL||fast->next==NULL)return false;
slow = slow->next;
fast = fast->next->next;
}while(slow != fast);
return true;
}
};
题目描述
给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。
题解
当 slow 和 fast 第一次相遇时
我们将 fast 重新移动到链表开头,并让 slow 和 fast 每次都前进一步。
当 slow 和 fast 第二次相遇时,相遇的节点即为环路的开始点。
详解:
/**
* 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) {
ListNode *slow = head, *fast = head;
// 判断是否存在环路
do {
if (!fast || !fast->next) return NULL;//如果不是环状则从这退出
fast = fast->next->next;
slow = slow->next;
} while (fast != slow);//如果是环状则从这退出循环
// 运行到这里就是存在环状,查找环路节点
fast = head;
while (fast != slow) {
slow = slow->next;
fast = fast->next;
}
return fast;
}
};
题目描述
给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。
输入输出样例
输入: s = “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
题解
s
,请你找出其中不含有重复字符的 最长子串 的长度。class Solution {
public:
int lengthOfLongestSubstring(string s) {
int len = s.length();
int ret = 0;
//把这两个看成左指针右指针
int Lptr=0 ;
unordered_map mm;
for(int Rptr = 0 ; Rptr < len ; Rptr++)
{
//把每一个元素都加入哈希表中
//就算有相同的元素,新加入的元素依然在会有自己的位置,
//哈希表中元素和字符串中元素排列相同
mm[s[Rptr]]++;
//这里运用循环很关键,可以参图片来考虑
while(mm[s[Rptr]] == 2)
{
//此时,把左指针向右移动,
//那么窗口中就少了一个元素,所以要-- , 此时符合情况
mm[s[Lptr]]--;
Lptr++;
}
ret = max(ret , Rptr - Lptr + 1);
}
return ret;
}
};
题目描述
给定一个非负整数
c
,你要判断是否存在两个整数a
和b
,使得a2 + b2 = c
。
示例 1:
输入:c = 5
输出:true
解释:1 * 1 + 2 * 2 = 5
示例 2:
输入:c = 3
输出:false
题解
本题利用双指针,左指针指向0,右指针指向c的算数平方根,
class Solution {
public:
bool judgeSquareSum(int c) {
long left = 0;
long right = (int)sqrt(c);
while (left <= right) {
long sum = left * left + right * right;
if (sum == c) {
return true;
} else if (sum > c) {
right--;
} else {
left++;
}
}
return false;
}
};
题目描述
给你一个字符串
s
,最多 可以从中删除一个字符。请你判断
s
是否能成为回文字符串:如果能,返回true
;否则,返回false
。
示例 1:
输入:s = “abca”
输出:true
解释:你可以删除字符 ‘c’ 。
示例 2:
输入:s = “abc”
输出:false
题解
题目所给函数 validPalindrome 先判断是否有不一样的元素,如果有那么就删除一个字母(这里是跳过一个字母)
尝试把左指针+1,或者右指针-1,然后调用函数isNo()判断调整后的是否是回文字符串,两种方式成功一个就可。
class Solution {
public:
bool isNo(string s , int L , int R){
while(L
题目描述
给你一个字符串
s
和一个字符串数组dictionary
,找出并返回dictionary
中最长的字符串,该字符串可以通过删除s
中的某些字符得到。如果答案不止一个,返回长度最长且字母序最小的字符串。如果答案不存在,则返回空字符串。
示例 1:
输入:s = “abpcplea”, dictionary = [“ale”,“apple”,“monkey”,“plea”]
输出:“apple”
示例 2:
输入:s = “abpcplea”, dictionary = [“a”,“b”,“c”]
输出:“a”
题解
ans保存答案,dstr保存容器中的需要测试是否相等的字符串
通过第一层循环,把每一个容器中的每一个字符串拿出来,进行测试
通过第二层循环,判断是否包含全部字符,
若包含则与之前保存的字符串进行对比,若长度大于之前的 或者 长度相等但字符序较小的 则重新赋值
注意
compare():在不同的编译器下返回值不同
- 当相等的时候返回0,不相等的时候返回 -1 ,1
- 当相等的时候返回0,不相等的时候返回 ascii 的差值
class Solution {
public:
string findLongestWord(string s, vector& dictionary) {
string ans = "" , dstr = "";
for(int m = 0 ; m < dictionary.size() ; m++){
dstr = dictionary[m];
for(int i = 0 , j = 0 ; i < s.size() && j < dstr.size() ; i++){
if(s[i] == dstr[j])j++;
if(j == dstr.size()){
if(dstr.size() > ans.size() ||
(dstr.size() == ans.size() && ans.compare(dstr) > 0))
ans = dstr;
}
}
}
return ans;
}
};