mirror of
https://gitlab.com/game-loader/hugo.git
synced 2025-04-20 05:52:07 +08:00
leetcode update
This commit is contained in:
parent
b281534992
commit
ab244c4817
@ -15232,8 +15232,11 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
## day227 2024-10-11
|
## day227 2024-10-11
|
||||||
### 1942. The Number of the Smallest Unoccupied Chair
|
|
||||||
|
### 1942. The Number of the Smallest Unoccupied Chair
|
||||||
|
|
||||||
There is a party where n friends numbered from 0 to n - 1 are attending. There is an infinite number of chairs in this party that are numbered from 0 to infinity. When a friend arrives at the party, they sit on the unoccupied chair with the smallest number.
|
There is a party where n friends numbered from 0 to n - 1 are attending. There is an infinite number of chairs in this party that are numbered from 0 to infinity. When a friend arrives at the party, they sit on the unoccupied chair with the smallest number.
|
||||||
|
|
||||||
For example, if chairs 0, 1, and 5 are occupied when a friend comes, they will sit on chair number 2.
|
For example, if chairs 0, 1, and 5 are occupied when a friend comes, they will sit on chair number 2.
|
||||||
@ -15246,10 +15249,12 @@ Return the chair number that the friend numbered targetFriend will sit on.
|
|||||||

|

