并查集-尽量减少恶意软件的传播

54 阅读1分钟

924. 尽量减少恶意软件的传播

给出了一个由 n 个节点组成的网络,用 n × n 个邻接矩阵图 graph 表示。在节点网络中,当 graph[i][j] = 1 时,表示节点 i 能够直接连接到另一个节点 j。 

一些节点 initial 最初被恶意软件感染。只要两个节点直接连接,且其中至少一个节点受到恶意软件的感染,那么两个节点都将被恶意软件感染。这种恶意软件的传播将继续,直到没有更多的节点可以被这种方式感染。

假设 M(initial) 是在恶意软件停止传播之后,整个网络中感染恶意软件的最终节点数。

如果从 initial 中移除某一节点能够最小化 M(initial), 返回该节点。如果有多个节点满足条件,就返回索引最小的节点。

请注意,如果某个节点已从受感染节点的列表 initial 中删除,它以后仍有可能因恶意软件传播而受到感染。

 

示例 1:

输入: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
输出: 0

示例 2:

输入: graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2]
输出: 0

示例 3:

输入: graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1,2]
输出: 1

 

提示:

  • n == graph.length
  • n == graph[i].length
  • 2 <= n <= 300
  • graph[i][j] == 0 或 1.
  • graph[i][j] == graph[j][i]
  • graph[i][i] == 1
  • 1 <= initial.length <= n
  • 0 <= initial[i] <= n - 1
  • initial 中所有整数均不重复
/**
 * @param {number[][]} graph
 * @param {number[]} initial
 * @return {number}
 */
var minMalwareSpread = function(graph, initial) {
	const n = graph.length;
	const unionFind = new UnionFind(n);
	for (let i = 0; i < n; i++) {
		for (let j = i + 1; j < n; j++) {
			if (graph[i][j]) {
				unionFind.union(i, j);
			}
		}
	}

	const temp = {};

    let result = Number.MAX_VALUE;
	for (const i of initial) {
        result = Math.min(result, i);
		const rootNode = unionFind.find(i);
		if (temp[rootNode]) {
			temp[rootNode].num++;
		} else {
			temp[rootNode] = {
				num: 1
			}
		}
		if (temp[rootNode].num === 1) {
			temp[rootNode].nodeId = i;
		}
	}

    let maxSize = 0;
	for (let key in temp) {
		if (temp[key].num === 1) {
			const size = unionFind.getSize(key);
			if (size > maxSize) {
				maxSize = size;
				result = temp[key].nodeId;
			} else if (size === maxSize) {
				result = Math.min(result, temp[key].nodeId);
			}
		}
	}

	return result;

};

class UnionFind {
	constructor(count) {
		this.count = count;
		this.parents = [];
		this.sizes = new Array(count).fill(1);
        this.init();
	}

	init() {
		for (let i = 0; i < this.count; i++) {
			this.parents[i] = i;
		}
	}

	find(node) {
		let curNode = node;
		while (this.parents[curNode] !== curNode) {
			this.parents[curNode] = this.parents[this.parents[curNode]];
			curNode = this.parents[curNode];
		}

		return curNode;
	}

	union(left, right) {
		const leftRoot = this.find(left);
		const rightRoot = this.find(right);
		if (leftRoot !== rightRoot) {
			if (this.sizes[leftRoot] < this.sizes[rightRoot]) {
				this.parents[leftRoot] = rightRoot;
				this.sizes[rightRoot] += this.sizes[leftRoot];
			} else {
				this.parents[rightRoot] = leftRoot;
				this.sizes[leftRoot] += this.sizes[rightRoot];
			}
			this.count--;
			return true;
		}

		return false;
	}

	getSize(node) {
		return this.sizes[this.find(node)]
	}
}