首页 > 其他 > 详细

2.双指针

时间:2021-03-30 00:23:13      阅读:29      评论:0      收藏:0      [点我收藏+]
/*
双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多个数组的多个指针
若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索;
若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。
*/


//167.两数之和
/*
通过一边遍历解决在一个增序的整数数组里找到两个数,使他们的和为给定值。已知有且只有一对解。 使用遍历方向相反的指针
*/
vector<int> twoSum(vector<int>& 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<int>{l+1,r+1};    //vector返回值写法
}


//88.合并两个有序数组
/*
给定两个有序数组,把两个数组合并为一个
两个数组长度为m,n;其中一个数组的长度被延长至m+n,多出的n用0补位,不需要开辟额外空间
题解:这两个数组已经排好序,我们可以把两个指针分别放在两个数组的末尾,即nums1的m-1位和nums2的n-1位。
每次将较大的数字复制到nums1的后边,然后向前移动一位。因为我们也要定位这个移动的下标,所以还需要第三个指针
*/
void merge(vector<int>& nums1, int m, vector<int>&nums2, int n) {
    int pos = m-- + n-- -1;
    while(m >= 0 && n >= 0 ){
        if(nums1[m] > nums2[n])  nums[pos--] = nums1[m--];
        else nums[pos--] = nums[n--];
    }
    while(n>=0){
        nums1[pos--]=nums2[n--];
    }
}


//142.链表是否有环
/*给定一个链表,如果有环路,找出环路的开始点*,如果没有环路返回一个空指针
题解:对于链表找环路问题,有一个通用的解法---快慢指针,给定两个指针,分别命名为slow和
fast,起始位置在链表的开头。每次fast前进两步,slow前进一步,如果fast可以走到尽头,那么
说明没有环路;如果fast可以无线循环下去,那么说明一定有环路,且一定存在一个时刻slow和fast
相遇。当slow和fast第一次相遇时,我们将fast重新移动到链表开头,并让slow和fast每次都前进
一步。当slow和fast第二次相遇时,相遇的节点即为环路的开始点。
*/
struct ListNode{
    int val;
    ListNode *next;
    ListNode(int x):val(x),next(nullptr) {} //链表初始化方法
};

//哈希表 简单粗暴 判断链表是否有环 时间空间复杂度 O(N)
ListNode *detectCycle(ListNode *head){
    unordered_set<ListNode*> visited;   //用set可以,unordered_set是哈希set,无序的速度快
    while(head != nullptr){
        if(visited.count(head)){
            return head;
        }
        visited.insert(head);
        head = head->next;
    }
    return nullptr;
}

//快慢指针方法 时间复杂度O(N) 空间复杂度O(1)
ListNode *detectCycle(ListNode *head) {
    ListNode *slow = head, *fast = head;
    //判断是否存在环路
    do{
        if(!fast || !fast->next) return nullptr;
        fast = fast->next->next;
        slow = slow->next;
    }while(fast != slow);
    //如果存在,查找环路节点
    fast = head;
    while(fast != slow){
        slow=slow->next;
        fast=fast->next;
    }
    return fast;
}


//76.最小覆盖子串(hard)
/*
题目描述:给定两个字符串S和T,求S中包含T所有字符的最短连续子字符串的长度,同时要求时间复杂度不得超过O(n)
输入输出样例:输入是两个字符串S和T,输出是一个S字符串的子串。
Input:S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
在这个样例中,S中同时包含一个A、一个B、一个C的最短子字符串是 "BANC"
题解:本题使用滑动窗口求解,即两个指针l和r都是从最左端向最右端移动,且l的位置一定在r的左边或重合。注意本题虽然在for循环里出现一个while循环,
但是因为while循环负责移动l指针,且l只会从左到右移动一次,因此总时间复杂度仍然是O(n).本题使用长度为128的数组来映射字符,也可以用哈希表来代替;
其中chars表示目前每个字符缺少的数量,flag表示每个字符是否在T中存在。
*/
string minWindow(stirng S, string T){
    vector<int> chars(128, 0);
    vector<bool> flag(128, false);
    //先统计T中的字符情况
    for(int i=0; i<T.size(); ++i){
        flag[T[i]]= true;
        ++chars[T[i]];
    }
    //移动滑动窗口,不断更改统计数据
    int cnt=0, l=0, min_l=0, min_size=S.size()+1;
    for(int r=0; r<S.size(); ++r){
        if(flag[S[r]]){
            if(--chars[S[r]] >= 0){     //因为T中可能有重复的 所用>= 而不是==
                ++cnt;
            }
            //若目前滑动窗口已包含T中全部字符,
            //则尝试将l右移,在不影响结果的情况下获得最短子字符串
            while(cnt == T.size()){
                if(r-l +1 < min_size){
                    min_l=l;
                    min_size=r-l+1;
                }
                if(flag[S[l]] && ++chars[S[l]]>0){
                    --cnt;
                }
                ++l;
            }
        }
    }
    return min_size > S.size()? "":S.substr(min_l,min_size);
}

 

2.双指针

原文:https://www.cnblogs.com/go-ahead-wsg/p/14594432.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!