指针的妙用

指针或者叫做索引、下标,灵活使用可以完成比较巧妙地效果

快慢指针

快慢指针,表示两个指针,移动的速度不同,通常一般快指针速度是慢指针速度的两倍,通过两个指针相对位置的关系可以解决一些问题,例如:查找链表环起点问题
在这里插入图片描述
算法步骤:

  1. 快慢指针都从链表起点开始移动(两个指针速度插一倍),当两个指针再次相遇时停止本次移动
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,如下:
在这里插入图片描述

  1. 假设环起点与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);
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值