leetcode 684.冗余连接(C/C++/java/python)

PS:算法并非原创,仅作个人学习记录使用,侵删。

题目描述
在这里插入图片描述
算法分析
这道题很明显和图论相关,并且涉及到顶点之间的关联关系,所以首先想到的思路还是并查集、深度优先遍历、广度优先遍历三种常见的解题方式。根据已知的数组条件构造出图之后,存在一个冗余的边界条件,可以从后往前进行排查。
不过,这里让我不由想起了最小生成树的相关算法,也可在产生最小生成树的时候,有意识地按照数组排列顺序的优先级进行判断。这就和并查集的思想相差无几了。
那就每次选择排列靠前的边,来连接两个不同的连通分量,相同的连通分量内部的顶点不再进行连接,将整个图形联通为整个连通分量,势必会剩下一条没有用的边,这条边就是输出结果。
博客大佬们的算法更加精简,最常见的就是并查集思想,当出现环的时候,直接返回结果(由题目的条件决定)。
此外,成环的判断还可以使用DFS、BFS思想。
还有利用拓扑关系的解法,利用顶点的度数和环的关系,判断成环边的存在。

代码实现
【C】

/**
 * 算法思想: 并查集
 */

int find(int *arr , int i){//查找老大
    return i==arr[i] ? i : (arr[i] = find(arr, arr[i]));
}

void un(int *arr, int i, int j){//联系两个并查集
    int x = find(arr, i);
    int y = find(arr, j);
    
    arr[y] = x;
}

#define LEN 0xffff

int* findRedundantConnection(int** eds, int len, 
                             int* edgesColSize, int* returnSize){
    int i, j, arr[LEN], *ret=(int*)malloc(sizeof(int) * 2), max=0;
    
    for(i=1; i<LEN; i++){//单独的连通分量
        arr[i] = i;
    }
    
    for(i=0; i<len; i++){//遍历边数组
        if(find(arr, eds[i][0]) == find(arr, eds[i][1])) {//如果查找到形成环的边,那么不用后续遍历,这就是我们要找的元素,
            ret[0] = eds[i][0];
            ret[1] = eds[i][1];
            break;
        }
        un(arr, eds[i][0], eds[i][1]);//如果尚未形成环,那么合并两个并查集
    }

    *returnSize = 2;
    return ret;
}

C语言参考网址
【C++】

/*
算法思想:并查集
*/
class Solution {
public:
    int per[10005];
    
    void init(){//初始化图中元素。连通分量都是自己
        for(int i=0;i<=10000;i++) per[i]=i;
    }
    
    int find(int x){//迭代查找这组连通分量的组长
        if(x==per[x]) return x;
        return per[x]=find(per[x]);
    }
    
    bool C(int x,int y){//判断x和y是不是属于一个连通分量,并且合并不同的连通分量。
        int xx=find(x);
        int yy=find(y);
        if(xx!=yy){
            per[xx]=yy;
            return true;
        }
        return false;
    }
    
    vector<int> findRedundantConnection(vector<vector<int>>& a) {
        int len=a.size();
        init();//初始化图的连通分量
        vector<int>v;
        for(int i=0;i<len;i++){//遍历边集合
            if(!C(a[i][0],a[i][1])){//如果一条边没有形成环,那么合并两个连通分量即可
            	//如果一条边形成了环,那么构造结果,不再遍历
                v.push_back(min(a[i][0],a[i][1]));//为了满足题目要求
                v.push_back(max(a[i][0],a[i][1]));
                break;
            }
        }
        return v;//最终结果
    }
};

C++参考网址

【java】

/*
算法思想:并查集
*/
class Solution {
    public int[] findRedundantConnection(int[][] edges) {
        int[] parent = new int[edges.length + 1];//组长数组
        for (int i = 0; i < parent.length; i++) {//初始化组长数组,起始时每个顶点连通分量组长是它自己。
            parent[i] = i;
        }
        int[] res = null;//结果数组
        for(int[] edge : edges){//遍历边集合
            int x = edge[0];//左侧顶点编号
            int y = edge[1];//右侧顶点编号
            while(x != parent[x]){
                x = parent[x];
            }//得到左侧顶点所在的连通分量的组长顶点编号
            while(y != parent[y]){
                y = parent[y];
            }//得到右侧顶点所在的连通分量的组长顶点编号
            if(x == y){// 若两个组长是相同,说明是同一个连通分量
                res = edge;//这个形成环的边就是结果
            }
            else{//两者的组长不相同,说明是两个不同的连通分量,进行合并
                parent[x] = y;
            }
        }//遍历所有边的集合,不断更新成环边(最后结果)
        return res;// 返回最后一个导致有环的边
        /*
        之所以C语言解法和C++解法不需要遍历全部的集合,
        而是只需要在找到一个成环边就跳出循环,并且也可得
        到正确答案,其原因在于:连通分量的合并是按照边排
        列顺序依次进行的,并且只存在一个成环边,所以找到
        结果之后中途跳出就可以满足题目要求。
        */
    }
}

java参考博客

【python】

#算法思想:拓扑关系
#先把度为 1 的节点找出,然后,不断的删除度为1节点的连接边,在最终节点中,如果边的两个节点度都大于1,则该边为冗余边。
from collections import deque
class Solution:
    def findRedundantConnection(self, edges):
        N = len(edges)
        graph = {}#存储顶点和邻接顶点
        #1. 存储图
        for i in range(N):
            if edges[i][0] not in graph:
                graph[edges[i][0]] = []
            graph[edges[i][0]].append(edges[i][1])
            if edges[i][1] not in graph:
                graph[edges[i][1]] = []
            graph[edges[i][1]].append(edges[i][0])#建立图
            
        # 2. 统计度 == 1
        indegree = {}
        queue = deque()#存储度为1的顶点
        for node in graph.keys():#遍历图中的顶点
            indegree[node] = len(graph[node])#某个点的邻接顶点个数
            if len(graph[node]) == 1:#如果某点只和一个顶点连接,
                queue.append(node)
        while len(queue) > 0:#当队列不为空时
            node = queue.popleft()
            for other_node in graph[node]:
                indegree[other_node] -= 1
                if indegree[other_node] == 1:
                    queue.append(other_node)
                    
        # 3. 度大于1的节点的任意组合都是可以删除的边
        for i in range(N-1, -1, -1):
            start, end = edges[i][0], edges[i][1]
            if indegree[start] > 1 and indegree[end] > 1:#当边的两侧顶点的度都是大于1的时候
                return edges[i]#只有一个特殊边,所有不用更新结果,直接返回
        return None

python参考网址——强推,DFS解法+拓扑解法+并查集解法的python实现

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值