C++でプロセスを強制終了する方法:BFSを使った実装解説
n個のプロセスがあると仮定します。各プロセスには、PID(プロセスID)と呼ばれる一意の識別子が割り当てられており、さらにPPID(親プロセスID)も持っています。
各プロセスが持てる親プロセスは1つだけですが、子プロセスは1つでも複数でも構いません。これはまさに木構造と同じ形です。PPIDが0になるプロセスは1つだけであり、それはそのプロセスに親が存在しないことを意味します。また、すべてのPIDは一意な正の整数です。
問題の概要
ここでは、2つの整数リストを使ってプロセスの一覧を表現します。1つ目のリストには各プロセスのPIDが含まれ、2つ目のリストにはそれに対応するPPIDが含まれます。このとき、強制終了したいプロセスを表すPIDが与えられたら、最終的に終了されるすべてのプロセスのPIDリストを求める必要があります。ただし、あるプロセスが終了すると、その子プロセスもすべて連鎖的に終了されるものと仮定します。
例として、入力が pid = [1, 3, 10, 5]、ppid = [3, 0, 5, 3]、kill = 5 の場合を考えてみましょう。このとき出力は [5, 10] となります。
これは、プロセス5を終了すると、その子プロセスである10も一緒に終了されるためです。
解決のためのアプローチ
この問題は、親から子への関係をマップで管理し、幅優先探索(BFS)で終了対象を収集することで効率的に解けます。具体的な手順は以下の通りです。
- 親プロセスIDをキー、子プロセスIDのリストを値とするマップ「child」を定義します。
- n := pid のサイズとします。
- 結果を格納する配列 ret を定義します。
- i := 0 から開始し、i < n の間、i を1ずつ増やしながら以下を繰り返します。
- u := ppid[i]
- v := pid[i]
- child[u] の末尾に v を追加します。
- キュー q を定義し、kill を挿入します。
- q が空でない間、以下を繰り返します。
- curr := q の先頭要素
- q から先頭要素を取り出します。
- ret の末尾に curr を追加します。
- i := 0 から開始し、i < child[curr] のサイズの間、i を1ずつ増やしながら child[curr][i] を q に挿入します。
- ret を返します。
C++での実装例
理解を深めるために、以下の実装例を見てみましょう。
#include <bits/stdc++.h>
using namespace std;
void print_vector(vector<auto> v){
cout << "[";
for(int i = 0; i<v.size(); i++){
cout << v[i] << ", ";
}
cout << "]"<<endl;
}
class Solution {
public:
vector<int> killProcess(vector<int>& pid, vector<int>& ppid, int kill) {
map<int, vector<int> > child;
int n = pid.size();
vector<int> ret;
for (int i = 0; i < n; i++) {
int u = ppid[i];
int v = pid[i];
child[u].push_back(v);
}
queue<int> q;
q.push(kill);
while (!q.empty()) {
int curr = q.front();
q.pop();
ret.push_back(curr);
for (int i = 0; i < child[curr].size(); i++) {
q.push(child[curr][i]);
}
}
return ret;
}
};
main(){
Solution ob;
vector<int> v = {1,3,10,5}, v1 = {3,0,5,3};
print_vector(ob.killProcess(v, v1, 5));
}入力
{1,3,10,5},{3,0,5,3},5出力
[5, 10]
計算量について
まず、すべてのプロセスを走査して親子関係のマップを構築する処理にO(n)の時間がかかります。その後のBFSでは、各プロセスは最大1回ずつキューに追加・取り出しされるため、こちらもO(n)です。したがって、全体の時間計算量はO(n)、マップやキューの分だけ余分なメモリが必要なため、空間計算量もO(n)となります。
このように、BFSを活用すれば、プロセスツリー上で特定のプロセスとその子孫をすべて効率よく特定できます。実際のOSにおけるプロセス管理でも、同様の考え方が応用されています。
-
C++で解くリスのナッツ収集シミュレーション ― 最小移動距離を求めるアルゴリズム
問題概要 1本の木、1匹のリス、そして複数のナッツがフィールド上にあります。それぞれの位置は2次元グリッドのセルで表現されます。この問題の目的は、リスがすべてのナッツを集めて木の下に1個ずつ運ぶときの最小移動距離を求めることです。 リスの行動には次の制約があります。 一度に持てるナッツは最大1個 移動は上下左右の4方向で、隣接するセルへのみ可能 距離は移動回数(ステップ数)で表される たとえば、入力が「高さ: 5 / 幅: 7 / 木の位置: [2,2] / リスの位置: [4,4] / ナッツ: [[3,0], [2,5]]」の場合、出力は 12 となります。 解法のポイント まず、
-
C++で解く長方形エリアII ― 座標圧縮と走査線法による被覆面積の計算
問題概要 軸に平行な長方形のリストが与えられるものとします。各 rectangle[i] = {x1, y1, x2, y2} において、(x1, y1) は i 番目の長方形の左下隅の座標、(x2, y2) は右上隅の座標を表します。 求めたいのは、平面上でこれらすべての長方形が覆っている領域の合計面積です。答えは非常に大きな値になる可能性があるため、109 + 7 で割った余りを返すことになっています。 たとえば、入力が次のような場合を考えてみましょう。 このとき、出力は 6 となります。 解法の方針:座標圧縮+走査線(スイープライン) この問題は、座標圧縮(座標の離散化)と走査線法(