LeetCode——133.Clone Graph && 621.Task Scheduler

来源:互联网 发布:淘宝天猫分销平台 编辑:程序博客网 时间:2024/06/06 20:08

本周两道题,一道是图的题,一道是贪心的题。

Clone Graph


1.问题描述

克隆一个无向图,已知节点的形式,是一个结构体,有一个label和一个邻接表,表示所有邻居节点。节点的label值是唯一的,图可能是有环,甚至是自环。


2.思路

题目思路很简单,但是做起来就发现还是要注意很多细节。首先这个题目可使用BFS或者DFS形式来实现,BFS的话就是对于一个点,把它的邻接节点都建了,压入队列中,接着取队列中下一个节点,再对其邻接节点进行创建或者链接。DFS的方法是使用递归,建立自己的节点,然后递归地取建立子图。

下面是BFS的形式。对于一个点来说,它的邻接节点可能是已经在之前创建过的,也就那么此时就只能找出已创建过的那个节点,然后添加到该节点的邻接表中。所以,我们需要一个容器来存放已经创建的节点。在实现时,我使用vector容器vec存储已经创建的节点,当遍历一个节点的子节点时,通过查找vec判断该子节点是否已经创建,如果已经创建,则无需再new一次,直接使用即可。

在这里注意,需要维持两个队列,一个队列存放的是原图的节点,另一个队列存放的是新图的节点,因为你需要遍历原图节点,得知当前节点的邻接表,同时需要根据当前节点的邻接表,为新图的同样是该节点,创建或查找到邻接节点,并链接到新图节点上。两个队列的遍历顺序,入队顺序都是一样的。


3.代码

struct UndirectedGraphNode {int label;vector<UndirectedGraphNode *> neighbors;UndirectedGraphNode(int x) : label(x) {};};UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {if (node == NULL)return NULL;queue<UndirectedGraphNode*> q;queue<UndirectedGraphNode*> newq;vector<UndirectedGraphNode*> vec;q.push(node);UndirectedGraphNode* newroot = new UndirectedGraphNode(node->label);newq.push(newroot);vec.push_back(newroot);bool flag = 0;while (!q.empty()) {UndirectedGraphNode* top = q.front();q.pop();UndirectedGraphNode* newtop = newq.front();newq.pop();for (int i = 0; i < top->neighbors.size(); i++) {for (int j = 0; j < vec.size(); j++) {if (vec[j]->label == top->neighbors[i]->label) {newtop->neighbors.push_back(vec[j]);flag = 1;break;}}if (flag == 0) {UndirectedGraphNode* next = new UndirectedGraphNode(top->neighbors[i]->label);q.push(top->neighbors[i]);vec.push_back(next);newtop->neighbors.push_back(next);newq.push(next);}flag = 0;}}return newroot;}


Task Scheduler


1.问题描述

CPU调度任务,给出任务列表和间隔n,每一个任务需要一个CPU周期,同一个任务之间必须间隔n个空隙,求最少需要多少个CPU周期。例子如下:

输入: tasks = ['A','A','A','B','B','B'], n = 2, 输出: 8

解释:A -> B -> idle -> A -> B -> idle -> A -> B.


2.思路

对所有任务数进行统计,则按照上面的例子tasks = ['A','A','A','B','B','B'],得到一个统计结果是<A, 3>,<B, 3>,按照任务量最大的先做,n是2,先选一个A,接着是B,没有第三个可选,A和下一个A之间至少要有2个空隙,所以这里只能插入一个idle,需要的周期是3;下一轮<A, 2>,<B, 2>,再次选择A,B,idle,需要的周期累计为6,得到结果<A, 1>,<B, 1>;第三轮,选了A,B之后,发现所有任务都做完了,不需要再插入idle了,所以需要的周期是6+2为8;

按照对题目的理解,首先是按任务量顺序选n+1个后,此时已做的任务项对应的任务量会对应减1,当遍历队伍发现不足n+1个不同任务可选时,如果任务队伍中还有任务,那需要加入空闲时间段(idle),如果没有任务了,那就直接结束了。对所有任务的任务量重新排序,依旧是选择任务量大的先开始做。

我们这里再举一个例子验证思路。假设tasks = ['A','A','A','A','A','A', 'B','C','D','E','F','G'],此时可以得到一个统计数据是[6, 1, 1, 1, 1, 1, 1],此处省略了字符,因为不需要列出具体的任务顺序。给定n = 2,那么第一轮后剩下的是[5, 0, 0, 1, 1, 1, 1];此时要对任务进行重新排序,0不需要加入队伍中了,得到[5, 1, 1, 1, 1],此时周期数是3;第二轮后得到[4, 1, 1],周期数是6;第三轮后得到[3],周期数累计是9;第四轮,只有一个字符,选了A之后,没有其他能选的了,但是任务还没做完,所以只能加入idle,那么累计周期数是12,得到[2];接下来与上一轮类似,最后得到结果是16,看来这个思路是可以的。

到了写算法的问题,选择什么样的数据结构来存储任务数量?使用vector的话,实际上进行删除,排序,处理比较麻烦,所以这里选了可以自动排序,且可以简单pop出元素的priority_queue实现。算法思想:

I. 统计不同任务的数量,将数量加入优先队列pq中;

II. 当pq不为空时:

i. 循环n+1次,将pq的前n+1个字符pop出,加入临时数组temp中,记录压入temp数组的次数time;如果不足n+1个,则直接break;

ii. 对于临时数组temp中的元素,取出来,减1,如果还是大于0,压回pq中;

iii. 判断此时pq是否为空,如果为空,说明没有需要执行的任务了,那么此时累计结果res只需要加上time个周期;如果此时pq不为空,说明后面还有任务要执行,但是必须要空闲一段时间,那么,累计结果res就需要加上n+1,表明这一轮后需要的周期数。

3.代码

int leastInterval(vector<char>& tasks, int n) {vector<int> counter;int size = tasks.size();if (size == 0)return 0;if (n == 0)return size;for (int i = 0; i < 26; i++)counter.push_back(0);for (int i = 0; i < size; i++) {counter[tasks[i] - 'A'] ++;}priority_queue<int> pq;for (int i = 0; i < 26; i++) {if (counter[i] != 0)pq.push(counter[i]);}int res = 0;while (!pq.empty()) {int time = 0;vector<int> tmp;for (int i = 0; i < (n+1); i++) {if (!pq.empty()) {tmp.push_back(pq.top());pq.pop();time++;}elsebreak;}for (int i = 0; i < tmp.size(); i++) {if (--tmp[i]) {pq.push(tmp[i]);}}res += !pq.empty() ? (n+1) : time;}return res;}




原创粉丝点击