【leetcode】4月每日一题(上)

4.3 : 8. 字符串转换整数 (atoi)

需要注意可以通过移项来先进行范围判断。

class Solution {
public:
    int myAtoi(string str) {
        int len = str.size(); 
        //空串
        if(len == 0) return 0;
        int cur = 0;
        //全是空格
        while(cur<len and str[cur]==' '){
            cur++;
        }
        if(cur==len) return 0;
        //第一个字符非有效
        bool neg = false;
        if(str[cur]=='-'){
            neg = true;
            cur++;
        }
        else if(str[cur]=='+'){
            cur++;
        }
        else if(!isdigit(str[cur])){
            return 0;
        }

        int res = 0;
        while(cur<len and isdigit(str[cur])){
            int tmp = str[cur]-'0';
            //判断是否超出范围
            if(res > (INT32_MAX - tmp) / 10){
                return neg ? INT32_MIN : INT32_MAX;
            }
            res = res * 10 + tmp;
            cur++;
        }
        return neg ? -res : res;
    }
};

4.4* : 42. 接雨水

学到了三个方法:动态编程单调栈双指针,每个都很绝。。。

  • 动态编程
    通过遍历两次数组,分别记录下对于每个元素左边的最大值和右边的最大值,最后对每个元素取最小值,减去该元素的值,更新结果。
  • 单调栈
    通过栈内元素递减,表示目前这个块的接水量受前一个元素限制,如果当前值小于或等于栈顶的值,说明还不能接水,此时直接入栈。如果当前值大于栈顶的值,说明可以接水了,并且栈顶的值被当前值和栈的前一个值界定,因此可以弹出栈顶元素并且累加答案。
class Solution {
public:
    int trap(vector<int>& height) {
        stack<int> st;
        int len = height.size();
        if(len < 3) return 0;
        int res = 0;
        for(int i=0; i<len; ++i){
            while(!st.empty() and height[st.top()] < height[i]){
                int tmp = st.top();
                st.pop();
                while(!st.empty() and height[st.top()] == height[tmp]) st.pop(); //一样
                if(!st.empty()){
                    int right = st.top();
                    int distance = i - right - 1;
                    res += distance * (min(height[right], height[i]) - height[tmp]);
                }
            }
            st.push(i);
        }
        return res;
    }
};
  • 双指针
    该方法只需遍历一次数组。真的太奇妙了。通过两个指针分别保存从左到右、从右到左的最大值left_max和right_max,如果当前left_max小于right_max,那么就不管右边了,这时候只用去处理left坐标。
class Solution {
public:
    int trap(vector<int>& height) {
        int left = 0;
        int right = height.size()-1;
        int left_max = 0, right_max = 0;
        int res = 0;
        while(left <= right){
            if(left_max < right_max){ //处理left
                res += max(0, left_max - height[left]);
                left_max = max(height[left], left_max);
                left++;
            }
            else{//处理right
                res += max(0, right_max - height[right]);
                right_max = max(height[right], right_max);
                right--;
            }
        }
        return res;
    }
};

为了加深理解单调栈,又做了84. 柱状图中最大的矩形,对于单调栈的每个元素,都会有一次入栈和出栈操作,最重要的是要想清楚出栈的意义。

以下是个人理解:该元素出栈总是以该元素本身为中心的,比如接雨水中,元素出栈就是已经得到以该元素为底可以接到的水量。而第84题,当元素出栈的时候,得到以该元素为高的值。本质上讲,单调栈就是让你快速获取该元素左右两侧比他大(或小)的数的位置。那么总结一下,如果想找比他大的位置,用递减栈;反之用递增栈。

4.5* : 460 LFU缓存

要求时间复杂度为O(1),要注意一些细节。

struct Node{
    int key, val, freq;
    Node(int _key, int _val, int _freq):key(_key), val(_val), freq(_freq){}
};

class LFUCache {
    int minFreq, capacity;
    unordered_map<int, list<Node>::iterator> keymap;
    unordered_map<int, list<Node> > freqmap;
public:
    LFUCache(int _capacity) {
        minFreq = 0;
        capacity = _capacity;
        keymap.clear();
        freqmap.clear();
    }

