之前收藏了极客时间的算法训练营3期 共21课,计划每一课写博客来记录学习,主要形式为
方法类型1
题1
题解
题2
题解
方法类型2
题1
题解
……
题目大体来自leetcode 和 acwing
主要记录和理解代码,所以基本完全搬运了视频题解代码,
个人学习感受体现在大致思路的总结和注释上。
第一题
本题可以用hash_map来标记rk值,也可以使用计数排序
1.hash + 快排
class Solution {
public:
vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
for (int i = 0; i < arr2.size(); i++) {
arr2Orders[arr2[i]] = i;
}
sort(arr1.begin(), arr1.end(), [&](int A, int B) {
int rkA = arr2Orders.find(A) != arr2Orders.end() ? arr2Orders[A] : arr2.size();
int rkB = arr2Orders.find(B) != arr2Orders.end() ? arr2Orders[B] : arr2.size();
return rkA < rkB || (rkA == rkB && A < B);
});
return arr1;
}
private:
unordered_map<int, int> arr2Orders;
};
2.计数
class Solution {
public:
vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
vector<int> cnt(1001, 0);
for (int num : arr1) {
cnt[num]++;
}
vector<int> ans;
for (int val : arr2) {
while (cnt[val] > 0) {
ans.push_back(val);
cnt[val]--;
}
}
for (int i = 0; i < 1001; i++) {
while (cnt[i] > 0) {
ans.push_back(i);
cnt[i]--;
}
}
return ans;
}
private:
};
第二题
本题使用双指针,其实对于数组的排序只需要排序开始结点即可,
也可以使用差分,用前缀和来表示有效。
遇到可以连接上的左端,就更新最大的右端,否则将已有区间加入答案
1.双指针
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort (intervals.begin(), intervals.end(),
[] (const vector<int>& A, const vector<int>& B){
return A[0] < B[0] || (A[0] == B[0] && A[1] < B[1]);
});
int farthest = -1;
int start = -1;
vector<vector<int>> ans;
for (vector<int> interval : intervals) {
if (interval[0] <= farthest) {
farthest = max(farthest, interval[1]);
}
else {
if (start != -1)
ans.push_back({start, farthest});
start = interval[0];
farthest = interval[1];
}
}
//if (start != -1)
ans.push_back({start, farthest});
return ans;
}
};
2.差分
将差分事件数组排序
{3, 1}代表在3的位置开始
{6,-1}代表在5也就是( 6 - 1)的位置结束,差分应该先算结束再算开始,正好按照-1到1的顺序排序了。
算答案就是算当前缀和为0时,记录答案。
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<pair<int, int>> events;
for (vector<int> interval : intervals) {
events.push_back({interval[0], 1});
events.push_back({interval[1] + 1, -1});
}
sort (events.begin(), events.end());
int covering = 0;
int start;
vector<vector<int>> ans;
for (const pair<int, int>& event : events) {
if (covering == 0) start = event.first;
covering += event.second;
if (covering == 0) {
ans.push_back({start, event.first - 1});
}
}
return ans;
}
};
第三题
快速选择算法,是快排的一半
对于pivot的选择,需要保证返回的是pivot的位置,在函数相应位置已经标注
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
return quickSort(nums, 0, nums.size() - 1, nums.size() - k);
}
private:
int quickSort (vector<int>& nums, int l, int r, int index) {
if (l >= r) return nums[l];
int pivot = partition(nums, l, r);
if (index <= pivot) return quickSort(nums, l, pivot, index);
else return quickSort(nums, pivot + 1, r, index);
}
int partition(vector<int>& q, int l, int r) {
int pivot = l + rand() % (r - l + 1);
int pivotVal = q[pivot];
while (l <= r) {
while (q[l] < pivotVal) l++;
while (q[r] > pivotVal) r--;
//函数返回的是pivot所在的位置,所以当l = r时要进入判断r的位置是否合理
if (l == r) break;
if (l < r) {
int tmp = q[l];
q[l] = q[r];
q[r] = tmp;
l++, r--;
}
}
return r;
}
};
第四题
归并排序的过程中,计数这种特殊逆序对。
class Solution {
public:
int reversePairs(vector<int>& nums) {
mergeSort(nums, 0, nums.size() - 1);
return ans;
}
private:
int ans = 0;
void mergeSort (vector<int>& q, int l, int r) {
if (l >= r) return;
int mid = (l + r) >> 1;
mergeSort(q, l, mid);
mergeSort(q, mid + 1, r);
int j = mid;
for (int i = l; i <= mid; i++) {
while (j < r && q[i] > 2l * q[j + 1]) j++;
ans += j - mid;
}
int i = l;
j = mid + 1;
vector<int> tmp;
while (i <= mid && j <= r) {
if (q[i] <= q[j]) tmp.push_back(q[i++]);
else tmp.push_back(q[j++]);
}
while (i <= mid) tmp.push_back(q[i++]);
while (j <= r) tmp.push_back(q[j++]);
for (int i = l, k = 0; i <= r; i++) {
q[i] = tmp[k++];
}
}
};