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

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

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++;
			}
			else
				break;
		}
		for (int i = 0; i < tmp.size(); i++) {
			if (--tmp[i]) {
				pq.push(tmp[i]);
			}
		}
		res += !pq.empty() ? (n+1) : time;
	}
	return res;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值