• (双指针) leetcode 378. 209. 3. 438. 76


    class Solution {
    public:
        struct Event{
            int x;
            int y;
            int value;
            //构造函数初始化列表
            Event(int x, int y, int v):x(x),y(y),value(v){}
            //Event():x(0),y(0),value(0){}; 
        };
        
        struct cmp{
            bool operator() (const Event& a, const Event& b){
                return a.value > b.value;  //小顶堆
            }
        };
        
        int kthSmallest(vector<vector<int>>& matrix, int k) {
            int dx[2] = {0, 1};
            int dy[2] = {1, 0};    //只向右或下移动
            
            if(matrix.empty() || matrix[0].empty())
                return -1;
            int n = matrix.size(), m = matrix[0].size();
            //if(n*m < k)
                //return -1;
            
            //priority_queue 不会判断元素是否重复
            priority_queue<Event, vector<Event>, cmp> q;
            vector<vector<bool>> visited(n, vector<bool>(m, false));   //记录是否遍历过
          
            q.emplace(0, 0, matrix[0][0]);
            visited[0][0] = true;
            
            for(int i=0; i<k-1; i++){
                int x = q.top().x, y=q.top().y, value = q.top().value;
                q.pop();
                for(int j=0; j<2; j++){
                    int nx = x + dx[j];
                    int ny = y + dy[j];
                    if(nx<n && ny<m && !visited[nx][ny]){
                        visited[nx][ny] = true;
                        q.emplace(nx, ny, matrix[nx][ny]);
                    }
                }
            }
            return q.top().value;
        }
    };

    滑动窗口:这两个索引表示的是一个窗口,让这个窗口不停的在数组中滑动,来找到问题的解。

    -什么叫子数组:可以不连续。但是本题强调了是要连续的。、

    解法一:滑动窗口

    时间复杂度:O(n),空间复杂度O(1) 因为没有另外开辟空间。

    class Solution {
    public:
        int minSubArrayLen(int s, vector<int>& nums) {
            //初始化,希望区间不包含任何元素
            int l = 0, r = -1;   //nums[l...r]为我们的滑动窗口
            int sum = 0;    //
            int res = nums.size()+1;    //连续子数组初始化为数组的长度+1
            
            while(l<nums.size()){
                if(r+1<nums.size() && sum<s){
                    r++;
                    sum += nums[r];     //应注意r的取值不能越界
                }
                else{
                    sum -= nums[l];
                    l++;
                }
                if(sum >= s)
                    res = min(res, r-l+1);
            }
            
            if(res == nums.size()+1)
                return 0;
            
            return res;
        }
    };

    思路:和209类似采用滑动窗口的思路。在[l...r]区间中表示不重复的字符集,为了扩大当前的字符集,加载一位新的元素时要判断新元素是否在[l...r]中,若不在扩大r+1,若在就将左边界l减至重复元素之后。在此期间不断更新维护字符区间的长度。

    如何判断新的元素是否在字符集区间呢?遍历查找?find?这里有个小技巧,查表
    预定义一个256大小的数组来维护字符集区间内元素出现的频率freq[256]初始为0,当加入1个元素对应的元素下标+1,当从字符集中删除一个元素对应删除元素下标需要-1,最终通过查表来看该元素是否在字符集空间。  这个想法很巧妙啊 :)

    class Solution {
    public:
        int lengthOfLongestSubstring(string s) {
            int freq[256] = {0};   //记录数组中的字符出现的频率
            int l=0, r = -1;      //滑动窗口为s[l...r]
            int res = 0;         //满足条件的字串的最长长度
            
            while(l<s.size()){
                if(r+1<s.size() && freq[s[r+1]]==0){
                    //r+1为下标的字符出现的频率为0时,可以向右扩张
                    r++;
                    freq[s[r]]++;
                }
                else{
                    //重复出现时,左侧窗口向右移,即缩小
                    freq[s[l]]--;
                    l++;
                }
                res = max(res, r-l+1);
            }
            return res;
        }
    };

     

     思路:哈希表+滑动窗口。设立两个vector容器vp和vs,它们的初值都设为0,共26个,然后先遍历一遍p,将p里面的元素,对应的vs相应值+1;然后遍历一遍s,保证滑动窗口的长度和p的长度相同,然后比较两个容器是否相同,若相同,将初始索引值放入ans中。

    class Solution {
    public:
        vector<int> findAnagrams(string s, string p) {
            int n = s.length();
            int l = p.length();
            vector<int> ans, vp(26,0), vs(26,0);  //ans返回所有索引, 初始化vp和vs26个值为0
            for(char c : p) vp[c-'a']++;  //vp存储字符串p所包含的字母个数
            for(int i=0;i<n;i++){
                if(i>=l) vs[s[i-l]-'a']--;   //将s上一个字符移除
                vs[s[i]-'a']++;
                if(vs==vp) ans.push_back(i-l+1);
            }
            return ans;
        }
    };

    思路:使用一个int型的vector容器来存储字符在串中的次数,初始值为128个0,因为ASCLL有256个字符,而一般输入字母串的字符只有128个。

    1)首先遍历一遍 t,把字符对应出现的次数存到letterCnt中;

    2)遍历s,把遍历到的字母对应的letterCnt中的值减一,若减一之后的值仍大于等于0,cnt增加1,说明找到了一个t中出现的字符;

    3) 若cnt等于t串的长度时,开始循环,使用res记录一个小于minLen的子串,并更新minLen的值。然后将子窗口的左边界向右移,右移的同时相应的letterCnt要加1,若letterCnt加1后的值大于0,说明这个移除掉的字母也在t中,则cnt++,且left++;

    4)最后遍历完s后,可以得到包含t的最小子串。

     class Solution {
    public:
        string minWindow(string s, string t) {
            string res = "";  // 输出结果
            vector<int> letterCnt(128,0);   //因为ASCLL码有256个字符,而一般输入字符串的字符只有128个
            int left = 0, cnt = 0, minLen = INT_MAX; //cnt计数器:遍历到的字母是T串的字母则加1,minLen:记录出现过的包含T中所有字母的最短的子串长度
            for(char c:t) letterCnt[c] ++;
            for(int i=0; i<s.size();i++){
                if(--letterCnt[s[i]] >= 0 ) cnt++;   //若把s[i]相应的字符频率-1后仍大于等于0,说明当前字符也在t中,故cnt++
                while(cnt == t.size()){
                    if(minLen > i-left+1){
                        minLen = i-left+1;
                        res = s.substr(left, minLen);   //取从left开始,长度为minLen的子串保存到res中
                    }
                    if(++letterCnt[s[left]] > 0) --cnt;   //若左窗口的字符对应的letterCnt加1后大于0,说明这个字符也在t中,故cnt--
                    left++;
                }
            }
            return res;   //遍历一遍s后得到最小包含t的子串        
        }
    };
  • 相关阅读:
    jmeter跨平台执行时的文件路径问题
    jenkins配置
    jmeter--负载测试
    jmeter-脚本制作
    jmeter学习-性能指标、jmeter初识
    功能测试--其他
    功能测试--Fiddler
    功能测试--APP专项
    功能测试--基础(二)
    功能测试-基础(一)
  • 原文地址:https://www.cnblogs.com/Bella2017/p/10159234.html
Copyright © 2020-2023  润新知