    int get(int key) {
        // 先根据key在keymap找到位置
        if(capacity == 0) return -1;
        auto it = keymap.find(key);
        if(it == keymap.end()) return -1;
        list<Node>::iterator node = it->second;
        int val = node->val;
        int freq = node->freq;
        //更新node的freq 先在freqmap删除
        freqmap[freq].erase(node);
        //有删除就得判断是否为空
        if(freqmap[freq].size() == 0){
            freqmap.erase(freq);
            if (minFreq == freq) minFreq += 1;
        }
        freqmap[freq+1].push_front(Node(key, val, freq+1)); //从前端插入保证最近访问
        keymap[key] =  freqmap[freq+1].begin();
        return val;
    }
    
    void put(int key, int value) {
        if(capacity == 0) return;
        auto it = keymap.find(key);
        if(it == keymap.end()){
            //没有键值先判断capacity再插入
            if(keymap.size() == capacity){
                //删除最小访问频率minFreq链表最末端
                auto it2 = freqmap[minFreq].back();
                freqmap[minFreq].pop_back();
                keymap.erase(it2.key);
            }
            //插入
            freqmap[1].push_front(Node(key, value, 1));
            keymap[key] = freqmap[1].begin();
            minFreq = 1;
        }
        else{
            //有键值就更新
            list<Node>::iterator node= it->second;
            int freq = node->freq;
            freqmap[freq].erase(node);
            if(freqmap[freq].size() == 0){
                freqmap.erase(freq);
                if(minFreq == freq) minFreq += 1;
            }
            freqmap[freq+1].push_front(Node(key, value, freq+1)); //从前端插入保证最近访问
            keymap[key] =  freqmap[freq+1].begin();
        }
    }
};

4.6*:72. 编辑距离

利用动态规划,首先明白了一个字符串的增减替其实等于“增增替”

class Solution {
public:
    int minDistance(string word1, string word2) {
        int lenA = word1.size();
        int lenB = word2.size();
        if(lenA * lenB == 0) return lenA+lenB;
        int db[lenA+1][lenB+1];
        //初始化 空串和任意一个串的距离都是另一个串的长度
        for(int i=0; i<lenA+1; ++i){
            db[i][0] = i;
        }

        for(int j=0; j<lenB+1; ++j){
            db[0][j] = j;
        }

         for(int i=1; i<lenA+1; ++i){
             for(int j=1; j<lenB+1; ++j){
                 int remove = db[i-1][j]+1; //删: 已知"abcd"变成"fgh"的步数, 求"abcde"到"fgh" "abcde"->"abcd"->"fgh"
                 int insert = db[i][j-1]+1; //增就等于另一个串的减 已知"abcde"变成"fg"的步数, 求"abcde"到"fgh" 
                 int change = db[i-1][j-1]; //替: 已知"abcd"变成“fg”, 求"abcde"到"fgh"  "abcde"->"fge"->"fgh"
                 if(word1[i-1]!=word2[j-1]) change += 1;
                 db[i][j] = min(insert, min(remove, change));
             }
         }
         return db[lenA][lenB];
    }
};

4.7 : 面试题 01.07. 旋转矩阵

从非原地方法出发,那么公式就是:
m a t r i x n e w [ c o l ] [ n r o w 1 ] = m a t r i x [ r o w ] [ c o l ] matrix_{new}[col][n-row-1] = matrix[row][col]
那么原地的话,就得有个临时变量来储存。从该式出发就可以得到下列转换公式:
在这里插入图片描述
如何循环?
在这里插入图片描述
左上角长为n/2,宽为(n+1)/2的矩阵。

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        for (int i = 0; i < n / 2; ++i) {
            for (int j = 0; j < (n + 1) / 2; ++j) {
                int temp = matrix[i][j];
                matrix[i][j] = matrix[n - j - 1][i];
                matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
                matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
                matrix[j][n - i - 1] = temp;
            }
        }
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/rotate-matrix-lcci/solution/xuan-zhuan-ju-zhen-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4.11* :887.鸡蛋掉落

动态规划的特点:求最优,不问过程,只要结果

官方题解

发布了86 篇原创文章 · 获赞 10 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_36530992/article/details/105358015