Connecting Graph
给一个图中的n
个节点, 记为 1
到 n
. 在开始的时候图中没有边。
你需要完成下面两个方法:
connect(a, b)
, 添加连接节点a
,b
的边.query(a, b)
, 检验两个节点是否联通
思路:Union Find模板要会背诵;
public class ConnectingGraph {
private class UnionFind {
int[] father;
int count;
public UnionFind(int n) {
father = new int[n+1];
count = n;
for(int i = 0; i <= n; i++) {
father[i] = i;
}
}
public int find(int x) {
int j = x;
while(father[j] != j) {
j = father[j];
}
// path compression;
while(x != j) {
int fx = father[x];
father[x] = j;
x = fx;
}
return j;
}
public void union(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if(root_a != root_b) {
father[root_a] = root_b;
this.count--;
}
}
public int getCount() {
return this.count;
}
}
UnionFind uf;
public ConnectingGraph(int n) {
uf = new UnionFind(n);
}
public void connect(int a, int b) {
uf.union(a,b);
}
public boolean query(int a, int b) {
return uf.find(a) == uf.find(b);
}
}
Connecting Graph II
给一个图中的 n
个节点, 记为 1
到 n
.在开始的时候图中没有边.
你需要完成下面两个方法:
connect(a, b)
, 添加一条连接节点 a, b的边query(a)
, 返回图中含a
的联通区域内节点个数
Example
例1:
输入:
ConnectingGraph2(5)
query(1)
connect(1, 2)
query(1)
connect(2, 4)
query(1)
connect(1, 4)
query(1)
输出:
[1,2,3,3]
思路:size的信息记录在老大哥那里,只需加一个size array就行,每次union的时候b的size += a.size;
public class ConnectingGraph2 {
private class UnionFind {
private int[] father;
private int[] size;
private int count;
public UnionFind(int n) {
this.father = new int[n+1];
this.size = new int[n+1];
for(int i = 0; i <= n; i++) {
father[i] = i;
size[i] = 1;
}
this.count = n;
}
public int find(int j) {
int x = j;
while(father[j] != j) {
j = father[j];
}
// path compression;
while(x != j) {
int fx = father[x];
father[x] = j;
x = fx;
}
return j;
}
public void union(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if(root_a != root_b) {
father[root_a] = root_b;
size[root_b] += size[root_a];
}
}
public int getSize(int a) {
int root_a = find(a);
return size[root_a];
}
}
UnionFind uf;
public ConnectingGraph2(int n) {
uf = new UnionFind(n);
}
public void connect(int a, int b) {
uf.union(a, b);
}
public int query(int a) {
return uf.getSize(a);
}
}
Connecting Graph III
给一个图中的 n
个节点, 记为 1
到 n
. 在开始的时候图中没有边.
你需要完成下面两个方法:
connect(a, b)
, 添加一条连接节点 a, b的边query()
, 返回图中联通区域个数
Example
例1:
输入:
ConnectingGraph3(5)
query()
connect(1, 2)
query()
connect(2, 4)
query()
connect(1, 4)
query()
输出:[5,4,3,3]
思路:还是UnionFind的模板,注意count每次union的时候减减,就可以了。
public class ConnectingGraph3 {
private class UnionFind {
private int[] father;
private int count;
public UnionFind(int n) {
this.father = new int[n+1];
for(int i = 0; i <= n; i++) {
father[i] = i;
}
this.count = n;
}
public int find(int x) {
int j = x;
while(father[j] != j) {
j = father[j];
}
// path compression;
while(x != j) {
int fx = father[x];
father[x] = j;
x = fx;
}
return j;
}
public void union(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if(root_a != root_b) {
father[root_a] = root_b;
count--;
}
}
public int getCount() {
return this.count;
}
}
private UnionFind uf;
public ConnectingGraph3(int n) {
uf = new UnionFind(n);
}
public void connect(int a, int b) {
uf.union(a, b);
}
public int query() {
return uf.getCount();
}
}
Number of Islands II
给定 n, m, 分别代表一个二维矩阵的行数和列数, 并给定一个大小为 k 的二元数组A. 初始二维矩阵全0. 二元数组A内的k个元素代表k次操作, 设第i个元素为 (A[i].x, A[i].y)
, 表示把二维矩阵中下标为A[i].x行A[i].y列的元素由海洋变为岛屿. 问在每次操作之后, 二维矩阵中岛屿的数量. 你需要返回一个大小为k的数组.
Example
样例 1:
输入: n = 4, m = 5, A = [[1,1],[0,1],[3,3],[3,4]]
输出: [1,1,2,2]
解释:
0. 00000
00000
00000
00000
1. 00000
01000
00000
00000
2. 01000
01000
00000
00000
3. 01000
01000
00000
00010
4. 01000
01000
00000
00011
样例 2:
输入: n = 3, m = 3, A = [[0,0],[0,1],[2,2],[2,1]]
输出: [1,1,2,2]
Notice
设定0表示海洋, 1代表岛屿, 并且上下左右相邻的1为同一个岛屿.
思路:这题就是加入一个点,然后跟他的四个连边进行union,如果四个边有1,那么进行union,result加入union之后的count;
这个题是二维变成一维的union Find。题目出的还是不错的,不亏是google的题目,很有筛选性;
/**
* Definition for a point.
* class Point {
* int x;
* int y;
* Point() { x = 0; y = 0; }
* Point(int a, int b) { x = a; y = b; }
* }
*/
public class Solution {
private class UnionFind {
private int[] father;
private int count;
public UnionFind(int n) {
father = new int[n+1];
for(int i = 0; i <= n; i++) {
father[i] = i;
}
count = n;
}
public int find(int x) {
int j = x;
while(father[j] != j) {
j = father[j];
}
// path compression;
while(x != j) {
int fx = father[x];
father[x] = j;
x = fx;
}
return j;
}
public void union(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if(root_a != root_b) {
father[root_a] = root_b;
count--;
}
}
public void setCount(int count) {
this.count = count;
}
public int getCount() {
return this.count;
}
}
public List<Integer> numIslands2(int n, int m, Point[] operators) {
List<Integer> result = new ArrayList<Integer>();
if(n <= 0 || m <= 0 || operators == null || operators.length == 0) {
return result;
}
// matrix记录状态;
int[][] matrix = new int[n][m];
//这里巧妙的将二维矩阵的union,转换为一维的union;
UnionFind uf = new UnionFind(n*m);
int[] dx = {0,0,1,-1};
int[] dy = {1,-1,0,0};
int count = 0;
for(Point point: operators) {
int x = point.x;
int y = point.y;
if(matrix[x][y] == 1) {
result.add(count);
continue;
}
matrix[x][y] = 1;
count++;
// 先设置好初始值count,假设没有连边;
uf.setCount(count);
for(int k = 0; k < 4; k++){
int nx = x + dx[k];
int ny = y + dy[k];
//如果四个边上的点也是1,那么就union;
if(0 <= nx && nx < n && 0 <= ny && ny < m && matrix[nx][ny] == 1){
uf.union(m*x + y , m*nx + ny);
}
}
// union完之后,count取出来,方便下一次计算,同时加入result;
count = uf.getCount();
result.add(count);
}
return result;
}
}
Minimum Spanning Tree
给出一些Connections,即Connections类,找到一些能够将所有城市都连接起来并且花费最小的边。
如果说可以将所有城市都连接起来,则返回这个连接方法;不然的话返回一个空列表。
Example
样例 1:
输入:
["Acity","Bcity",1]
["Acity","Ccity",2]
["Bcity","Ccity",3]
输出:
["Acity","Bcity",1]
["Acity","Ccity",2]
样例 2:
输入:
["Acity","Bcity",2]
["Bcity","Dcity",5]
["Acity","Dcity",4]
["Ccity","Ecity",1]
输出:
[]
解释:
没有办法连通
Notice
返回按cost排序的连接方法,如果cost相同就按照city1进行排序,如果city1也相同那么就按照city2进行排序。
思路:需要判断是否连通满了,所以需要用unionfind来判断;如何利用最小的边去connect,核心思想就是:首先把边按照value sort一下,然后依次取出来,给city的点进行编号,然后看两个点是否connect,不connect则conenct,这样每次用的边都是value最小的,那么最后connect完了之后,所用到的value就是最小的,而且由于点全部connect了,那么后面value比较大的边,就不会用来connect了;最后如果能够形成的边如果是n-1那么就是正确答案,否则不能connect所有的点,return empty list;
/**
* Definition for a Connection.
* public class Connection {
* public String city1, city2;
* public int cost;
* public Connection(String city1, String city2, int cost) {
* this.city1 = city1;
* this.city2 = city2;
* this.cost = cost;
* }
* }
*/
public class Solution {
private class UnionFind {
private int[] father;
private int count;
public UnionFind(int n) {
father = new int[n+1];
for(int i = 0; i <= n; i++) {
father[i] = i;
}
count = n;
}
public int find(int x) {
int j = x;
while(father[j] != j) {
j = father[j];
}
// path compression;
while(x != j) {
int fx = father[x];
father[x] = j;
x = fx;
}
return j;
}
public void union(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if(root_a != root_b) {
father[root_a] = root_b;
count--;
}
}
public int getCount() {
return this.count;
}
}
private class ConnectionComparator implements Comparator<Connection> {
@Override
public int compare(Connection a, Connection b) {
if(a.cost != b.cost) {
return a.cost - b.cost;
} else {
if(a.city1.equals(b.city1)){
return a.city2.compareTo(b.city2);
} else {
return a.city1.compareTo(b.city1);
}
}
}
}
public List<Connection> lowestCost(List<Connection> connections) {
// 需要判断是否连通满了,所以需要用unionfind来判断;
// 如何利用最小的边去connect,核心思想就是:首先把边按照value sort一下,然后依次取出来,给city的点进行编号,然后看两个点是否connect,不connect则conenct,这样每次用的边都是value最小的,那么最后connect完了之后,所用到的value就是最小的,而且由于点全部connect了,那么后面value比较大的边,就不会用来connect了;
// 最后如果能够形成的边如果是n-1那么就是正确答案,否则不能connect所有的点,return empty
List<Connection> result = new ArrayList<Connection>();
Collections.sort(connections, new ConnectionComparator());
int n = 0;
// 求出所有的点,并对其进行编号;
HashMap<String, Integer> hashmap = new HashMap<String, Integer>();
for(Connection connection: connections) {
if(!hashmap.containsKey(connection.city1)){
hashmap.put(connection.city1, ++n);
}
if(!hashmap.containsKey(connection.city2)){
hashmap.put(connection.city2, ++n);
}
}
UnionFind uf = new UnionFind(n);
// 依次取出最小cost的边,如果没有相连,连起来加入result;
for(Connection connection: connections) {
int a = hashmap.get(connection.city1);
int b = hashmap.get(connection.city2);
if(uf.find(a) != uf.find(b)){
uf.union(a, b);
result.add(connection);
}
}
if(result.size() == n -1) {
return result;
} else {
return new ArrayList<Connection>();
}
}
}
Accounts Merge
给定一个帐户列表,每个元素accounts [i]
是一个字符串列表,其中第一个元素accounts [i] [0]
是账户名称,其余元素是这个帐户的电子邮件。
现在,我们想合并这些帐户。
如果两个帐户有相同的电子邮件地址,则这两个帐户肯定属于同一个人。
请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为两个不同的人可能会使用相同的名称。
一个人可以拥有任意数量的账户,但他的所有帐户肯定具有相同的名称。
合并帐户后,按以下格式返回帐户:每个帐户的第一个元素是名称,其余元素是按字典序排序后的电子邮件。
帐户本身可以按任何顺序返回。
Example
样例 1:
输入:
[
["John", "[email protected]", "[email protected]"],
["John", "[email protected]"],
["John", "[email protected]", "[email protected]"],
["Mary", "[email protected]"]
]
输出:
[
["John", '[email protected]', '[email protected]', '[email protected]'],
["John", "[email protected]"],
["Mary", "[email protected]"]
]
解释:
第一个第三个John是同一个人的账户,因为这两个账户有相同的邮箱:"[email protected]".
剩下的两个账户分别是不同的人。因为他们没有和别的账户有相同的邮箱。
你可以以任意顺序返回结果。比如:
[
['Mary', '[email protected]'],
['John', '[email protected]'],
['John', '[email protected]', '[email protected]', '[email protected]']
]
也是可以的。
思路:经典的union find题目。以email为点来进行union,email刚开始的默认index是row index,此时union find的是accounts的index,0,1,2,如果后面email有相同的,则跟row index union。然后再根据index相同来收集emails。最后加入email去找index,找到name,然后combine成result,学会用putIfAbsent.
public class Solution {
/**
* @param accounts: List[List[str]]
* @return: return a List[List[str]]
*/
private class UnionFind {
private int[] father;
private int count;
public UnionFind(int n) {
father = new int[n+1];
for(int i = 0; i <= n; i++) {
father[i] = i;
}
count = n;
}
public int find(int x) {
int j = x;
while(father[j] != j) {
j = father[j];
}
// path compression;
while(x != j) {
int fx = father[x];
father[x] = j;
x = fx;
}
return j;
}
public void union(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if(root_a != root_b) {
father[root_a] = root_b;
count--;
}
}
public int getCount() {
return this.count;
}
}
public List<List<String>> accountsMerge(List<List<String>> accounts) {
List<List<String>> results = new ArrayList<List<String>>();
if(accounts == null || accounts.size() == 0) {
return results;
}
HashMap<String, Integer> emailToIndex = new HashMap<String, Integer>();
UnionFind uf = new UnionFind(accounts.size());
for(int i = 0; i < accounts.size(); i++) {
List<String> list = accounts.get(i);
for(int j = 1; j < list.size(); j++) {
String email = list.get(j);
emailToIndex.putIfAbsent(email, i);
uf.union(emailToIndex.get(email), i);
}
}
// collect result;
// 首先按照root index,去搜集这个union集合下面的email
HashMap<Integer, List<String>> indexToEmailList = new HashMap<Integer, List<String>>();
for(String email: emailToIndex.keySet()){
int index = emailToIndex.get(email);
int root = uf.find(index);
indexToEmailList.putIfAbsent(root, new ArrayList<String>());
indexToEmailList.get(root).add(email);
}
// 然后按照email的index,去找name,然后整合输出result;
for(Integer index: indexToEmailList.keySet()){
String name = accounts.get(index).get(0);
List<String> list = indexToEmailList.get(index);
Collections.sort(list);
list.add(0, name);
results.add(list);
}
return results;
}
}
Graph Valid Tree
给出 n
个节点,标号分别从 0
到 n - 1
并且给出一个 无向
边的列表 (给出每条边的两个顶点), 写一个函数去判断这张`无向`图是否是一棵树
Example
样例 1:
输入: n = 5 edges = [[0, 1], [0, 2], [0, 3], [1, 4]]
输出: true.
思路:tree的条件就是:
1.只有一个连通块, connected component = 1;
2. 边的数目 = 点的数目-1; edges = points - 1;
public class Solution {
/**
* @param n: An integer
* @param edges: a list of undirected edges
* @return: true if it's a valid tree, or false
*/
private class UnionFind {
private int[] father;
private int count;
public UnionFind(int n) {
this.father = new int[n+1];
// 千万别忘记初始化father数组;
for(int i = 0; i <= n; i++) {
father[i] = i;
}
this.count = n;
}
public int find(int x) {
int j = x;
while(father[j] != j) {
j = father[j];
}
// path compression;
while(x != j) {
int fx = father[x];
father[x] = j;
x = fx;
}
return j;
}
public void union(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if(root_a != root_b) {
father[root_a] = root_b;
count--;
}
}
public int getCount() {
return count;
}
}
public boolean validTree(int n, int[][] edges) {
if(n <= 0 || edges == null){
return false;
}
if(edges.length != n - 1) {
return false;
}
UnionFind uf = new UnionFind(n);
for(int i = 0; i < edges.length; i++) {
uf.union(edges[i][0], edges[i][1]);
}
return uf.getCount() == 1;
}
}
Maximum Association Set
亚麻卖书,每本书都有与其关联性很强的书,给出ListA与ListB,表示ListA[i]
与ListB[i]
有关联,输出互相关联的最大集合。(输出任意顺序),题目保证只有一个最大的集合。
Example
样例 1:
输入: ListA = ["abc","abc","abc"], ListB = ["bcd","acd","def"]
输出: ["abc","acd","bcd","def"]
解释:
"abc" 和其他书均有关联,全集就是最大集合。
样例 2:
输入: ListA = ["a","b","d","e","f"], ListB = ["b","c","e","g","g"]
输出: ["d","e","f","g"]
解释:
关联的集合有 [a, b, c] 和 [d, e, g, f], 最大的是 [d, e, g, f]
Notice
- 书籍的数量不会超过
5000
。
思路:以书为node,建立一个<string,Integer> mapping, 注意size是2*n, 用不同的index代表不同的书,如果hashmap里面有了,就不用加了,是同一本书;union完之后,扫描一遍求出最大的size和最大的index,然后根据index来收集书名;注意去重,因为我是扫描了一遍书名,书名就有重复的,必须去重复;
public class Solution {
/**
* @param ListA: The relation between ListB's books
* @param ListB: The relation between ListA's books
* @return: The answer
*/
private class UnionFind {
private int[] father;
private int[] size;
public UnionFind(int n) {
father = new int[n+1];
size = new int[n+1];
for(int i = 0; i <= n; i++) {
father[i] = i;
size[i] = 1;
}
}
public int find(int x) {
int j = x;
while(father[j] != j) {
j = father[j];
}
// path compression;
while(x != j) {
int fx = father[x];
father[x] = j;
x = fx;
}
return j;
}
public void union(int a, int b) {
int root_a = find(a);
int root_b = find(b);
if(root_a != root_b) {
father[root_a] = root_b;
size[root_b] += size[root_a];
}
}
public int getSize(int index){
return this.size[index];
}
}
public List<String> maximumAssociationSet(String[] ListA, String[] ListB) {
List<String> result = new ArrayList<String>();
if(ListA == null || ListA.length == 0 || ListB == null || ListB.length == 0) {
return result;
}
int n = ListA.length;
// 注意这是两倍的size;
UnionFind uf = new UnionFind(2*n);
// 只用一个map就可以建立mapping;
HashMap<String, Integer> map = new HashMap<String, Integer>();
for(int i = 0; i < ListA.length; i++) {
if(!map.containsKey(ListA[i])){
map.put(ListA[i], i);
}
if(!map.containsKey(ListB[i])){
map.put(ListB[i], n+i);
}
uf.union(map.get(ListA[i]), map.get(ListB[i]));
}
int globalmax = 0;
int maxindex = 0;
for(int i = 0; i < ListA.length; i++){
int index = uf.find(map.get(ListA[i]));
int size = uf.getSize(index);
if(size > globalmax) {
globalmax = size;
maxindex = index;
}
}
HashSet<String> set = new HashSet<String>();
for(int i = 0; i < ListA.length; i++) {
int root_a = uf.find(map.get(ListA[i]));
int root_b = uf.find(map.get(ListB[i]));
if(root_a == maxindex && !set.contains(ListA[i])){
result.add(ListA[i]);
set.add(ListA[i]);
}
if(root_b == maxindex && !set.contains(ListB[i])){
result.add(ListB[i]);
set.add(ListB[i]);
}
}
return result;
}
}