Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3229949

Browse files
author
dev1
committed
Add refactors of code smells for GraphValidTree.java and WordLadder.java
1 parent 20ce6de commit 3229949

File tree

2 files changed

+87
-130
lines changed

2 files changed

+87
-130
lines changed

src/main/java/com/leetcode/graphs/GraphValidTree.java

+48-66
Original file line numberDiff line numberDiff line change
@@ -28,95 +28,77 @@
2828
* @since 2019-08-05
2929
*/
3030
public class GraphValidTree {
31-
31+
3232
/**
33-
*
34-
* @param n
35-
* @param edges
36-
* @return
33+
* Checks if the given edges form a valid tree using BFS.
3734
*/
3835
public static boolean isValidTree(int n, int[][] edges) {
39-
List<List<Integer>> adjacencyList = new ArrayList<>(n);
36+
List<List<Integer>> adjacencyList = buildAdjacencyList(n, edges);
37+
return isTreeBFS(n, adjacencyList);
38+
}
4039

40+
/**
41+
* Builds the adjacency list from the given edges.
42+
*/
43+
private static List<List<Integer>> buildAdjacencyList(int n, int[][] edges) {
44+
List<List<Integer>> adjacencyList = new ArrayList<>(n);
4145
for (int i = 0; i < n; i++) {
4246
adjacencyList.add(new ArrayList<>());
4347
}
44-
45-
for (int i = 0; i < edges.length; i++) {
46-
adjacencyList.get(edges[i][0]).add(edges[i][1]);
47-
}
48-
49-
boolean[] visited = new boolean[n];
50-
51-
if (hasCycle(adjacencyList, 0, -1, visited)) {
52-
return false;
53-
}
54-
55-
for (int i = 0; i < n; i++) {
56-
if (!visited[i]) {
57-
return false;
58-
}
48+
for (int[] edge : edges) {
49+
adjacencyList.get(edge[0]).add(edge[1]);
50+
adjacencyList.get(edge[1]).add(edge[0]); // Since the graph is undirected
5951
}
60-
61-
return true;
52+
return adjacencyList;
6253
}
6354

64-
private static boolean hasCycle(List<List<Integer>> adjacencyList, int node1, int exclude, boolean[] visited) {
65-
visited[node1] = true;
66-
67-
for (int i = 0; i < adjacencyList.get(node1).size(); i++) {
68-
int node2 = adjacencyList.get(node1).get(i);
69-
70-
if ((visited[node2] && exclude != node2) || (!visited[node2] && hasCycle(adjacencyList, node2, node1, visited))) {
71-
return true;
55+
/**
56+
* Uses BFS to check for cycles and disconnected components.
57+
*/
58+
private static boolean isTreeBFS(int n, List<List<Integer>> adjacencyList) {
59+
Set<Integer> visited = new HashSet<>();
60+
Queue<Integer> queue = new LinkedList<>();
61+
queue.offer(0);
62+
visited.add(0);
63+
64+
while (!queue.isEmpty()) {
65+
int node = queue.poll();
66+
for (int neighbor : adjacencyList.get(node)) {
67+
if (!visited.add(neighbor)) { // if 'add' returns false, 'neighbor' is already visited
68+
return false;
69+
}
70+
queue.offer(neighbor);
7271
}
7372
}
74-
75-
return false;
73+
return visited.size() == n;
7674
}
7775

78-
7976
/**
80-
* Union-find algorithm: We keep all connected nodes in one set in the union operation and in find operation we
81-
* check whether two nodes belong to the same set. If yes then there's a cycle and if not then no cycle.
82-
*
83-
* Good articles on union-find:
84-
* - https://www.hackerearth.com/practice/notes/disjoint-set-union-union-find/
85-
* - https://www.youtube.com/watch?v=wU6udHRIkcc
86-
*
87-
* @param n
88-
* @param edges
89-
* @return
77+
* Checks if the given edges form a valid tree using the Union-Find algorithm.
9078
*/
9179
public static boolean isValidTreeUsingUnionFind(int n, int[][] edges) {
92-
int[] roots = new int[n];
80+
int[] parent = new int[n];
81+
Arrays.fill(parent, -1);
9382

94-
for (int i = 0; i < n; i++) {
95-
roots[i] = i;
96-
}
83+
for (int[] edge : edges) {
84+
int x = find(parent, edge[0]);
85+
int y = find(parent, edge[1]);
9786

98-
for (int i = 0; i < edges.length; i++) {
99-
// find operation
100-
if (roots[edges[i][0]] == roots[edges[i][1]]) {
101-
return false;
102-
}
103-
// union operation
104-
roots[edges[i][1]] = findRoot(roots, roots[edges[i][0]]); // note: we can optimize this even further by
105-
// considering size of each side and then join the side with smaller size to the one with a larger size (weighted union).
106-
// We can use another array called size to keep count of the size or we can use the same root array with
107-
// negative values, i.e, negative resembles that the node is pointing to itself and the number will represent
108-
// the size. For example, roots = [-2, -1, -1, 0] means that node 3 is pointing to node 0 and node 0 is pointing
109-
// to itself and is has 2 nodes under it including itself.
87+
if (x == y) return false; // x and y are in the same set
88+
89+
// Union operation
90+
parent[y] = x;
11091
}
11192

112-
return edges.length == n - 1;
93+
return edges.length == n - 1; // Tree should have exactly n-1 edges
11394
}
11495

115-
private static int findRoot(int[] roots, int node) {
116-
while (roots[node] != node) {
117-
node = roots[node];
118-
}
119-
return node;
96+
/**
97+
* Finds the root of the node 'i' using path compression.
98+
*/
99+
private static int find(int[] parent, int i) {
100+
if (parent[i] == -1) return i;
101+
return find(parent, parent[i]);
120102
}
121103

122104
public static void main(String[] args) {

src/main/java/com/leetcode/graphs/WordLadder.java

+39-64
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,94 @@
11
package com.leetcode.graphs;
22

3-
43
import javafx.util.Pair;
5-
64
import java.util.*;
75

8-
import static org.junit.jupiter.api.Assertions.assertEquals;
9-
106
/**
117
* Level: Medium
128
* Link: https://leetcode.com/problems/word-ladder/
139
* Description:
1410
* Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation
1511
* sequence from beginWord to endWord, such that:
16-
* <p>
12+
*
1713
* Only one letter can be changed at a time. Each transformed word must exist in the word list. Note that beginWord
1814
* is not a transformed word.
19-
* <p>
15+
*
2016
* Note:
2117
* - Return 0 if there is no such transformation sequence.
2218
* - All words have the same length.
2319
* - All words contain only lowercase alphabetic characters.
2420
* - You may assume no duplicates in the word list.
2521
* - You may assume beginWord and endWord are non-empty and are not the same.
26-
* <p>
22+
*
2723
* Example 1:
2824
* Input:
2925
* beginWord = "hit",
3026
* endWord = "cog",
3127
* wordList = ["hot","dot","dog","lot","log","cog"]
32-
* <p>
28+
*
3329
* Output: 5
34-
* <p>
30+
*
3531
* Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog",
3632
* return its length 5.
37-
* <p>
33+
*
3834
* Example 2:
3935
* Input:
4036
* beginWord = "hit"
4137
* endWord = "cog"
4238
* wordList = ["hot","dot","dog","lot","log"]
43-
* <p>
39+
*
4440
* Output: 0
45-
* <p>
46-
* Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
4741
*
48-
* @author rampatra
49-
* @since 2019-08-15
42+
* Explanation: The endWord "cog" is not in wordList, therefore no possible transformation.
5043
*/
5144
public class WordLadder {
5245

53-
/**
54-
* Runtime: <a href="https://leetcode.com/submissions/detail/251960230/">79 ms</a>.
55-
*
56-
* @param beginWord
57-
* @param endWord
58-
* @param wordList
59-
* @return
60-
*/
46+
private static final char WILDCARD_CHAR = '*';
47+
6148
public static int ladderLength(String beginWord, String endWord, List<String> wordList) {
62-
int L = beginWord.length();
63-
Map<String, Set<String>> transformedToOriginalWordMap = new HashMap<>();
64-
Queue<Pair<String, Integer>> queue = new LinkedList<>();
49+
Map<String, Set<String>> allComboDict = preprocessWords(wordList);
50+
return performBFS(beginWord, endWord, allComboDict);
51+
}
6552

53+
private static Map<String, Set<String>> preprocessWords(List<String> wordList) {
54+
Map<String, Set<String>> allComboDict = new HashMap<>();
55+
int L = wordList.get(0).length();
6656
wordList.forEach(word -> {
67-
String transformedWord;
68-
for (int i = 0; i < L; i++) {
69-
transformedWord = word.substring(0, i) + "*" + word.substring(i + 1, L);
70-
transformedToOriginalWordMap.putIfAbsent(transformedWord, new HashSet<>());
71-
transformedToOriginalWordMap.get(transformedWord).add(word);
72-
}
73-
}
74-
);
57+
for (int i = 0; i < L; i++) {
58+
String newWord = word.substring(0, i) + WILDCARD_CHAR + word.substring(i + 1, L);
59+
allComboDict.computeIfAbsent(newWord, k -> new HashSet<>()).add(word);
60+
}
61+
});
62+
return allComboDict;
63+
}
7564

76-
Set<String> visited = new HashSet<>();
65+
private static int performBFS(String beginWord, String endWord, Map<String, Set<String>> allComboDict) {
66+
Queue<Pair<String, Integer>> queue = new LinkedList<>();
7767
queue.add(new Pair<>(beginWord, 1));
68+
69+
Set<String> visited = new HashSet<>();
7870
visited.add(beginWord);
7971

8072
while (!queue.isEmpty()) {
81-
Pair<String, Integer> currPair = queue.poll();
82-
String word = currPair.getKey();
83-
Integer level = currPair.getValue();
84-
85-
if (word.equals(endWord)) {
86-
return level;
87-
}
88-
89-
String transformedWord;
90-
for (int i = 0; i < L; i++) {
91-
transformedWord = word.substring(0, i) + "*" + word.substring(i + 1, L);
92-
93-
for (String originalWord : transformedToOriginalWordMap.getOrDefault(transformedWord, Collections.emptySet())) {
94-
if (!visited.contains(originalWord)) {
95-
queue.add(new Pair<>(originalWord, level + 1));
96-
visited.add(originalWord);
73+
Pair<String, Integer> node = queue.remove();
74+
String word = node.getKey();
75+
int level = node.getValue();
76+
for (int i = 0; i < word.length(); i++) {
77+
String newWord = word.substring(0, i) + WILDCARD_CHAR + word.substring(i + 1);
78+
for (String adjacentWord : allComboDict.getOrDefault(newWord, new HashSet<>())) {
79+
if (adjacentWord.equals(endWord)) {
80+
return level + 1;
81+
}
82+
if (!visited.contains(adjacentWord)) {
83+
visited.add(adjacentWord);
84+
queue.add(new Pair<>(adjacentWord, level + 1));
9785
}
9886
}
9987
}
10088
}
101-
10289
return 0;
10390
}
10491

105-
/**
106-
* TODO: Optimized both end BFS solution
107-
*
108-
* @param beginWord
109-
* @param endWord
110-
* @param wordList
111-
* @return
112-
*/
113-
public static int ladderLengthOptimized(String beginWord, String endWord, List<String> wordList) {
114-
return -1;
115-
}
116-
11792
public static void main(String[] args) {
11893
assertEquals(5, ladderLength("hit", "cog", Arrays.asList("hot", "dot", "dog", "lot", "log", "cog")));
11994
assertEquals(0, ladderLength("hit", "cog", Arrays.asList("hot", "dot", "dog", "lot", "log")));

0 commit comments

Comments
 (0)