指针或者叫做索引、下标,灵活使用可以完成比较巧妙地效果
快慢指针
快慢指针,表示两个指针,移动的速度不同,通常一般快指针速度是慢指针速度的两倍,通过两个指针相对位置的关系可以解决一些问题,例如:查找链表环起点问题
算法步骤:
- 快慢指针都从链表起点开始移动(两个指针速度插一倍),当两个指针再次相遇时停止本次移动
ListNode fast, slow;
fast = slow = head;
while(fast != null && fast.next != null)
{
fast = fast.next.next;
slow = slow.next
if(fast == slow) break;
}
假设慢指针移动k步,那么快指针移动了2k步,两者在A点相遇,则环的大小为k,如下:
- 假设环起点与A点距离m步,那么链表起点到环起点为k -m 步,恰巧环的大小为k,若从m开始移动,那么也是k-m步会再次到达环的起点,因此两个指针一个从链表起点开始移动,另一个从相遇点移动,两者同速,那么会在同一时间到达环起点
slow = head
while( slow !=fast)
{
fast = fast.next;
slow = slow.next;
}
return slow;
左右指针
典型应用就是二分查找算法
int Search(int [] nums , int target)
{
int left =0;
int rright =nums.length -1;
while(left < = right)
{
int mid = (right +left) /2;
if(num[mid] == target)
return mid;
else if(num[mid] < target)
left = mid +1;
else if(num[mid] > target)
return right = mid -1;
}
}
滑动窗口
1、我们在字符串S中使用双指针中的左右指针技巧,初始化left = right = 0,把索引左闭右开区间[left, right)称为一个「窗口」。
2、我们先不断地增加right指针扩大窗口[left, right),直到窗口中的字符串符合要求(包含了T中的所有字符)。
3、此时,我们停止增加right,转而不断增加left指针缩小窗口[left, right),直到窗口中的字符串不再符合要求(不包含T中的所有字符了)。同时,每次增加left,我们都要更新一轮结果。
4、重复第 2 和第 3 步,直到right到达字符串S的尽头。
这个思路其实也不难,第 2 步相当于在寻找一个「可行解」,然后第 3 步在优化这个「可行解」,最终找到最优解,也就是最短的覆盖子串。左右指针轮流前进,窗口大小增增减减,窗口不断向右滑动,这就是「滑动窗口」这个名字的来历。
下面举例
理解一下,needs和window相当于计数器,分别记录T中字符出现次数和「窗口」中的相应字符的出现次数。
初始状态:
将窗口右指针向右移动,即扩大窗口,知道T中包含所有
虽然T包含了查找串的所有字符,但并不是最小值,而且字符B重复了,此时可以缩小窗口,即左指针右移动
之后重复上述过程,先移动right,再移动left…… 直到right指针到达字符串S的末端,算法结束。
string minWindow(string s, string t) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
// 记录最小覆盖子串的起始索引及长度
int start = 0, len = INT_MAX;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 右移窗口
right++;
// 进行窗口内数据的一系列更新
if (need.count(c)) {
window[c]++;
if (window[c] == need[c])
valid++;
}
// 判断左侧窗口是否要收缩
while (valid == need.size()) {
// 在这里更新最小覆盖子串
if (right - left < len) {
start = left;
len = right - left;
}
// d 是将移出窗口的字符
char d = s[left];
// 左移窗口
left++;
// 进行窗口内数据的一系列更新
if (need.count(d)) {
if (window[d] == need[d])
valid--;
window[d]--;
}
}
}
// 返回最小覆盖子串
return len == INT_MAX ?
"" : s.substr(start, len);
}