链表 (part2 )

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 数据库的 题目 ,我就 不参和了 。。。

你可能感兴趣的:(链表,数据结构)