leecode - 入门 -- 双指针总结

链接

双指针技巧再分为两类,一类是「快慢指针」,一类是「左右指针」。
者解决主要解决链表中的问题,比如典型的判定链表中是否包含环;
者主要解决数组(或者字符串)中的问题,比如二分查找。

快慢指针

1 判定链表中是否含有环

力扣第 141 题

class Solution {
public:
    bool hasCycle(ListNode* head) {
        if (head == nullptr || head->next == nullptr) {
            return false;
        }
        ListNode* slow = head;
        ListNode* fast = head->next;
        while (slow != fast) {
            if (fast == nullptr || fast->next == nullptr) {
                return false;
            }
            slow = slow->next;
            fast = fast->next->next;
        }
        return true;
    }
};

2 已知链表中含有环,返回这个环的起始位置

相遇点到环起点 的 步数链头部 到 环起点的步数相同(前提是 slow 单步,fast 两步)

ListNode detectCycle(ListNode head) {
    ListNode fast, slow;
    fast = slow = head;
    while (fast != NULL&& fast.next != NULL) {
        fast = fast.next.next;
        slow = slow.next;
        if (fast == slow) break;
    } 
    // 上面的代码类似 hasCycle 函数
    slow = head;
    while (slow != fast) {
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

3 寻找链表的中点

当链表的长度是奇数时,slow恰巧停在中点位置;
如果长度是偶数,slow最终的位置是中间偏右

通过判断 快指针fast->next是否为NULL 来看链表长度是偶数还是奇数;
为NULL是偶数,不为NULL为奇数

力扣第 876 题

class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        ListNode *slow ,*fast;
        slow=fast=head;
        while (fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
};

4 寻找链表的倒数第n个元素

力扣第 19 题

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *slow,*fast;
        fast=slow=head;
        while(n-- > 0)
            fast=fast->next;
        if(fast==nullptr) return head->next; // 此时fast指向尾部,第n个即首部
		
		// fast 指向 首部时,fast不为null fast->next 为空
        while(fast!=nullptr&&fast->next!=nullptr){
            fast=fast->next;
            slow=slow->next;
        } 
        slow->next=slow->next->next;
        delete p;
        return head;
    }
};

java中new ListNode(0)常见用法详细区别(全)

左右指针

1 二分查找

链接

2 两数之和 – 有序数组

只要 数组有序,就应该想到 双指针技巧

力扣第 167

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left,right;
        left = 0; right=numbers.size()-1;
        while(left < right){
            int sum = numbers[left]+numbers[right];
            if(sum == target) return {left+1,right+1};
            else if(sum < target) left++;
            else if(sum>target) right--;
        }
        return {-1,-1};
    }
};

3 反转数组

力扣第 344

class Solution {
public:
    void reverseString(vector<char>& s) {
        int n = s.size();
        for (int left = 0, right = n - 1; left < right; ++left, --right) {
            swap(s[left], s[right]);
        }
    }
};

C++容器的swap()函数的三点说明
C++中的swap(交换函数)

4 滑动窗口算法

链接

以下四个问题:
1、当移动right扩大窗口,即加入字符时,应该更新哪些数据
2、什么条件下,窗口应该暂停扩大,开始移动left缩小窗口
3、当移动left缩小窗口,即移出字符时,应该更新哪些数据
4、我们要的结果应该在扩大窗口时还是缩小窗口时(何时)进行更新

针对 Leecode 76 题: 匹配对应字符及该字符个数
1、扩大窗口时,应更新窗口内包含目标子串的字符频数,每当完全匹配到一个字符时,更新一次当前窗口已经完全匹配到的字符数
2、当窗口已经完全包含所有目标子串字符时,开始缩小窗口
3、在收缩窗口前更新数据

LeetCode 76 题 最小覆盖子串

class Solution {
public:
    string minWindow(string s, string t) {
		
        unordered_map<char,int> need,window; // 用于存储目标子串各字符频数 和 当前窗口中的字符频数
		
        for(char c : t) need[c]++; // 初始化 目标子串 各 字符频数
		
		// 搜索字符区间 (左开右闭) [left,right) 所以初始窗口为空
        int left=0,right=0; // 左右指针
		
        int valid = 0;  // 标记已匹配到的字符数

        int start=0,len=INT_MAX; // 记录最小子串起始位置及其长度,长度初始化为一个默认大值
		
		// 当 右指针right 超出字符串索引,结束
        while(right < s.size()){
			
            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){  // 因为搜索区间为[left,right) ,所以 right-left 即为窗口子串长度,无须 +1
                    start = left;
                    len = right-left;
                }
				
				// 移出窗口元素
                char d = s[left]; 
                left ++; // 左移窗口
				
				// 窗口内数据更新
                if(need.count(d)){
                    if(window[d] == need[d])
                        valid --;
                    window[d]--;
                }
            }
        }
		// 判断len 是否更新 未更新表示未搜索到最小子串,返回空字符串
        return len == INT_MAX ? "" : s.substr(start,len);
    }
};

LeetCode 567 字符串排列

class Solution {
public:
    bool checkInclusion(string s1, string s2) {

        unordered_map<char,int> need,window;
        for(char c:s1) need[c]++;

        int left=0,right=0;
        int valid=0;

        while(right < s2.size()){
            char c=s2[right];
            right++;

            if(need.count(c)){
                window[c]++;
                if(window[c] == need[c])
                    valid++;
            }
			// 当窗口等于目标子串长度时收缩窗口
            while(right - left >= s1.size()){
                if(valid == need.size()) return true;
                char d=s2[left];
                left++;
                if(need.count(d)){
                    if(window[d] == need[d])
                        valid --;
                    window[d]--;
                }
            }
        }
        return false;
    }
};

LeetCode 第 438 : 找所有字母异位词

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        unordered_map<char,int> need,window;
        for(char c: p) need[c]++;

        int left=0,right=0;
        int valid=0;
        vector<int> start;

        while(right<s.size()){
            char c=s[right];
            right++;
            if(need.count(c)){
                window[c]++;
                if(window[c] == need[c])
                    valid++;
            }
            // 窗口大小限制
            while(right - left >=p.size()){
                if(valid == need.size()) start.push_back(left);

                char d=s[left];
                left++;
                if(need.count(d)){
                    if(window[d] == need[d])
                        valid --;
                    window[d]--;
                }
            }
        }
        return start;
    }
};

LeetCode 第 3 题 : 最长无重复子串

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_map<char,int> window;
        int left=0,right=0;
        int res =0 ;
        while(right<s.size()){
            char c=s[right];
            right++;
            // 更新窗口
            window[c]++;
            // 收缩窗口
            while(window[c]>1){
                char d=s[left];
                left++;
                window[d]--;
            }
            // 更新结果
            res=max(res,right-left);
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值