力扣每日一题(2023年1月)

力扣每日一题(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;
    }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值