LeetCode数据结构与算法学习Day03
题目引用自
https://leetcode-cn.com/
图解数据结构与算法
每日学习Day03学习笔记
59 I 滑动窗口最大值
题目描述:
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
解题思路:
1、使用双层for循环,第一层为创建窗口,第二层为遍历找最大值,时间复杂度过高,对于数据量巨大的测试用例会造成时间超时。
2、利用min函数栈的思想实现。
3、优先队列实现。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
int n = nums.size();
if(n <= 0){
vector<int>p;
return p;
}
priority_queue<pair<int, int>> q;
for (int i = 0; i < k; ++i) {
q.emplace(nums[i], i);
}
vector<int> ans = {q.top().first};
for (int i = k; i < n; ++i) {
q.emplace(nums[i], i);
while (q.top().second <= i - k) {
q.pop();
}
ans.push_back(q.top().first);
}
return ans;
}
};
执行用时:
16 ms, 在所有 C++ 提交中击败了95.36%的用户
内存消耗:
16.7 MB, 在所有 C++ 提交中击败了6.99%的用户
59 II 队列的最大值
题目描述:
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1
解题思路:
本题个人认为与min栈有相同之处,解题思路为维护一个保存最大值的队列,当弹入元素时,对队列尾部元素判断,弹出尾部元素直到尾部元素不小于该值的时候停止。弹出元素时进行判断,弹出值是否为队列头部元素,是的话一起弹出,如果不是则最大值队列不作弹出动作。
class MaxQueue {
public:
MaxQueue() {
}
int max_value() {
return q_max.empty()?-1:q_max.front();
}
void push_back(int value) {
q_s.push(value);
while(!q_max.empty()&&q_max.back()<value)
q_max.pop_back();
q_max.push_back(value);
}
int pop_front() {
if(q_s.empty())
return -1;
int tp = q_s.front();
q_s.pop();
if(tp == q_max.front())
{
q_max.pop_front();
}
return tp;
}
queue<int>q_s;
deque<int>q_max;
};
执行用时:
148 ms, 在所有 C++ 提交中击败了38.17%的用户
内存消耗:
47.4 MB, 在所有 C++ 提交中击败了98.63%的用户
67 把字符串转换成整数
题目描述:
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
解题思路:
1、直接对每个字符进行判断处理,同时拼接前进行越界判断。
2、利用状态机判断(最近着重在看这部分,试着实现了一下,反而效率比第一种方法低,可能是由于if判断过多?不太确定)
//状态机
class Solution {
public:
enum stats {
START,
BLANK,
SIGNALP,
SIGNALD,
NUMBER
};
enum chartype {
intnum,
signp,
signd,
blank,
other
};
chartype whattype(char c) {
if (c == ' ')
return blank;
else if (c >= '0' && c <= '9')
return intnum;
else if (c == '+')
return signp;
else if (c == '-')
return signd;
else
return other;
}
int strToInt(string str) {
unordered_map<stats, unordered_map<chartype, stats>>dp{
{
START,{
{blank,BLANK},{signp,SIGNALP},
{intnum,NUMBER},{signd,SIGNALD},
}
},
{
BLANK,{
{blank,BLANK},{signp,SIGNALP},
{intnum,NUMBER},{signd,SIGNALD},
}
},
{
NUMBER,{
{intnum,NUMBER},
}
},
{
SIGNALP,{
{intnum,NUMBER},
}
},
{
SIGNALD,{
{intnum,NUMBER},
}
},
};
int length = str.length();
stats ss = START;
bool flag = false;
long long sum = 0;
for (int i = 0; i < length; ++i) {
chartype tp = whattype(str[i]);
if (dp[ss].find(tp) == dp[ss].end())
if (sum == 0)
return 0;
else
break;
else
ss = dp[ss][tp];
if (ss == SIGNALD)
flag = true;
else if (ss == NUMBER)
{
sum = sum * 10 + str[i] - '0';
if (sum > INT_MAX)
{
if (flag)
return INT_MIN;
return INT_MAX;
}
}
}
if (flag)
sum = -sum;
return sum;
}
};
执行用时:
20 ms, 在所有 C++ 提交中击败了24.39%的用户
内存消耗:
9.5 MB, 在所有 C++ 提交中击败了5.03%的用户
class Solution {
public:
int strToInt(string str) {
int res = 0, bndry = INT_MAX / 10;
int i = 0, sign = 1, length = str.size();
if(length == 0) return 0;
while(str[i] == ' ')
if(++i == length) return 0;
if(str[i] == '-') sign = -1;
if(str[i] == '-' || str[i] == '+') i++;
for(int j = i; j < length; j++) {
if(str[j] < '0' || str[j] > '9') break;
if(res > bndry || res == bndry && str[j] > '7')
return sign == 1 ? INT_MAX : INT_MIN;
res = res * 10 + (str[j] - '0');
}
return sign * res;
}
};
执行用时:
0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:
6.1 MB, 在所有 C++ 提交中击败了61.68%的用户
10 I 斐波那契数列
题目描述:
写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出。
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
解题思路:
1、利用推导推出Fn = 3Fn-3 + 2Fn-4,递归求出结果。
2、利用动态规划。
class Solution {
public:
int fib(int n) {
int a = 0,b = 1,sum;
for(int i = 0;i<n;++i){
sum = (a+b)% 1000000007;
a = b;
b = sum;
}
return a;
}
};
执行用时:
0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:
5.8 MB, 在所有 C++ 提交中击败了58.29%的用户
10 II 青蛙跳台阶问题
题目描述:
一只青蛙一次可以跳上1级台阶,也可以跳上2级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。
解题思路:
与上述题目一致,利用动态规划。
class Solution {
public:
int numWays(int n) {
int a = 1,b = 1,sum;
for(int i = 0;i<n;++i)
{
sum = (a+b) %1000000007;
a = b;
b = sum;
}
return a;
}
};
19 正则表达式匹配
题目描述:
请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。
解题思路:
利用动态规划,判断出s[i]与p[j]能否匹配的条件,写出程序。
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
auto matches = [&](int i, int j) {
if (i == 0) {
return false;
}
if (p[j - 1] == '.') {
return true;
}
return s[i - 1] == p[j - 1];
};
vector<vector<int>> f(m + 1, vector<int>(n + 1));
f[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p[j - 1] == '*') {
f[i][j] |= f[i][j - 2];
if (matches(i, j - 1)) {
f[i][j] |= f[i - 1][j];
}
}
else {
if (matches(i, j)) {
f[i][j] |= f[i - 1][j - 1];
}
}
}
}
return f[m][n];
}
};
执行用时:
4 ms, 在所有 C++ 提交中击败了92.49%的用户
内存消耗:
6.9 MB, 在所有 C++ 提交中击败了29.85%的用户
42 连续子数组的最大和
题目描述:
输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
解题思路:
由于连续子数组的最大和可视为数组当前元素与前一个元素的关系,如果前一个元素<0,则对当前元素而言是负收益,而如果前一个元素>0,则对当前元素而言是正收益,可将两数相加后存入原位。同时初始时将第0位数字保存起来,每次都与其判断其中最大值。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int res = nums[0];
for(int i = 0,j = i+1;j <nums.size();++i,++j)
{
if(nums[i] > 0)
nums[j] += nums[i];
if(nums[j] > res)
res = nums[j];
}
return res;
}
};
执行用时:
28 ms, 在所有 C++ 提交中击败了41.56%的用户
内存消耗:
22.4 MB, 在所有 C++ 提交中击败了69.27%的用户