T1:我的日程 安排表(I)
实现一个 MyCalendar
类来存放你的日程安排。如果要添加的日程安排不会造成 重复预订 ,则可以存储这个新的日程安排。
当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生 重复预订 。
日程可以用一对整数 start
和 end
表示,这里的时间是半开区间,即 [start, end)
, 实数 x
的范围为, start <= x < end
。
实现 MyCalendar
类:
MyCalendar()
初始化日历对象。boolean book(int start, int end)
如果可以将日程安排成功添加到日历中而不会导致重复预订,返回 true
。否则,返回 false
并且不要将该日程安排添加到日历中。解:
1.关键 :
(1)利用 map 存储下 每一个时间段
(2)主要 判断是否 发生 交叉 不好处理 , 朴素的想法就是 遍历 map
改进:
其实 ,答案的 判断 时间段是否 交叉的 方法 更加 好:
2个时间段 发生交叉 --iff -- start1 < end2 && start2 < end1 ,反正就是 2者的 开始 时间 都小于 另外一方的 结束 时间
2.代码:
class MyCalendar {
public:
//关键 是 怎么 判断 2个时间 是否 产生交叉
//最 简单的 方法就是 遍历!暴力搜索 -- 利用map进行存储
unordered_map Map;
MyCalendar() {
}
bool book(int start, int end) {
for(auto it:Map)
{
if((start>= it.first && start< it.second) || (end >it.first && end<=it.second) || (start<=it.first && end>it.second)) //少考虑了一种包含情况
{
return false;
}
}
Map[start] =end;
return true;
}
};
T2: 检测 是否 发生时间段的 3重 重叠 的 时间安排表
实现一个 MyCalendar
类来存放你的日程安排。如果要添加的时间内不会导致三重预订时,则可以存储这个新的日程安排。
MyCalendar
有一个 book(int start, int end)
方法。它意味着在 start
到 end
时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end)
, 实数 x
的范围为, start <= x < end
。
当三个日程安排有一些时间上的交叉时(例如三个日程安排都在同一时间内),就会产生三重预订。
每次调用 MyCalendar.book
方法时,如果可以将日程安排成功添加到日历中而不会导致三重预订,返回 true
。否则,返回 false
并且不要将该日程安排添加到日历中。
请按照以下步骤调用MyCalendar
类: MyCalendar cal = new MyCalendar();
MyCalendar.book(start, end)
解:
1.关键:(利用 朴素的 暴力 搜索法)
(1)利用 2个 vector容器, Vec1 用于 存储 已经遍历过的 时间段
Vec2 用于 存储 发生了 时间2次 重叠的时间段 ,Vec中的元素都是 pair
//不能用Map,因为 可能 有 相同的 开始 时间
(2)每次 添加一个 新的 时间段, 首先 搜索 是否 和 Map2中的某个时间段 发生重叠,如果重叠直接返回 false
(3)然后 搜索 是否和第一次 遍历的 时间段的Map1中的 某个时间段 发生 重叠(利用时间重叠判断条件) , 然后 将真正重叠的 那个时间段 冲出到 Map2中
2.代码:
class MyCalendarTwo {
public:
MyCalendarTwo() {
}
bool book(int start, int end) {
for (auto &[l, r] : overlaps) {
if (l < end && start < r) {
return false;
}
}
for (auto &[l, r] : booked) {
if (l < end && start < r) {
overlaps.emplace_back(max(l, start), min(r, end));
}
}
booked.emplace_back(start, end);
return true;
}
private:
vector> booked;
vector> overlaps;
};
解法二: 利用 “插旗子计数法”
1.关键:
(1)必须 利用 有序 map,因为 遍历的 时候 必须从前往后 有序遍历
(2)如果 某个时刻 计数max >=3 说明 存储了 这个日程安排之后 会出现3次重叠的情况
2.代码:
class MyCalendarTwo {
public:
MyCalendarTwo() {
}
bool book(int start, int end) {
//法二: 利用“插旗子”计数的思想
int max = 0;
cnt[start]++;
cnt[end]--; //初始值 都是0
for(auto &[_,flag] : cnt)
{
max+=flag;
if(max > 2)
{
cnt[start]--;
cnt[end]++;
return false;
}
}
return true;
}
map cnt;
};
T3: 我的日程 安排表 version3
当 k
个日程安排有一些时间上的交叉时(例如 k
个日程安排都在同一时间内),就会产生 k
次预订。
给你一些日程安排 [start, end)
,请你在每个日程安排添加后,返回一个整数 k
,表示所有先前日程安排会产生的最大 k
次预订。
实现一个 MyCalendarThree
类来存放你的日程安排,你可以一直添加新的日程安排。
MyCalendarThree()
初始化对象。int book(int start, int end)
返回一个整数 k
,表示日历中存在的 k
次预订的最大值。解:
1.关键:
(1) 利用 讨论区的 “插旗子”计数 思想:
举例子 [10, 15) [12, 18)
插旗子 计数 <10, 1> <12, 1> <15, -1> <18, -1> 如果 两个Interval 不相交,则 连续两个 插旗计数的 和 必然等于零,一个+1,一个-1
如果 两个 Interval 相交,则 连续两个插旗计数 的和 必然大于0,一个+1,一个+1
(2)总之 , 相对于之前 就是 多了 一个 打擂台的 环节 获得 最终的 最大值 ans ,然后 返回即可
2.代码:
class MyCalendarThree {
public:
MyCalendarThree() {
}
int book(int startTime, int endTime) {
//借助 之前的解法 , 利用“插旗子计数”的 思想进行求解
int max_num = 0;
cnt[startTime]++;
cnt[endTime]--;
int ans=0;
for(auto &[_,flag]:cnt)
{
max_num+=flag;
ans = max(max_num,ans);
}
return ans;
}
map cnt;
};
T4:简单 2层循环: excel的 某个范围内的 所以 下标
Excel 表中的一个单元格 (r, c)
会以字符串 "
的形式进行表示,其中:
即单元格的列号 c
。用英文字母表中的 字母 标识。
1
列用 'A'
表示,第 2
列用 'B'
表示,第 3
列用 'C'
表示,以此类推。|
即单元格的行号 r
。第 r
行就用 整数 r
标识。给你一个格式为 "
的字符串 s
,其中
表示 c1
列,
表示 r1
行,
表示 c2
列,
表示 r2
行,并满足 r1 <= r2
且 c1 <= c2
。
找出所有满足 r1 <= x <= r2
且 c1 <= y <= c2
的单元格,并以列表形式返回。单元格应该按前面描述的格式用 字符串 表示,并以 非递减 顺序排列(先按列排,再按行排)。
解:
1.关键:
(1) 字母作为 外层循环 ,数字 作为 内层循环
2.代码:
class Solution {
public:
vector cellsInRange(string s) {
//其实 就是从 第一列 开始 ,一次 将每一列 加入到 vec中
//2层循环即可
//(1)先取出 字母 和 数字
char alpha1 = s[0];
char alpha2 = s[3];
char num1 = s[1];
char num2 = s[4];
vector ans;
//(2)2层循环
for(char ch=alpha1 ; ch<=alpha2 ; ch++)
{
for(char i=num1 ;i <=num2 ;i++)
{
//以string的形式返回
string str;
str = str+ch + i;
ans.push_back(str);
}
}
return ans;
}
};
T5:最长 数对 链
给你一个由 n
个数对组成的数对数组 pairs
,其中 pairs[i] = [lefti, righti]
且 lefti < righti
。
现在,我们定义一种 跟随 关系,当且仅当 b < c
时,数对 p2 = [c, d]
才可以跟在 p1 = [a, b]
后面。我们用这种形式来构造 数对链 。
找出并返回能够形成的 最长数对链的长度 。
你不需要用到所有的数对,你可以以任何顺序选择其中的一些数对来构造。
解:
1.关键:
(1)一眼 看出 这个题目 就是 会议安排的 问题 ,利用 贪婪法 就可以 得到 最多的 会议安排个数
(2)先 对 pairs二维数组中的 end时间点 进行归并排序
(3)然后 每次选出 不冲突的 结束 时间最早的 那个即可
2.代码:
class Solution {
public:
int findLongestChain(vector>& pairs) {
//返回 最长数对链的 长度
//悟: 这不就是 那个 经典贪婪法 的 会议安排个数 最多的 问题吗
//先 按照 最先end的时间 进行 排序 ,然后 每次 选出 最先end但是 又不
//冲突的 会议,cnt++
//(1)练习 : 利用归并排序对 pairs的 数组进行排序
pairs = merge_sort(pairs); //此时的pairs已经按照end时间进行了排序,然后挑选
//(2)每次挑选 最先结束的 那个会议
int last_end = -9999;
int cnt= 0;
int size= pairs.size();
for(int i=0;i<=size-1;i++)
{
if(pairs[i][0] > last_end)
{
cnt++;
last_end = pairs[i][1]; //贪婪法
}
}
return cnt;
}
vector> merge_sort(vector>& pairs)
{
//这是一个 递归函数 , 一直分治到 1个元素 作为出口
if(pairs.size() == 1)
{
return pairs;
}
// 左边为 向下 取整的一半 ,右边是另一半
vector> left;
vector> right;
int size = pairs.size();
int half = size/2;
for(int i=0;i> merge(vector>& a,vector>& b)
{
vector> res;
//这个函数的 参数是 2个有序的顺序表 , 只要合并为一个有序的顺序表即可
int size1=a.size();
int size2=b.size();
int i=0,j=0;
while(i<=size1-1 && j<=size2-1)
{
if(a[i][1] <= b[j][1])
{
res.push_back(a[i]);
i++;
}
else
{
res.push_back(b[j]);
j++;
}
}
//收尾:
if(i>size1-1)
{
while(j<=size2-1)
{
res.push_back(b[j]);
j++;
}
}
if(j>size2-1)
{
while(i<=size1-1)
{
res.push_back(a[i]);
i++;
}
}
//
return res;
}
};
剩下 都是 一些 SQL 数据库的 题目 ,我就 不参和了 。。。