5.18 二轮 并查集

This commit is contained in:
zhangsan 2025-05-18 12:42:43 +08:00
parent 69f8aa3fd1
commit a900792e79
5 changed files with 246 additions and 0 deletions

View File

@ -0,0 +1,57 @@
package graph;
/**
* 题目 133. 克隆图 (cloneGraph)
* 描述给你无向 连通 图中一个节点的引用请你返回该图的 深拷贝克隆
*
* 图中的每个节点都包含它的值 valint 和其邻居的列表list[Node]
*
* class Node {
* public int val;
* public List<Node> neighbors;
* }
*
* 差值是一个正数其数值等于两值之差的绝对值
*
示例 1
输入adjList = [[2,4],[1,3],[2,4],[1,3]]
输出[[2,4],[1,3],[2,4],[1,3]]
解释
图中有 4 个节点
节点 1 的值是 1它有两个邻居节点 2 4
节点 2 的值是 2它有两个邻居节点 1 3
节点 3 的值是 3它有两个邻居节点 2 4
节点 4 的值是 4它有两个邻居节点 1 3
* 链接https://leetcode.cn/problems/clone-graph/
*/
//不会做
import graph.Node;
import java.util.ArrayList;
import java.util.HashMap;
public class CloneGraph {
private HashMap<Node, Node> visited = new HashMap <> ();
public Node cloneGraph(Node node) {
if (node == null) {
return node;
}
// 如果该节点已经被访问过了则直接从哈希表中取出对应的克隆节点返回
if (visited.containsKey(node)) {
return visited.get(node);
}
// 克隆节点注意到为了深拷贝我们不会克隆它的邻居的列表
Node cloneNode = new Node(node.val, new ArrayList());
// 哈希表存储
visited.put(node, cloneNode);
// 第一次 遇到某个邻居时递归会深入先把它自身拷贝出来并加入 visited再去拷贝它的邻居们
//如果再次遇到 同一个原节点例如有环或多条路径到同一节点就会走到第 2 步直接从 visited 里拿到已有的克隆节点避免重复和死循环
for (Node neighbor: node.neighbors) {
cloneNode.neighbors.add(cloneGraph(neighbor));
}
return cloneNode;
}
}

View File

@ -0,0 +1,21 @@
package graph;
import java.util.ArrayList;
import java.util.List;
class Node {
public int val;
public List<Node> neighbors;
public Node() {
val = 0;
neighbors = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
neighbors = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _neighbors) {
val = _val;
neighbors = _neighbors;
}
}

View File

@ -0,0 +1,32 @@
package union_find;
import java.util.List;
/**
* 题目 399. 除法求值 (calcEquation)
* 描述给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件其中 equations[i] = [Ai, Bi] values[i] 共同表示等式 Ai / Bi = values[i] 每个 Ai Bi 是一个表示单个变量的字符串
*
* 另有一些以数组 queries 表示的问题其中 queries[j] = [Cj, Dj] 表示第 j 个问题请你根据已知条件找出 Cj / Dj = ? 的结果作为答案
*
* 返回 所有问题的答案 如果存在某个无法确定的答案则用 -1.0 替代这个答案如果问题中出现了给定的已知条件中没有出现的字符串也需要用 -1.0 替代这个答案
*
* 注意输入总是有效的你可以假设除法运算中不会出现除数为 0 的情况且不存在任何矛盾的结果
*
* 注意未在等式列表中出现的变量是未定义的因此无法确定它们的答案
*
示例 1
输入equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]]
输出[6.00000,0.50000,-1.00000,1.00000,-1.00000]
解释
条件a / b = 2.0, b / c = 3.0
问题a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ?
结果[6.0, 0.5, -1.0, 1.0, -1.0 ]
注意x 是未定义的 => -1.0
* 链接https://leetcode.cn/problems/evaluate-division/
*/
public class CalcEquation {
public double[] calcEquation(List<List<String>> equations, double[] values, List<List<String>> queries) {
return new double[1];
}
}

View File

