LeetCode
剑指 Offer 20. 表示数值的字符串
模拟,字符串处理
对着规则,if-else判断就好了
复杂度分析:全过程只遍历了一遍字符串,那么时间复杂度为O(n),使用的额外空间位常数,那么空间复杂度为O(1)
class Solution {
public:
bool isNumber(string s) {
int i = 0, j = s.size() - 1;
while(s[i] == ' ') ++i;//首先去掉前后的空格
if(i == s.size()) return false;//排除只有空格的情况
while(s[j] == ' ') --j;
bool num = false;
if(s[i] == '-' || s[i] == '+') ++i;//首先判断e前面的小数或者整数
while(i <= j && isdigit(s[i])){
num = true;//如果字符串没有数字,那么是错的
++i;
}
if(i <= j && s[i] == '.') ++i;
while(i <= j && isdigit(s[i])){
num = true;
++i;
}
if(i <= j && (s[i] == 'E' || s[i] == 'e')){//判断e之后的整数,注意只能是整数
if(i + 1 > j) return false;//1e这种
++i;
if(s[i] == '-' || s[i] == '+'){
++i;
if(i > j) return false;//排除1e+这种
}
while(isdigit(s[i])) ++i;
if(i <= j) return false;//如果没能扫描到最后一个字符,那么是错误的
}
else if(i <= j && !isdigit(s[i]) && s[i] != 'E' && s[i] != 'e'){
return false;//排除1+6这种
}
return num;
}
};
剑指 Offer 41. 数据流中的中位数
大根堆 小根堆
构建两个堆,一个大根堆,一个小根堆,大根堆里放较小的一半的数,小根堆里放较大一半的数。
每次放元素addNum,先把元素放入大根堆,如果大根堆的堆顶比小根堆的对顶大,那么交换两者的对顶元素。如果大根堆的size比小根堆多两个了,那么把小根堆的堆顶放入大根堆中。
每次取中位数findMedian,如果大小根堆size()不同,那么取小根堆的堆顶。否则取大小根堆的堆顶的平均值。
复杂度分析:时间复杂度,由于堆的插入与弹出是O(logN),所以addNum是O(logN),而由于获取根顶元素使用O(1)的时间,那么findMedian是O(1)
空间复杂度:由于需要大小根堆保存数据,那么空间复杂度为O(N)
class MedianFinder {
public:
priority_queue min;
priority_queue, greater> max;
/** initialize your data structure here. */
MedianFinder() {
}
void addNum(int num) {
min.push(num);
if(min.size() - max.size() != 1){
max.push(min.top());
min.pop();
}
if(max.size() > 0 && min.top() > max.top()){
int t1 = min.top(), t2 = max.top();
min.pop(), max.pop();
min.push(t2), max.push(t1);
}
}
double findMedian() {
if(min.size() + max.size() & 1) return min.top();
else return (max.top() + min.top()) / 2.0;
}
};
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder* obj = new MedianFinder();
* obj->addNum(num);
* double param_2 = obj->findMedian();
*/
剑指 Offer 56 - I. 数组中数字出现的次数
异或运算
首先取得数组元素异或和,异或和应该等于两个只出现一次的元素x,y,两者的异或和xy。之后只需要划分数组元素,方法是:通过lowbit运算获取xy的最低位为1的二进制位,通过判断数组元素是否具有这一个二进制位来划分数组元素,已到达区分数组的目的。
时间复杂度:由于只需要遍历数组两遍,那么时间复杂度为O(N)
空间复杂度:需要三个各位变量,所以为O(1)
class Solution {
public:
vector singleNumbers(vector& nums) {
int x = 0, y = 0, m = 0;
for(auto i : nums){
y ^= i;
}
m = y & (-y);//lowbit
for(auto i : nums){
if(i & m) x ^= i;
}
return {x, y ^ x};
}
};
剑指 Offer 45. 把数组排成最小的数
排序 贪心
这里的排序正确性证明:
需要证明排序规则满足反对称性与传递性。反对称性,a + b >= b + a,并且b + a >= a + b,那么a + b == b + a,由于a + b与b + a的位数一样,那么字典序大小就是字符串对应数值大小,得证。传递性,a + b < b + a, b + c < c + b,假设a,b,c的位数分别为n, m, k,那么
时间复杂度:O(nlogn),n为最终返回值的字符数目,内置排序是一个,平均时间复杂度为O(nlogn)。
空间复杂度:O(n),需要数组存储所有字符串。
class Solution {
public:
static bool cmp(string &a, string &b){
return a + b < b + a;//需要注意,这里不能写成<=,不然会违反sort元素严格弱序的要求
}
string minNumber(vector& nums) {
vector ans;
for(auto c : nums){
ans.push_back(to_string(c));
}
sort(ans.begin(), ans.end(), cmp);
string s;
for(auto i : ans){
s += i;
}
return s;
}
};
剑指 Offer 62. 圆圈中最后剩下的数字
约瑟夫环问题
假设f(n,m)表示为从从n个元素中删除圆圈的第m个数,直到剩下最后一个元素为止的元素编号,那么可以推出f(n,m)=(f(n - 1, m) + m) % n。也就是说在数组长度为n - 1中元素编号为x的元素,它在数组长度为n的数组中的编号便为(x + m)% n。
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public:
int lastRemaining(int n, int m) {
// if(n == 1) return 0;
// else return (lastRemaining(n - 1, m) + m) % n;
int ans = 0;
for(int i = 2; i <= n; ++i){
ans = (ans + m) % i;
}
return ans;
}
};
剑指 Offer 67. 把字符串转换成整数
模拟
时间复杂度:由于只扫描了一边字符串,所以为O(n)。
空间复杂度:O(1)
class Solution {
public:
int strToInt(string str) {
int i = 0, j = str.size();
while(str[i] == ' ') ++i;//去掉首空格
if(i >= j) return 0;
bool minus = false;
if(str[i] == '-'){
minus = true;
++i;
} else if(str[i] == '+'){
++i;
}
long long ans = 0;
while(i < j){
if(isdigit(str[i])){
ans *= 10;
ans += str[i] - '0';
++i;
}else break;
// if(minus && ans * -1 < (1 << 31)) return (1 << 31);//判断是否越界
// else if(!minus && ans > (unsigned (1 << 31) - 1)) return (unsigned (1 << 31) - 1);
if(minus && ans * -1 < INT_MIN) return INT_MIN;//判断是否越界
else if(!minus && ans > INT_MAX) return INT_MAX;
}
if(minus) ans *= -1;
return ans;
}
};
剑指 Offer 63. 股票的最大利润
动态规划
假设dp[i]表示第i - 1天的最大利润,那么dp[i] = max(dp[i - 1], prices[i] - min(cost, prices[i])),继续优化dp数组之前的数据不用保持了,用一个profit变量保存即可,profit = max(profit, prices[i] - min(cost, prices[i])), cost = min(prices[i], cost)。
时间复杂度:O(n)。
空间复杂度:O(1)
class Solution {
public:
int maxProfit(vector& prices) {
if(prices.size() < 2) return 0;
int cost = INT_MAX, profit = 0;
for(auto i : prices){
profit = max(i - price, profit);
cost = min(i, cost);
}
return profit;
}
};
剑指 Offer 66. 构建乘积数组
前后缀分解
分两步计算ans数组
时间复杂度:O(n)
空间复杂度:O(1)
class Solution {
public:
vector constructArr(vector& a) {
int s = a.size();
vector ans(s, 1);
for(int i = 1; i < s; ++i)
ans[i] = ans[i - 1] * a[i - 1];
int t = 1;
for(int i = s - 1; ~i ; --i){
ans[i] *= t;
t *= a[i];
}
return ans;
}
};