目录
Prim
prim和disjkra类似的思想!简单
推荐这个版本:优先队列的JAva版本力扣
class Solution {
public int minCostConnectPoints(int[][] points) {
// 转化成无向图邻接表的形式
List<int[]>[] graph = buildGraph(points);
// 执行 Prim 算法
Prim prim = new Prim(graph);
return prim.minWeightSum;
}
// 转化成无向图邻接表的形式
public List<int[]>[] buildGraph(int[][] points){
// 图中共有 n 个节点
int n = points.length;
List<int[]>[] graph = new List[n];
for(int i = 0; i < n; i++){
graph[i] = new ArrayList<>();
}
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
if(i == j){
continue;
}
int x1 = points[i][0], y1 = points[i][1];
int x2 = points[j][0], y2 = points[j][1];
int weight = Math.abs(x1 - x2) + Math.abs(y1 - y2);
// 用 points 中的索引表示坐标点
// 无向图其实就是双向图
// 一条边表示为 int[]{from, to, weight}
graph[i].add(new int[]{i, j, weight});
graph[j].add(new int[]{j, i, weight});
}
}
return graph;
}
class Prim{
// 核心数据结构,存储横切边的优先级队列
// 按照边的权重从小到大排序
PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> (a[2] - b[2]));
// 类似 visited 数组的作用,记录哪些节点已经成为最小生成树的一部分
boolean[] inMST;
// 记录最小生成树的权重和
int minWeightSum = 0;
// graph 是用邻接表表示的一幅图,
// graph[s] 记录节点 s 所有相邻的边,
// 三元组 int[]{from, to, weight} 表示一条边
List<int[]>[] graph;
public Prim(List<int[]>[] graph){
this.graph = graph;
inMST = new boolean[graph.length];
// 随便从一个点开始切分都可以,从节点 0 开始
cut(0);
inMST[0] = true;
// 不断进行切分,向最小生成树中添加边
while(!pq.isEmpty()){
int[] edge = pq.poll();
int to = edge[1];
int weight = edge[2];
if(inMST[to]){
// 节点 to 已经在最小生成树中,跳过
// 否则这条边会产生环
continue;
}
// 将边 edge 加入最小生成树
// 节点 to 加入后,进行新一轮切分,会产生更多横切边
cut(to);
inMST[to] = true;
minWeightSum += weight;
}
}
// 将 s 的横切边加入优先队列
public void cut(int v){
List<int[]> edges = graph[v];
// 遍历 s 的邻边
for(int[] edge : edges){
if(inMST[edge[1]]){
// 相邻接点 to 已经在最小生成树中,跳过
// 否则这条边会产生环
continue;
}
// 加入横切边队列
pq.add(edge);
}
}
}
}
作者:雷莫
链接:https://leetcode.cn/problems/min-cost-to-connect-all-points/solutions/2145799/1584-lian-jie-suo-you-dian-de-zui-xiao-f-i12e/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
未优化的C++版本
void Prim( Graph G)
{
int lowcost[MAXSIZEF];
int closest[MAXSIZE];
int min;
int sum=0;
n = G.vernum;
for(int i=2;i<=n;i++)
{
lowcost[i]=G.edge[1][i];
closest[i] =1;
}
lowcost[1] = INF;
for(int i=2;i<=n;i++)
{//循环n-1次
min = INF;
for(int j=2;j<=n;j++)
{
if(lowcost[j]<min)
{
k=j;
min = lowcost[j]
}
}//找到最小lowcost
cout<<closet[k]<<k;
sum+=min;
LOWCOST[k] =INF;
for(int j =2;j<=n;j++)
{
if(G.[k][j]<lowcost[j]&&lowcost[j]<INF)
{
lowcost[j] = G.edge[k][j];
closest[j] = k;
}
}
}
}
java代码 prim-
class Solution {
public int minCostConnectPoints(int[][] points) {
int N = points.length;
int[][] newPoints = new int[N][N];
int res = 0;
//建立邻接矩阵
for (int i = 0; i < N; i++) {
for (int j = i+1; j < N; j++) {
newPoints[i][j] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
newPoints[j][i] = Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]);
}
}
//lowcast存放与连接图中顶点的最小值
int lowcast[] = new int[N];
List<Integer> count = new ArrayList<>();
count.add(0);
lowcast[0] = -1;
for (int i = 1; i < N; i++) {
lowcast[i] = newPoints[i][0];
}
while (count.size() < N) {
//选取lowcast数组中与已在图中的顶点最小距离的点
int minDistance = Integer.MAX_VALUE;
int flag = 0;
for (int i = 0; i < N; i++) {
if (lowcast[i] > 0 && lowcast[i] < minDistance) {
flag = i;
minDistance = lowcast[i];
}
}
//将选取的点加入图中
count.add(flag);
//计入最短距离
res += minDistance;
//更新lowcast
for (int j = 0; j < N; j++) {
if (j == flag) {
lowcast[j] = -1;
} else {
if (lowcast[j] != -1)
lowcast[j] = Math.min(lowcast[j], newPoints[j][flag]);
}
}
}
return res;
}
}
Kruskal
之前那个prim是不断把目前有最短的那个点加入到集合
这个Kruska,直接把所有边按权重降序排列,依次
直接把最短边加进来,途中判断是不是连通就行
推荐版本2:自己做的力扣
class Solution {
class Edge{
int from;
int to;
int weight;
public Edge(int from,int to,int weight)
{
this.from=from;
this.to=to;
this.weight=weight;
}
}
public int minCostConnectPoints(int[][] points) {
List<Edge> graph = new ArrayList<>();
int n = points.length;
for(int i=0;i<n;i++)
{
int xi = points[i][0];
int yi = points[i][1];
for(int j=i+1;j<n;j++)
{
int xj =points[j][0];
int yj = points[j][1];
int dis = Math.abs(xi-xj) + Math.abs(yi-yj);
Edge edge1 = new Edge(i,j,dis);
Edge edge2 = new Edge(j,i,dis);
graph.add(edge1);
graph.add(edge2);
}
}
Collections.sort(graph, (a,b)->a.weight-b.weight);
UF uf = new UF(n);
int minWeight= 0;
for(Edge edge:graph){
if(uf.isConnected(edge.from,edge.to)){
continue;
}
uf.union(edge.from,edge.to);
minWeight += edge.weight;
}
return minWeight;
}
class UF{
int[] parent;
int count;
public UF(int count)
{
this.parent = new int[count];
for(int i=0;i<count;i++){
parent[i]=i;
}
this.count=count;
}
public void union(int x,int y){
int rootX = findRoot(x);
int rootY = findRoot(y);
if(rootX==rootY){
return;
}
parent[rootX]=rootY;
count--;
}
public int findRoot(int x)
{
if(parent[x]!=x)
{
parent[x] = findRoot(parent[x]);
}
return parent[x];
}
public boolean isConnected(int x, int y)
{
int rootX = findRoot(x);
int rootY = findRoot(y);
if(rootX==rootY)
return true;
return false;
}
public int getCount()
{
return this.count;
}
}
}
推荐版本:别人写的力扣
class Solution {
public int minCostConnectPoints(int[][] points) {
int n = points.length;
// 生成所有边及权重
ArrayList<int[]> edges = new ArrayList<>();
for(int i = 0; i < n; i++){
// ⭐注意j从i+1开始,不要重头开始,避免重复
for(int j = i + 1; j < n; j++){
int x1 = points[i][0], y1 = points[i][1];
int x2 = points[j][0], y2 = points[j][1];
int dis = Math.abs(x1 - x2) + Math.abs(y1 - y2);
// 用坐标点在 points 中的索引表示坐标点
edges.add(new int[]{i, j, dis});
}
}
// 将边按照权重从小到大排序
Collections.sort(edges, (a, b) -> (a[2] - b[2]));
// 执行 Kruskal 算法
UF uf = new UF(n);
int minWeight = 0;
for(int[] edge : edges){
int i = edge[0];
int j = edge[1];
// 若这条边会产生环,则不能加入 mst
if(uf.isConnected(i, j)){
continue;
}
// 若这条边不会产生环,则属于最小生成树
uf.union(i, j);
minWeight += edge[2];
}
return minWeight;
}
class UF{
private int count;
private int[] parent;
public UF(int n){
parent = new int[n];
count = 0;
for(int i = 0; i < n; i++){
parent[i] = i;
}
}
public void union(int p, int q){
int rootP = findRoot(p);
int rootQ = findRoot(q);
parent[rootP] = rootQ;
// 两个连通分量合并成一个连通分量,count数量减一
count--;
}
public boolean isConnected(int p, int q){
int rootP = findRoot(p);
int rootQ = findRoot(q);
return rootP == rootQ;
}
public int findRoot(int x){
if(parent[x] != x){
parent[x] = findRoot(parent[x]);
}
return parent[x];
}
public int getCount(){
return count;
}
}
}
作者:雷莫
链接:https://leetcode.cn/problems/min-cost-to-connect-all-points/solutions/2145799/1584-lian-jie-suo-you-dian-de-zui-xiao-f-i12e/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。