杀死进程-LeetCode-582

英文版

582. Kill Process

Given n processes, each process has a unique PID (process id) and its PPID (parent process id).

Each process only has one parent process, but may have one or more children processes. This is just like a tree structure. Only one process has PPID that is 0, which means this process has no parent process. All the PIDs will be distinct positive integers.

We use two list of integers to represent a list of processes, where the first list contains PID for each process and the second list contains the corresponding PPID.

Now given the two lists, and a PID representing a process you want to kill, return a list of PIDs of processes that will be killed in the end. You should assume that when a process is killed, all its children processes will be killed. No order is required for
the final answer.

Example 1:

Input: 
pid =  [1, 3, 10, 5]
ppid = [3, 0, 5, 3]
kill = 5
Output: [5,10]
Explanation: 
           3
         /   \
        1     5
             /
            10
Kill 5 will also kill 10.

Note:

  • The given kill id is guaranteed to be one of the given PIDs.
  • n >= 1.

中文版

给你n个进程,每个进程都有一个唯一的PID(process id)和PPID(parent process id)。每个进程最多只有一个父进程,但可能有多个子进程。如果一个进程的PPID为0,表示它没有父进程。如果一个进程被杀死,那么它的子进程也会被杀死。输入两个相同长度的整数链表,第一个链表是每个进程的PID,第二个链表是对应位置进程的PPID,再输入一个将被杀死的进程的PID,请输出所有将被杀死的进程的PID。

例如,输入的PID链表为[1, 3, 10, 5],PPID链表为[3, 0, 5, 3],将被杀死的进程的PID为5,那么最终被杀死的进程有两个,PID分别为5和10。这是因为PID为10的进程是PID为5的进程的子进程。

分析

由于每个进程最多只有一个父进程,但可能有多个子进程,因此进程之间的关系可以用一棵树来表示。父进程对应树中的父节点,而子进程对应树中的子节点。如果进程[1, 3, 10, 5]的父进程为[3, 0, 5, 3],那么这些进程可以构成如下的一棵树:如样例中的树结构

因此,解决这个问题的第一步是根据输入的PID和PPID两个链表构建出一个树。树是一种特殊的图(Graph)。图有两种常用的表示方法,一是基于邻接表,二是基于邻接矩阵。由于树是一类比较稀疏的图,一般用邻接表更为高效。

用邻接表来表示一棵树,我们可以把整棵树存到一个HashMap中。这个HashMap的Key为进程的PID,Value为对应进程的所有子进程的PID,也就是树中对应节点的所有子节点。下面是根据PID和PPID链表构建树的参考代码:

private Map<Integer, List<Integer>> buildTree(List<Integer> pid, List<Integer> ppid) {
	Map<Integer, List<Integer>> processTree = new HashMap<>();
	Iterator<Integer> iterator1 = pid.iterator();
	Iterator<Integer> iterator2 = ppid.iterator();
	while (iterator1.hasNext() && iterator2.hasNext()) {
		int p = iterator1.next();
		int pp = iterator2.next();
		if (!processTree.containsKey(pp)) {
			processTree.put(pp, new LinkedList<>());
		}
		processTree.get(pp).add(p);
	}
	return processTree;
}

由于杀死一个进程会杀死它的所有子进程,那么对应到树中,杀死某个节点对应的进程,也就会杀死以该节点为根节点的子树中所有节点对应的所有进程。因此,我们可以把问题转化为:如何遍历以某节点为根节点的子树。

虽然这棵由进程构成的树不一定是二叉树(因为节点的子节点数目可能大于2),但遍历算法大同小异,仍然可以按照广度优先或者深度优先的顺序遍历。下面分别介绍两种方法的代码。

深度优先解

树的深度优先搜索的代码有递归和非递归两种写法。通常基于递归的代码更为简洁,深度优先算法需要定义一个递归函数用来杀死某个进程,如下面的辅助函数kill。在函数kill中,每当杀死进程target,如果它有子进程,则递归调用函数kill去杀死它的每一个子进程child。

public List<Integer> killProcess(List<Integer> pid, List<Integer> ppid, int target){
	Map<Integer, List<Integer>> processTree = buildTree(pid, ppid);
	List<Integer> result = new LinkedList<>();
	kill(processTree, target, result);
	return result;
}

private void kill(Map<Integer, List<Integer>> processTree, int target, List<Integer> result) {
	result.add(target);
	if (!processTree.containsKey(target)) {
		return ;
	}
	for (int child : processTree.get(target)) {
		kill(processTree, child, result);
	}
}

也可以不构建树结构直接,利用这两个数组进行递归杀死进程。

public List<Integer> killProcess(List<Integer> pid, List<Integer> ppid, int kill) {
        ArrayList<Integer> ans = new ArrayList<Integer>();
        ans.add(kill);
        for (int i = 0; i < ppid.size(); i++) {
            if (ppid.get(i) == kill) {
                ans.addAll(killProcess(pid, ppid, pid.get(i)));
            }
        }
        return ans;
}

广度优先解

基于广度优先的代码通常会用到队列(queue)。我们首先把第一个待杀死的进程的PID放入队列中,接下来只要队列中还有待杀死的进程,就重复执行如下步骤:首先从队列中去除一个进程,杀死它(即添加到输出的链表中);接着,如果该进程有子进程则把子进程添加到队列中。

public List<Integer> killProcess(List<Integer> pid, List<Integer> ppid, int target) {
	Map<Integer, List<Integer>> processTree = buildTree(pid, ppid));
	List<Integer> result = new LinkedList<>();
	Queue<Integer> queue = new LinkedList<>();
	queue.add(target);
	while (!queue.isEmpty()) {
		int process = queue.remove();
		result.add(process);
		if (processTree.containsKey(process)) {
			for (int child : processTree.get(process)) {
				queue.add(child);
			}
		}
	}
	return result;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是电话号码的字母组合问题的 C++ 代码实现: ``` class Solution { public: vector<string> letterCombinations(string digits) { unordered_map<char, string> mapping = { {'2', "abc"}, {'3', "def"}, {'4', "ghi"}, {'5', "jkl"}, {'6', "mno"}, {'7', "pqrs"}, {'8', "tuv"}, {'9', "wxyz"} }; vector<string> res; if (digits.empty()) { return res; } string combination; backtrack(res, combination, digits, 0, mapping); return res; } void backtrack(vector<string>& res, string& combination, string& digits, int index, unordered_map<char, string>& mapping) { if (index == digits.size()) { res.push_back(combination); return; } for (char c : mapping[digits[index]]) { combination.push_back(c); backtrack(res, combination, digits, index + 1, mapping); combination.pop_back(); } } }; ``` 其中 `letterCombinations` 函数用来生成所有的字母组合,`backtrack` 函数用来进行回溯操作。在 `letterCombinations` 函数中,首先根据数字字符和字母的映射关系创建了一个 `unordered_map` 对象 `mapping`。然后定义了一个空字符串 `combination` 和一个空 vector `res` 来保存最终结果。最后调用了 `backtrack` 函数来生成所有的字母组合。在 `backtrack` 函数中,首先判断是否达到了数字字符串的末尾,如果是,则将当前的 `combination` 字符串保存到 `res` 中。否则,遍历当前数字字符所能表示的所有字母,依次加入到 `combination` 字符串中,然后递归调用 `backtrack` 函数,添加下一个数字字符所能表示的字母。递归完成后,需要将 `combination` 字符串还原到上一个状态,以便进行下一次回溯。最终返回 `res` 数组即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值