最小生成树
【题目】给定一个图的点集,边集和权重,返回构建最小生成树的代价。
输入:N = 2, conn = [[1, 2, 37], [2, 1, 17], [1, 2, 68]]
输出:17
class Solution {
private long cost = 0;
// 这里直接申请了足够多的内存
private int[] F = null;
// 并查集初始化
// 注意点的编号是从1~n
private void Init(int n) {
F = new int[n+1];
for (int i = 0; i <= n; i++) {
F[i] = i;
}
cost = 0;
}
private int Find(int x) {
if (x == F[x]) {
return x;
}
F[x] = Find(F[x]);
return F[x];
}
// 在合并的时候,需要加上代价
private void Union(int x, int y, int pay) {
if (Find(x) != Find(y)) cost += pay;
F[Find(x)] = Find(y);
}
// 一共有n个点,编号从1~n
// conn表示输入的边的集合
// 每一项是一个三元组[点a, 点b, 需要费用c]
public long Kruskal(int n, int m, int[][] conn) {
Init(n);
// 边集的排序
Arrays.sort(conn, 0, m, new Comparator<int[]>() {
public int compare(int[] a, int[] b) {
return a[2] - b[2];
}
});
// 顺次将边集添加到集合中
for (int i = 0; i < m; i++) {
Union(conn[i][0], conn[i][1], conn[i][2]);
}
return cost;
}
}
例 2:帮派的数目
【题目】江湖上有 N 个人,编号从 [1 ~ N],现在只能告诉你,其中两人是一个帮派的,请你输出帮派的数目。
输入:N = 4, [[1, 2], [2,3]]
输出:2
解释:一共有 4 个人,[1,2, 3] 成为一个帮派,[4] 独自成为一个帮派,那么一共有 2 个帮派。
int count = 0;
int[] F = null;
void Init(int n) {
F = new int[n + 1];
for (int i = 0; i <= n; i++) {
F[i] = i;
}
count = n;
}
int Find(int x) {
if (x == F[x]) {
return x;
}
F[x] = Find(F[x]);
return F[x];
}
void Union(int x, int y) {
if (Find(x) != Find(y))
count--;
F[Find(x)] = Find(y);
}
int findGangNumber(int n, int[][] conn) {
Init(n);
int m = conn.length;
for (int i = 0; i < m; i++) {
Union(conn[i][0], conn[i][1]);
}
// 帮派里面帮主的个数
return count;
}
延伸:如果将这里的每个点都当成一个“图”结构中的一个点,将两两成对的输入当成“图”结构中的边。那么问题就变成了求解图的连通域个数。
200. 岛屿数量
class Solution {
private int[][] directions = new int[][]{
{0, 1}, {1, 0}
};
private int[] F = null;
private int count = 0;
private void Init(int n) {
F = new int[n];
for (int i = 0; i < n; i ++) {
F[i] = i;
}
count = n;
}
private int Find(int x) {
if (x == F[x]) {
return x;
}
F[x] = Find(F[x]);
return F[x];
}
private void Union(int x, int y) {
if (Find(x) != Find(y)) {
count --;
}
F[Find(x)] = Find(y);
}
public int numIslands(char[][] grid) {
final int m = grid.length;
final int n = grid[0].length;
Init(m * n);
int blackNumber = 0;
for (int i = 0; i < m; i ++) {
for (int j = 0; j < n; j ++) {
if ('1' == grid[i][j]) {
for (int d = 0; d < directions.length; d ++) {
int x = i + directions[d][0];
int y = j + directions[d][1];
if (x < 0 || x >= m || y < 0 || y >= n || '0' == grid[x][y]) {
continue;
}
Union(i * n + j, x * n + y);
}
} else {
blackNumber ++;
}
}
}
return count - blackNumber;
}
}
547. 省份数量
class Solution {
private int[] F = null;
private int count = 0;
private void Init(int n) {
F = new int[n];
for (int i = 0; i < n; i ++) {
F[i] = i;
}
count = n;
}
private int Find(int x) {
if (x == F[x]) {
return x;
}
F[x] = Find(F[x]);
return F[x];
}
private void Union(int x, int y) {
if (Find(x) != Find(y)) {
count --;
}
F[Find(x)] = Find(y);
}
public int findCircleNum(int[][] isConnected) {
final int n = isConnected.length;
Init(n);
for (int i = 0; i < n; i ++) {
for (int j = i + 1; j < n; j ++) {
if (1 == isConnected[i][j]) {
Union(i, j);
}
}
}
return count;
}
}
839. 相似字符串组
class Solution {
int count = 0;
int[] F = null;
private void Init(int n) {
F = new int[n];
for (int i = 0; i < n; i ++) {
F[i] = i;
}
count = n;
}
private int Find(int x) {
if (x == F[x]) {
return x;
}
F[x] = Find(F[x]);
return F[x];
}
private void Union(int x, int y) {
if (Find(x) != Find(y)) {
count --;
}
F[Find(x)] = Find(y);
}
private boolean check(String a, String b) {
final int n = a.length();
int[] aArr = new int[256];
int[] bArr = new int[256];
for (int i = 0; i < n; i ++) {
aArr[a.charAt(i)] ++;
bArr[b.charAt(i)] ++;
}
for (int i = 0; i < n; i ++) {
if (aArr[i] != bArr[i]) {
return false;
}
}
int number = 0;
for (int i = 0; i < n; i ++) {
if (a.charAt(i) != b.charAt(i)) {
number ++;
}
if (number > 2) {
return false;
}
}
return true;
}
public int numSimilarGroups(String[] strs) {
final int n = strs.length;
Init(n);
for (int i = 0; i < n; i ++) {
for (int j = i + 1; j < n; j ++) {
if (check(strs[i], strs[j])) {
Union(i, j);
}
}
}
return count;
}
}
虚拟点与虚拟边
上网的最小费用
【题目】园区里面有很多大楼,编号从 1~N。第 i 大楼可以自己花钱买路由器上网,费用为 cost[i-1],也可以从别的大楼拉一根网线来上网,比如大楼 a 和大楼 b 之间拉网线的费用为 c,表示为一条边 [a, b, c]。输入为每个大楼自己买路由器和拉网线的费用,请问,让所有大楼都能够上网的最小费用是多少?上网具有联通性,只要与能够上网的大楼连通,即可上网。
输入:cost = [1, 2, 3], edges = [[1,2,100], [2,3,3]]
输出:6
题目参考:https://kaiwu.lagou.com/course/courseInfo.htm?courseId=685#/detail/pc?id=6696
class Solution {
private int[] F = null;
private int totalCost = 0;
// 注意,编号是从1 ~ n
private void Init(int n) {
F = new int[n + 1];
for (int i = 0; i <= n; i++) {
F[i] = i;
}
totalCost = 0;
}
private int Find(int x) {
if (x == F[x]) {
return x;
}
F[x] = Find(F[x]);
return F[x];
}
private void Union(int x, int y, int pay) {
if (Find(x) != Find(y)) {
totalCost += pay;
}
F[Find(x)] = Find(y);
}
// N 表示结点数目
// cost[i-1]表示结点i自己买路由器的代价
// es[x] = [a, b, c]表示大楼a,b之间拉网线的费用
// 输出所有大楼通网的最小费用
public int minCostToSupplyWater(int N, int[] cost, int[][] es) {
// 初始化并查集
Init(N);
// 每个结点都要自己买路由器,那么我们可以认为这样
// 0号楼已经有网络了,可以用0费用上网
// i号楼与0号楼拉网线,需要的费用是cost[i-1]
// 那么这里就多了N条边
int[][] conn = new int[es.length + N][3];
for (int i = 0; i < es.length; i++) {
conn[i][0] = es[i][0];
conn[i][1] = es[i][1];
conn[i][2] = es[i][2];
}
int to = es.length;
for (int i = 1; i <= N; i++) {
conn[to][0] = 0;
conn[to][1] = i;
conn[to][2] = cost[i - 1];
to++;
}
// 接下来采用Krukal最小生成树算法
Arrays.sort(conn, new Comparator<int[]>() {
public int compare(int[] a, int[] b) {
return a[2] - b[2];
}
});
for (int i = 0; i < conn.length; i++) {
Union(conn[i][0], conn[i][1], conn[i][2]);
}
return totalCost;
}
}
476

被折叠的 条评论
为什么被折叠?