|
||||||
|
|
||||||
### 题解
|
### 题解
|
||||||
|
|
||||||
本题先根据arrival时间对times数组中的子数组进行排序,遍历有序的times数组,将当前空余的最小座位号的座位分配给当前朋友,同时向map中对应key为当前朋友离开时间的value数组插入分配给当前朋友的座位号。在遍历每一个新的arrival时间时,先从map中找到所有小于等于该时间的leaving对应的座位号数组。这些座位在该时间之前都处于空闲状态,因此将这些座位号加入到一个最小堆中。这样给新的朋友分配座位时只需从最小堆弹出顶部数字即为当前空余的最小座位号。这就解决了如何获得空余座位中最小座位号的问题。如果最小堆中没有任何座位号,那就将一个新的座位号分配给该朋友,同时更新当前的最大座位号。
|
本题先根据arrival时间对times数组中的子数组进行排序,遍历有序的times数组,将当前空余的最小座位号的座位分配给当前朋友,同时向map中对应key为当前朋友离开时间的value数组插入分配给当前朋友的座位号。在遍历每一个新的arrival时间时,先从map中找到所有小于等于该时间的leaving对应的座位号数组。这些座位在该时间之前都处于空闲状态,因此将这些座位号加入到一个最小堆中。这样给新的朋友分配座位时只需从最小堆弹出顶部数字即为当前空余的最小座位号。这就解决了如何获得空余座位中最小座位号的问题。如果最小堆中没有任何座位号,那就将一个新的座位号分配给该朋友,同时更新当前的最大座位号。
|
||||||
|
|
||||||
### 代码
|
### 代码
|
||||||
```cpp
|
|
||||||
|
```cpp
|
||||||
class MinHeap {
|
class MinHeap {
|
||||||
private:
|
private:
|
||||||
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
|
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
|
||||||
@ -15280,7 +15285,7 @@ public:
|
|||||||
|
|
||||||
class Solution {
|
class Solution {
|
||||||
public:
|
public:
|
||||||
int smallestChair(vector<vector<int>>& times, int targetFriend) {
|
int smallestChair(vector<vector<int>>& times, int targetFriend) {
|
||||||
int max_seat = 0;
|
int max_seat = 0;
|
||||||
unordered_map<int, vector<int>> leave_map;
|
unordered_map<int, vector<int>> leave_map;
|
||||||
MinHeap available_seats;
|
MinHeap available_seats;
|
||||||
@ -15330,19 +15335,23 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## day228 2024-10-12
|
## day228 2024-10-12
|
||||||
### 2406. Divide Intervals Into Minimum Number of Groups
|
|
||||||
|
### 2406. Divide Intervals Into Minimum Number of Groups
|
||||||
|
|
||||||
You are given a 2D integer array intervals where intervals[i] = [lefti, righti] represents the inclusive interval [lefti, righti].
|
You are given a 2D integer array intervals where intervals[i] = [lefti, righti] represents the inclusive interval [lefti, righti].
|
||||||
|
|
||||||
You have to divide the intervals into one or more groups such that each interval is in exactly one group, and no two intervals that are in the same group intersect each other.
|
You have to divide the intervals into one or more groups such that each interval is in exactly one group, and no two intervals that are in the same group intersect each other.
|
||||||
|
|
||||||
Return the minimum number of groups you need to make.
|
Return the minimum number of groups you need to make.
|
||||||
|
|
||||||
Two intervals intersect if there is at least one common number between them. For example, the intervals [1, 5] and [5, 8] intersect.
|
Two intervals intersect if there is at least one common number between them. For example, the intervals [1, 5] and [5, 8] intersect.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 题解
|
### 题解
|
||||||
|
|
||||||
本题先将intervals数组按left排序,排序的好处在于无序的数组遍历时信息过于丰富使得我们无法确定一些性质,而排序后信息含量减少更方便我们确定一些关系。直观来说,排序后left是有序的,因此我们不需要再考虑left和left之间的关系,只需考虑right以及left和right之间的关系。
|
本题先将intervals数组按left排序,排序的好处在于无序的数组遍历时信息过于丰富使得我们无法确定一些性质,而排序后信息含量减少更方便我们确定一些关系。直观来说,排序后left是有序的,因此我们不需要再考虑left和left之间的关系,只需考虑right以及left和right之间的关系。
|
||||||
|
|
||||||
其实信息量多还是少的定义是有一点反直觉的,比如一个乱序的句子和一个正常的通顺句子,哪个句子中的信息含量更高呢,其实是乱序的句子。因为乱序的句子其表达的意义有更多的可能性,这就意味着句子中每个单词都包含更丰富的信息。而通顺的句子其句子含义基本是可以确定的,去掉一两个字甚至也不影响我们理解句子的含义,这也意味着句子中有些字的包含的信息其实非常少。再比如文言文会比白话文难懂,正是因为文言文遣词造句更加精简,使得每个句子中包含的信息相对于白话文大大增加了,因此我们可能一下子很难理解这么多的信息,这就是“难懂”。
|
其实信息量多还是少的定义是有一点反直觉的,比如一个乱序的句子和一个正常的通顺句子,哪个句子中的信息含量更高呢,其实是乱序的句子。因为乱序的句子其表达的意义有更多的可能性,这就意味着句子中每个单词都包含更丰富的信息。而通顺的句子其句子含义基本是可以确定的,去掉一两个字甚至也不影响我们理解句子的含义,这也意味着句子中有些字的包含的信息其实非常少。再比如文言文会比白话文难懂,正是因为文言文遣词造句更加精简,使得每个句子中包含的信息相对于白话文大大增加了,因此我们可能一下子很难理解这么多的信息,这就是“难懂”。
|
||||||
@ -15352,7 +15361,8 @@ Two intervals intersect if there is at least one common number between them. For
|
|||||||
回到本题,在left有序后,只需考虑left和right之间的关系,遍历数组时将right保存起来,如果我们想将下一个interval和当前已经在某个组中的interval放在同一组中,则下一个interval的left应该大于当前某个组中最大的right。如果有多个组的最大right都符合条件,我们就应该使用贪心的思路,将该interval放入所有组中right最小的组内。由此,我们只需要知道当前所有组中最小的right是多少,与下一个interval的left比较,如果left>right,则放入该组并更新该组的right,否则创建一个新的组将这个interval单独放入一个组中。由于只需要最小的right,可以利用最小堆,将所有组的right都放入最小堆,每次返回堆顶即可
|
回到本题,在left有序后,只需考虑left和right之间的关系,遍历数组时将right保存起来,如果我们想将下一个interval和当前已经在某个组中的interval放在同一组中,则下一个interval的left应该大于当前某个组中最大的right。如果有多个组的最大right都符合条件,我们就应该使用贪心的思路,将该interval放入所有组中right最小的组内。由此,我们只需要知道当前所有组中最小的right是多少,与下一个interval的left比较,如果left>right,则放入该组并更新该组的right,否则创建一个新的组将这个interval单独放入一个组中。由于只需要最小的right,可以利用最小堆,将所有组的right都放入最小堆,每次返回堆顶即可
|
||||||
|
|
||||||
### 代码
|
### 代码
|
||||||
```cpp
|
|
||||||
|
```cpp
|
||||||
class MinHeap {
|
class MinHeap {
|
||||||
private:
|
private:
|
||||||
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
|
std::priority_queue<int, std::vector<int>, std::greater<int>> pq;
|
||||||
@ -15405,5 +15415,222 @@ public:
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## day229 2024-10-13
|
||||||
|
|
||||||
|
### 632. Smallest Range Covering Elements from K Lists
|
||||||
|
|
||||||
|
You have k lists of sorted integers in non-decreasing order. Find the smallest range that includes at least one number from each of the k lists.
|
||||||
|
|
||||||
|
We define the range [a, b] is smaller than range [c, d] if b - a < d - c or a < c if b - a == d - c.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
|
||||||
|
本题要找出能在每个数组中都包含至少一个数字的最小数字范围,则整体上可以先将所有数组内的数字打上标记标记其所在的数组,再将全部数组合并并排序,随后用滑动窗口,每当窗口内包含所有数组至少一个数字记录当前窗口的大小并与当前最小值比较,进行最小值的更新。
|
||||||
|
|
||||||
|
对上述思路中存在的问题进行一步步分析,打上标记可以构造一个新的结构体。排序可以将所有数组合并后使用快排,因为数组均为有序数组,也可以使用选择排序,每次遍历所有数组选择这些数组中最小的第一个数字。但直觉上我觉得快排可能更快一些。最后一个很重要的问题,如何快速判断窗口内是否满足所有数组都至少包含一个数字?可以使用带索引的最小堆(优先级队列)。用一个数组记录窗口中包含的每个数组中数字的个数,在最小堆中放入的是数字个数和对应的数组索引的组合,用数字个数来比较。
|
||||||
|
|
||||||
|
将(数字个数,数组索引)正常放入最小堆,但在弹出堆顶元素时,先根据所有判断这个元素是不是过时的,如果数字个数和数组中记录的数字个数相同则说明该个数有效,否则舍弃并继续弹出下一个数字。
|
||||||
|
|
||||||
|
使用滑动窗口时,若堆顶数字为0,说明窗口内缺少了某个数组的数字(不需要知道具体是哪个,知道缺了就够了),则扩大窗口,直到堆顶数字大于0。根据窗口左右两端数字更新范围最小值,缩小窗口,直到堆顶数字重新为0,再扩大窗口。直到遍历完所有数字。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class MinValueArray {
|
||||||
|
private:
|
||||||
|
vector<int> arr; // 存储实际的数组值
|
||||||
|
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
|
||||||
|
// 优先级队列存储 <值, 索引> 对,使用 greater 比较器使其成为最小堆
|
||||||
|
|
||||||
|
public:
|
||||||
|
MinValueArray(int size) : arr(size, 0) {
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
pq.push({0, i});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(int index, int value) {
|
||||||
|
if (index < 0 || index >= arr.size()) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
arr[index] = value;
|
||||||
|
pq.push({value, index});
|
||||||
|
}
|
||||||
|
|
||||||
|
void addValue(int index, int value) {
|
||||||
|
if (index < 0 || index >= arr.size()) {
|
||||||
|
throw std::out_of_range("Index out of range");
|
||||||
|
}
|
||||||
|
arr[index] += value;
|
||||||
|
pq.push({arr[index], index});
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMin() {
|
||||||
|
while (!pq.empty()) {
|
||||||
|
auto [value, index] = pq.top();
|
||||||
|
if (value == arr[index]) {
|
||||||
|
return value; // 找到了当前的最小值
|
||||||
|
}
|
||||||
|
pq.pop(); // 这是一个过时的值,将其移除
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Array is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Num {
|
||||||
|
int num;
|
||||||
|
int array;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
vector<int> smallestRange(vector<vector<int>>& nums) {
|
||||||
|
vector<Num*> bigarray;
|
||||||
|
for (int i = 0; i < nums.size(); i++) {
|
||||||
|
for (auto n : nums[i]) {
|
||||||
|
Num* num = new Num;
|
||||||
|
num->num = n;
|
||||||
|
num->array = i;
|
||||||
|
bigarray.push_back(num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort(
|
||||||
|
bigarray.begin(), bigarray.end(),
|
||||||
|
[](const Num* a, const Num* b) -> bool { return a->num < b->num; });
|
||||||
|
int left = 0;
|
||||||
|
int right = 0;
|
||||||
|
int result_left = 0;
|
||||||
|
int result_right = 0;
|
||||||
|
int minrange = 10e5+1;
|
||||||
|
MinValueArray minheap(nums.size());
|
||||||
|
while (right < bigarray.size()) {
|
||||||
|
if (minheap.getMin() == 0) {
|
||||||
|
// 如果最小值为0,说明还没有包含所有列表的元素
|
||||||
|
minheap.addValue(bigarray[right]->array, 1);
|
||||||
|
right++;
|
||||||
|
} else {
|
||||||
|
// 已经包含了所有列表的元素,尝试缩小范围
|
||||||
|
while (minheap.getMin() > 0) {
|
||||||
|
int currentRange = bigarray[right - 1]->num - bigarray[left]->num;
|
||||||
|
if (currentRange < minrange) {
|
||||||
|
minrange = currentRange;
|
||||||
|
result_left = bigarray[left]->num;
|
||||||
|
result_right = bigarray[right - 1]->num;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动左指针
|
||||||
|
minheap.addValue(bigarray[left]->array, -1);
|
||||||
|
left++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理最后一个窗口
|
||||||
|
while (minheap.getMin() > 0) {
|
||||||
|
int currentRange = bigarray[right - 1]->num - bigarray[left]->num;
|
||||||
|
if (currentRange < minrange) {
|
||||||
|
minrange = currentRange;
|
||||||
|
result_left = bigarray[left]->num;
|
||||||
|
result_right = bigarray[right - 1]->num;
|
||||||
|
}
|
||||||
|
minheap.addValue(bigarray[left]->array, -1);
|
||||||
|
left++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result_left, result_right};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### 总结
|
||||||
|
|
||||||
|
我的解法有些复杂了,使用优先级队列其实没必要将子数组合并,直接从各个子数组中拿出第一个数放入优先级队列,记录当前队列中的最大值,弹出最小值来计算范围,并更新结果。弹出一个最小值后再从其所在的子数组头部继续放入一个值进入优先级队列,尝试更新最大值,计算新的范围,重复以上步骤直到某个子数组中所有数字都被使用完。这里主要利用了每个子数组都是有序的这一条件,这样每次取出数组头部数字即为当前数组中的最小值,也意味着与当前的数字范围最接近。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
vector<int> smallestRange(vector<vector<int>>& nums) {
|
||||||
|
// Priority queue to store (value, list index, element index)
|
||||||
|
priority_queue<pair<int, pair<int, int>>,
|
||||||
|
vector<pair<int, pair<int, int>>>, greater<>>
|
||||||
|
pq;
|
||||||
|
int maxVal = INT_MIN, rangeStart = 0, rangeEnd = INT_MAX;
|
||||||
|
|
||||||
|
// Insert the first element from each list into the min-heap
|
||||||
|
for (int i = 0; i < nums.size(); i++) {
|
||||||
|
pq.push({nums[i][0], {i, 0}});
|
||||||
|
maxVal = max(maxVal, nums[i][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue until we can't proceed further
|
||||||
|
while (pq.size() == nums.size()) {
|
||||||
|
auto [minVal, indices] = pq.top();
|
||||||
|
pq.pop();
|
||||||
|
int row = indices.first, col = indices.second;
|
||||||
|
|
||||||
|
// Update the smallest range
|
||||||
|
if (maxVal - minVal < rangeEnd - rangeStart) {
|
||||||
|
rangeStart = minVal;
|
||||||
|
rangeEnd = maxVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If possible, add the next element from the same row to the heap
|
||||||
|
if (col + 1 < nums[row].size()) {
|
||||||
|
int nextVal = nums[row][col + 1];
|
||||||
|
pq.push({nextVal, {row, col + 1}});
|
||||||
|
maxVal = max(maxVal, nextVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {rangeStart, rangeEnd};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## day230 2024-10-14
|
||||||
|
|
||||||
|
### 2530. Maximal Score After Applying K Operations
|
||||||
|
|
||||||
|
You are given a 0-indexed integer array nums and an integer k. You have a starting score of 0.
|
||||||
|
|
||||||
|
In one operation:
|
||||||
|
|
||||||
|
choose an index i such that 0 <= i < nums.length,
|
||||||
|
increase your score by nums[i], and
|
||||||
|
replace nums[i] with ceil(nums[i] / 3).
|
||||||
|
Return the maximum possible score you can attain after applying exactly k operations.
|
||||||
|
|
||||||
|
The ceiling function ceil(val) is the least integer greater than or equal to val.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### 题解
|
||||||
|
|
||||||
|
本题可以使用贪心算法,每次都增加当前nums数组中的最大数字,增加后处理最大数字,再继续找到nums中的最大数字并增加。显然我们需要在一个不断变化的数组中一直返回其最大值。则最大堆符合要求,因此构造一个最大堆,将数组数字全部放进去,每次弹出堆顶数字并加到结果中,按照规则将该数字变换为1/3重新放入堆中。如此直到增加次数达到k。
|
||||||
|
|
||||||
|
### 代码
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class Solution {
|
||||||
|
public:
|
||||||
|
long long maxKelements(vector<int>& nums, int k) {
|
||||||
|
priority_queue<long long int> pq;
|
||||||
|
for (auto num : nums){
|
||||||
|
pq.push(num);
|
||||||
|
}
|
||||||
|
long long int result = 0;
|
||||||
|
long long int temp;
|
||||||
|
while(k>0){
|
||||||
|
result += pq.top();
|
||||||
|
temp = pq.top() / 3 + ((pq.top()%3)?1:0);
|
||||||
|
pq.pop();
|
||||||
|
pq.push(temp);
|
||||||
|
k--;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
Loading…
Reference in New Issue
Block a user