@ -0,0 +1,58 @@
package union_find;
/**
* 题目 990. 等式方程的可满足性 (equationsPossible)
* 描述给定一个由表示变量之间关系的字符串方程组成的数组每个字符串方程 equations[i] 的长度为 4并采用两种不同的形式之一"a==b" "a!=b"在这里a b 是小写字母不一定不同表示单字母变量名
* 只有当可以将整数分配给变量名以便满足所有给定的方程时才返回 true否则返回 false
*
示例 1
输入["a==b","b!=a"]
输出false
解释如果我们指定a = 1 b = 1那么可以满足第一个方程但无法满足第二个方程没有办法分配变量同时满足这两个方程
* 链接https://leetcode.cn/problems/satisfiability-of-equality-equations/
*/
//并查集第一次不会
public class EquationsPossible {
public boolean equationsPossible(String[] equations) {
int[] parent = new int[26];
for (int i = 0; i < 26; i++) {
parent[i] = i;
}
for (String str : equations) {
if (str.charAt(1) == '=') {
int index1 = str.charAt(0) - 'a';
int index2 = str.charAt(3) - 'a';
union(parent, index1, index2);
}
}
for (String str : equations) {
if (str.charAt(1) == '!') {
int index1 = str.charAt(0) - 'a';
int index2 = str.charAt(3) - 'a';
if (find(parent, index1) == find(parent, index2)) {
return false;
}
}
}
return true;
}
//合并x,y所属的集合这里没有做按秩合并默认合到index2所在集合
public void union(int[] parent, int index1, int index2) {
// 先分别找到 index1 index2 的根节点再把 root(index1) 的父指针指向 root(index2)
parent[find(parent, index1)] = find(parent, index2);
}
//查找 index 元素所在集合的根节点同时做路径压缩
public int find(int[] parent, int index) {
// parent[index] == index 说明已经是根节点
while (parent[index] != index) {
// 路径压缩将当前节点直接挂到它父节点的父节点上
// 这样可以让树变得更扁平后续查找更快
parent[index] = parent[parent[index]];
// 跳到上一级继续判断是否到根
index = parent[index];
}
// 循环结束时index 即为根节点下标
return index;
}
}

View File

@ -0,0 +1,78 @@
package union_find;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* 题目 547. 省份数量 (findCircleNum)
* 描述 n 个城市其中一些彼此相连另一些没有相连如果城市 a 与城市 b 直接相连且城市 b 与城市 c 直接相连那么城市 a 与城市 c 间接相连
* 省份 是一组直接或间接相连的城市组内不含其他没有相连的城市
* 给你一个 n x n 的矩阵 isConnected 其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连 isConnected[i][j] = 0 表示二者不直接相连
* 返回矩阵中 省份 的数量
*
示例 1
输入isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出2
* 链接https://leetcode.cn/problems/number-of-provinces/
*/
public class FindCircleNum {
//并查集
int find(int[] parent ,int index){
while (parent[index]!=index){
parent[index]=parent[parent[index]];
index=parent[index];
}
return index;
}
void union(int[] parent,int index1,int index2){
parent[find(parent,index1)]=find(parent,index2);
}
public int findCircleNum(int[][] isConnected) {
int res=0;
int[]parent=new int[200];
Set<Integer>set=new HashSet<>();
for (int i = 0; i < 200; i++) {
parent[i]=i;
}
int row=isConnected.length;
for (int i = 0; i < row; i++) {
for (int j = i+1; j < row; j++) {
if(isConnected[i][j]==1)
union(parent,i,j);
}
}
for (int i = 0; i < isConnected.length; i++) {
int father=find(parent,i);
if(!set.contains(father)){
res++;
set.add(father);
}
}
return res;
}
//深度优先搜索
public int findCircleNum2(int[][] isConnected) {
int cities = isConnected.length;
boolean[] visited = new boolean[cities];
int provinces = 0;
for (int i = 0; i < cities; i++) {
if (!visited[i]) {
dfs(isConnected, visited, cities, i);
provinces++;
}
}
return provinces;
}
public void dfs(int[][] isConnected, boolean[] visited, int cities, int i) {
for (int j = 0; j < cities; j++) {
if (isConnected[i][j] == 1 && !visited[j]) {
visited[j] = true;
dfs(isConnected, visited, cities, j);
}
}
}
}