双指针法
双指针法包括快慢指针法,对撞指针法。
快慢指针法,两个指针都从头出发,快指针移动速度比慢指针快。
对撞指针法,一个指针从头出发,一个指针从尾出发,最后两个指针碰头。
两道题
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama" 输出: true 示例 2:
输入: "race a car" 输出: false
解题思路: 使用对撞指针法,一个指针从头遍历,一个指针从尾部遍历,当两个指针能够相遇,则说明字符串回文,否则不回文。
class Solution {
public:
bool isPalindrome(string s) {
int length = s.length();
transform(s.begin(), s.end(), s.begin(), ::tolower);
int i = 0, j = length - 1;
while(i < j){
// 去除不考虑的字符,只比对字母和数字字符。
while(!('a' <= s[i] && s[i] <= 'z' || '0' <= s[i] && s[i] <= '9')){
i++;
if(i == length){
return true;
}
}
while(!('a' <= s[j] && s[j] <= 'z' || '0' <= s[j] && s[j] <= '9')){
j--;
}
if(s[i] != s[j]){
return false;
}
i++;
j--;
}
// i,j相遇,字符串回文,返回true
return true;
}
};
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 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 解释:链表中没有环。
解题思路:使用快慢指针法,一个快指针,一个慢指针。如果链表不存在环,快指针会先到达链表尾部,此时就可以返回false。如果链表存在环,那么快指针最终会追上慢指针,与慢指针相遇,这时返回true。
为什么快指针一定会和慢指针相遇?
我们将慢指针的移动过程分为两个阶段:非环部分与环形部分。
- 慢指针在走完非环部分阶段后进入环形部分:此时,快指针必然早一步进入了环形部分(因为它移动快),快指针迭代次数=非环部分长度=N。
- 两个指针都在环形部分中:考虑两个在环形跑道上的选手,快指针叫快跑者,慢指针叫慢跑者。快跑者每次移动两步而慢跑者每次移动一步。速度差值为1,因此需要经过 (二者之间的距离/速度差值 )次迭代后,快跑者可以与慢跑者相遇(追上)。
想象一下,在一个环形跑道上,是不可能相遇不了的,一定会相遇的。因为速度差值为1,二者之间的距离差距最大就为环形部分的长度K。每迭代一次,距离缩小1。最大的迭代次数都不会超过环形部分的长度。
为什么相遇就一定有环?
快指针比较快,如果没有环,它会到达NULL,结束。不会再与慢指针相遇。因此,相遇即是有环。
/**
* 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){
return false;
}
ListNode* fast = head, * slow = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
return true;
}
}
//fast == NULL or fast->next == NULL无环
return false;
}
};