【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. 旋转矩阵
从非原地方法出发,那么公式就是:
那么原地的话,就得有个临时变量来储存。从该式出发就可以得到下列转换公式:
如何循环?
左上角长为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.鸡蛋掉落
动态规划的特点:求最优,不问过程,只要结果