代码随想录-035期-算法训练营【博客笔记汇总表】-****博客
●day 19 周日休息(4.21)
目录
图论
并查集理论基础
1971_寻找图中是否存在路径
0684_冗余连接
0685_冗余连接II
图论
并查集理论基础
并查集常用来解决连通性问题。
大白话就是当我们需要判断两个元素是否在同一个集合里的时候,我们就要想到用并查集。
并查集主要有三个功能。
- 寻找根节点,函数:find(int u),也就是判断这个节点的祖先节点是哪个;
- 将两个节点接入到同一个集合,函数:join(int u, int v),将两个节点连在同一个根节点上;
- 判断两个节点是否在同一个集合,函数:isSame(int u, int v),就是判断两个节点是不是同一个根节点。
int n = 1005; //n根据题目中节点数量而定,一般比节点数量大一点就好
vector<int> father = vector<int> (n, 0); //C++里的一种数组结构
vector<int> rank = vector<int> (n, 1); //初始每棵树的高度都为1
//并查集初始化
void init() {
for (int i = 0; i < n; ++i) {
father[i] = i;
rank[i] = 1; // 也可以不写
}
}
//并查集里寻根的过程
int find(int u) {
return u == father[u] ? u : find(father[u]);// 注意这里不做路径压缩
}
//判断 u 和 v是否找到同一个根
bool isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
//将v->u 这条边加入并查集
void join(int u, int v) {
u = find(u); //寻找u的根
v = find(v); //寻找v的根
if (rank[u] <= rank[v]) father[u] = v; //rank小的树合入到rank大的树
else father[v] = u;
//如果两棵树高度相同,则v的高度+1,因为上面 if (rank[u] <= rank[v]) father[u] = v; 注意是 <=
if (rank[u] == rank[v] && u != v) rank[v]++;
}
1971_寻找图中是否存在路径
package com.question.solve.leetcode.programmerCarl._12_graphTheory;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
public class _1971_寻找图中是否存在路径 {
}
class Solution1971 {
int[] father;
public boolean validPath(int n, int[][] edges, int source, int destination) {
father = new int[n];
init();
for (int i = 0; i < edges.length; i++) {
join(edges[i][0], edges[i][1]);
}
return isSame(source, destination);
}
//并查集初始化
public void init() {
for (int i = 0; i < father.length; i++) {
father[i] = i;
}
}
//并查集里寻根的过程
public int find(int u) {
if (u == father[u]) {
return u;
} else {
father[u] = find(father[u]);
return father[u];
}
}
//判断 u 和 v是否找到同一个根
public boolean isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
//将v->u 这条边加入并查集
public void join(int u, int v) {
u = find(u); //寻找u的根
v = find(v); //寻找v的根
//如果发现根相同,则说明在一个集合,不用两个节点相连直接返回
if (u == v) return;
father[v] = u;
}
}
class Solution1971_2 {
//方法一:广度优先搜索
public boolean validPath(int n, int[][] edges, int source, int destination) {
List<Integer>[] adj = new List[n];
for (int i = 0; i < n; i++) {
adj[i] = new ArrayList<Integer>();
}
for (int[] edge : edges) {
int x = edge[0], y = edge[1];
adj[x].add(y);
adj[y].add(x);
}
boolean[] visited = new boolean[n];
Queue<Integer> queue = new ArrayDeque<Integer>();
queue.offer(source);
visited[source] = true;
while (!queue.isEmpty()) {
int vertex = queue.poll();
if (vertex == destination) {
break;
}
for (int next : adj[vertex]) {
if (!visited[next]) {
queue.offer(next);
visited[next] = true;
}
}
}
return visited[destination];
}
}
class Solution1971_3 {
//方法二:深度优先搜索
public boolean validPath(int n, int[][] edges, int source, int destination) {
List<Integer>[] adj = new List[n];
for (int i = 0; i < n; i++) {
adj[i] = new ArrayList<Integer>();
}
for (int[] edge : edges) {
int x = edge[0], y = edge[1];
adj[x].add(y);
adj[y].add(x);
}
boolean[] visited = new boolean[n];
return dfs(source, destination, adj, visited);
}
public boolean dfs(int source, int destination, List<Integer>[] adj, boolean[] visited) {
if (source == destination) {
return true;
}
visited[source] = true;
for (int next : adj[source]) {
if (!visited[next] && dfs(next, destination, adj, visited)) {
return true;
}
}
return false;
}
}
0684_冗余连接
package com.question.solve.leetcode.programmerCarl._12_graphTheory;
public class _0684_冗余连接 {
}
class Solution0684 {
private int n; //节点数量:3 到 1000
private int[] father;
public Solution0684() {
n = 1005;
father = new int[n];
//并查集初始化
for (int i = 0; i < n; ++i) {
father[i] = i;
}
}
//并查集里寻根的过程
private int find(int u) {
if (u == father[u]) {
return u;
}
father[u] = find(father[u]);
return father[u];
}
//将 v->u 这条边加入并查集
private void join(int u, int v) {
u = find(u);
v = find(v);
if (u == v) return;
father[v] = u;
}
//判断 u 和 v 是否找到同一个根,本题用不上
private Boolean same(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
public int[] findRedundantConnection(int[][] edges) {
for (int i = 0; i < edges.length; i++) {
if (same(edges[i][0], edges[i][1])) {
return edges[i];
} else {
join(edges[i][0], edges[i][1]);
}
}
return null;
}
}
class Solution0684_2 {
public int[] findRedundantConnection(int[][] edges) {
int n = edges.length;
int[] parent = new int[n + 1];
for (int i = 1; i <= n; i++) {
parent[i] = i;
}
for (int i = 0; i < n; i++) {
int[] edge = edges[i];
int node1 = edge[0], node2 = edge[1];
if (find(parent, node1) != find(parent, node2)) {
union(parent, node1, node2);
} else {
return edge;
}
}
return new int[0];
}
public void union(int[] parent, int index1, int index2) {
parent[find(parent, index1)] = find(parent, index2);
}
public int find(int[] parent, int index) {
if (parent[index] != index) {
parent[index] = find(parent, parent[index]);
}
return parent[index];
}
}
0685_冗余连接II
package com.question.solve.leetcode.programmerCarl._12_graphTheory;
import java.util.ArrayList;
public class _0685_冗余连接II {
}
class Solution0685 {
private static final int N = 1010; //如题:二维数组大小的在3到1000范围内
private int[] father;
public Solution0685() {
father = new int[N];
//并查集初始化
for (int i = 0; i < N; ++i) {
father[i] = i;
}
}
//并查集里寻根的过程
private int find(int u) {
if (u == father[u]) {
return u;
}
father[u] = find(father[u]);
return father[u];
}
//将v->u 这条边加入并查集
private void join(int u, int v) {
u = find(u);
v = find(v);
if (u == v) return;
father[v] = u;
}
//判断 u 和 v是否找到同一个根,本题用不上
private Boolean same(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
/**
* 初始化并查集
*/
private void initFather() {
//并查集初始化
for (int i = 0; i < N; ++i) {
father[i] = i;
}
}
/**
* 在有向图里找到删除的那条边,使其变成树
*
* @param edges
* @return 要删除的边
*/
private int[] getRemoveEdge(int[][] edges) {
initFather();
for (int i = 0; i < edges.length; i++) {
if (same(edges[i][0], edges[i][1])) { // 构成有向环了,就是要删除的边
return edges[i];
}
join(edges[i][0], edges[i][1]);
}
return null;
}
/**
* 删一条边之后判断是不是树
*
* @param edges
* @param deleteEdge 要删除的边
* @return true:是树,false:不是树
*/
private Boolean isTreeAfterRemoveEdge(int[][] edges, int deleteEdge) {
initFather();
for (int i = 0; i < edges.length; i++) {
if (i == deleteEdge) continue;
if (same(edges[i][0], edges[i][1])) {//构成有向环了,一定不是树
return false;
}
join(edges[i][0], edges[i][1]);
}
return true;
}
public int[] findRedundantDirectedConnection(int[][] edges) {
int[] inDegree = new int[N];
for (int i = 0; i < edges.length; i++) {
//入度
inDegree[edges[i][1]] += 1;
}
//找入度为2的节点所对应的边,注意要倒序,因为优先返回最后出现在二维数组中的答案
ArrayList<Integer> twoDegree = new ArrayList<Integer>();
for (int i = edges.length - 1; i >= 0; i--) {
if (inDegree[edges[i][1]] == 2) {
twoDegree.add(i);
}
}
//处理图中 情况1 和 情况2
//如果有入度为2的节点,那么一定是两条边里删一个,看删哪个可以构成树
if (!twoDegree.isEmpty()) {
if (isTreeAfterRemoveEdge(edges, twoDegree.get(0))) {
return edges[twoDegree.get(0)];
}
return edges[twoDegree.get(1)];
}
//明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
return getRemoveEdge(edges);
}
}