力扣每日一题(2023年1月)
1、1月2日 1801. 积压订单中的订单总数
思路:优先队列
由于需要寻找价格最低的销售订单sell和价格最高的采购订单buy,所以维护两个优先队列即销售订单优先队列(小顶堆)和采购订单优先队列(大顶堆),队列中每个元素是一个pair,分别存储价格price和数量amount。
遍历订单orders,对order = [price, amount, orderType]执行以下操作:
- 若orderType = 0则为采购订单。当销售订单优先队列中存在价格小于等于price时,将当前采购订单和积压的销售订单进行匹配执行,直到当前采购订单全部匹配执行、积压的销售订单全部匹配执行或剩余的积压的销售订单价格都大于price。如果还有剩余的当前采购订单未匹配执行,则将剩余的采购订单添加到采购订单优先队列。
- 若orderType = 1则为销售订单。当采购订单优先队列中存在价格大于等于price时,将当前销售订单和积压的采购订单进行匹配执行,直到当前销售订单全部匹配执行、积压的采购订单全部匹配执行或剩余的积压的采购订单价格都大于price。如果还有剩余的当前销售订单未匹配执行,则将剩余的销售订单添加到销售订单优先队列。
遍历完orders之后,计算两个队列的订单数目之和并返回。
C++代码
class Solution {
public:
int getNumberOfBacklogOrders(vector<vector<int>>& orders) {
const int MOD = 1000000007;
priority_queue<pair<int, int>, vector<pair<int, int>>, less<pair<int, int>>> pq_buy;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq_sell;
for (auto order : orders) {
int price = order[0], amount = order[1], orderType = order[2];
if (orderType == 0) {
while (amount > 0 && !pq_sell.empty() && pq_sell.top().first <= price) {
auto sellOrder = pq_sell.top();
pq_sell.pop();
int minAmount = min(amount, sellOrder.second);
amount -= minAmount;
sellOrder.second -= minAmount;
if (sellOrder.second > 0) pq_sell.push(sellOrder);
}
if (amount > 0) pq_buy.push({price, amount});
} else {
while (amount > 0 && !pq_buy.empty() && pq_buy.top().first >= price) {
auto buyOrder = pq_buy.top();
pq_buy.pop();
int maxAmount = min(amount, buyOrder.second);
amount -= maxAmount;
buyOrder.second -= maxAmount;
if (buyOrder.second > 0) pq_buy.push(buyOrder);
}
if (amount > 0) pq_sell.push({price, amount});
}
}
int res = 0;
while (!pq_buy.empty()) {
res = (res + pq_buy.top().second) % MOD;
pq_buy.pop();
}
while (!pq_sell.empty()) {
res = (res + pq_sell.top().second) % MOD;
pq_sell.pop();
}
return res;
}
};
2、1月3日 2042. 检查句子中的数字是否递增
思路:
定义一个栈,存储字符串中的数字。首先要根据空格分割字符串,并判断分割出的字符串是否可以转换成数字(数字的ASCII的范围是[48, 57]),如果可以转换成数字,判断该数字是否大于栈顶,如果大于则入栈,否则直接返回false。
C++代码:
class Solution {
public:
bool areNumbersAscending(string s) {
stack<int> st;
st.push(INT_MIN);
int i = 0, n = s.size();
while (i < n) {
int j = i + 1;
while (j < n && s[j] != ' ') j++;
string s_sub = s.substr(i, j - i);
if (isNum(s_sub)) {
int s_int = stoi(s_sub);
if (s_int > st.top()) st.push(s_int);
else return false;
}
i = j + 1;
}
return true;
}
bool isNum(string s) {
for (int i = 0; i < s.size(); ++i) {
int num = (int)s[i];
if (num > 57 || num < 48) {
return false;
}
}
return true;
}
};
3、1月4日 1802. 有界数组中指定下标处的最大值
思路:贪心+二分查找
题目要求寻找最大的nums[index],且nums元素和不超过maxSum。根据贪心思想,可以使除了nums[index]的之外的其他nums元素值尽可能小,即从nums[index]开始向左向右,下标每减小/增加1,元素值就减小1,直到nums边界或减小到1后保持为1不变。
利用二分查找来确定nums[index]的值,nums[index]的值一定在[1, maxSum)之间,所以设左边界为1,右边界为maxSum,利用valid函数判断nums[index](mid即左右边界中值)对应的numsSum是否不超过maxSum。numsSum由nums[index]、nums[index]左侧元素之和、nums[index]右侧元素之和3部分组成。
cal函数用来计算左右元素的和,利用梯形面积计算。如果元素减小不到1,计算公式为(大端+小端)*长度/2
;若元素能减小到1,则利用(大端 + 1 )*长度/2+1的个数
来计算
C++代码
class Solution {
public:
int maxValue(int n, int index, int maxSum) {
int left = 1, right = maxSum;
while (left < right) {
int mid = (left + right + 1) / 2;
if (valid(mid, n, index, maxSum)) left = mid;
else right = mid - 1;
}
return left;
}
bool valid(int mid, int n, int index, int maxSum) {
int left = index;
int right = n - index - 1;
return mid + cal(mid, left) + cal(mid, right) <= maxSum;
}
long cal(int big, int length) {
if (length + 1 < big) {
int small = big - length;
return (long)(big - 1 + small) * length / 2;
} else {
int ones = length - (big - 1);
return (long)(big - 1 + 1) * (big - 1) / 2 + ones;
}
}
};
4、1月6日 2180. 统计各位数字之和为偶数的整数个数
思路:暴力枚举
遍历[1, num]中的所有整数,判断整数各位数字之和是否为偶数。各位数字提取顺序为从低位到高位。
C++代码
class Solution {
public:
int countEven(int num) {
int res = 0;
for (int i = 1; i <= num; i++) {
int x = i, sum = 0;
while (x) {
sum += x % 10;
x /= 10;
}
if (sum % 2 == 0) res++;
}
return res;
}
};
5、1月7日 1658. 将 x 减到 0 的最小操作数
思路:双指针
首先将问题转换为:求和为sum-x的最长连续子数组的最大长度,其中sum是nums中元素的总和。定义两个快慢指针,快指针不断向右移动,直到快慢指针之间的元素和大于sum-x,再将慢指针向右移动。如果找到快慢指针的元素和等于num-x就与之前的操作数对比取最小。
C++代码
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int sum = 0, n = nums.size(), res = INT_MAX;
for (auto i : nums) sum += i;
int target = sum - x;
if (target == 0) return n;
int fastIndex = 0, slowIndex = 0, s = 0;
while (fastIndex < n) {
s += nums[fastIndex];
while (s > target && slowIndex < fastIndex) {
s -= nums[slowIndex];
++slowIndex;
}
if (s == target) res = min(res, n - 1 - fastIndex + slowIndex);
++fastIndex;
}
if (res == INT_MAX) return -1;
return res;
}
};
6、1月8日 2185. 统计包含给定前缀的字符串
思路:
遍历字串组中的每个字符串,判断每个字符串是否以pref作为前缀,如果满足条件,则结果加1。
C++代码
class Solution {
public:
int prefixCount(vector<string>& words, string pref) {
int res = 0;
for (auto s : words) {
int i = 0;
for (; i < pref.size(); i++) if (s[i] != pref[i]) break;
if (i == pref.size()) res++;
}
return res;
}
};
7、1月9日 1806. 还原排列的最少操作步数
思路:直接模拟
按照题目要求直接模拟即可
C++代码
class Solution {
public:
int reinitializePermutation(int n) {
vector<int> arr(n, 0);
for (int i = 0; i < n; i++) arr[i] = i;
vector<int> prem(arr.begin(), arr.end());
vector<int> target(arr.begin(), arr.end());
int step = 0;
while (1) {
for (int i = 0; i < n; i++) {
if (i % 2 == 0) arr[i] = prem[i / 2];
else arr[i] = prem[n / 2 + (i - 1) / 2];
}
prem = arr;
step++;
if (arr == target) break;
}
return step;
}
};
8、1月30日 1669. 合并两个链表
思路:直接模拟
在遍历list1之前,为了避免单独处理list1头结点插入的情况,引入虚拟头结点,还记录了list2的尾结点。
遍历list1过程中,如果到达下标a-1则将下标a-1的下一个结点置为list2的头结点,如果下标达到b+1,则将list2的尾结点置为list1的b+1结点。
C++代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) {
ListNode* _dummyHead = new ListNode(0);
_dummyHead->next = list1;
ListNode* cur1 = _dummyHead;
ListNode* tail = list2;
while (tail->next) tail = tail->next;
int i = -1;
while (cur1) {
if (i == a - 1) {
cur1->next = list2;
ListNode* cur2 = cur1;
while (cur2) {
if (i == b + 1) {
tail->next = cur2;
break;
}
cur2 = cur2->next;
i++;
}
break;
}
cur1 = cur1->next;
i++;
}
return _dummyHead->next;
}
};