Fundamentals
The usual pattern of Tree DFS (or recursion) is:
1
2
3
4
5
6
7
8
9
| private T1 global;
public T2 processTree(TreeNode root) {
// call dfs(root)
}
private T4 dfs(TreeNode node, T3 local) {
// call dfs(node.left) and dfs(node.right)
}
|
There are 3 core components:
- Global variable (
T1 global
) - DFS parameter (
T3 local
) - DFS return value (
T4
)
Global Variable
Global variables store the global state throughout the entire recursions. They are usually modified by each DFS call. Generally, there are two types of global variables: primitive/collection and structure.
Primitive/Collection
A primitive global variable is usually used as a counter or state tracker (e.g., bit mask). A collection global variable is used to store related elements.
Convert BST to Greater Tree
1
2
3
4
5
6
7
8
9
10
11
| private int sum = 0;
public TreeNode convertBST(TreeNode root) {
if (root != null) {
convertBST(root.right);
sum += root.val;
root.val = sum;
convertBST(root.left);
}
return root;
}
|
Distribute Coins in Binary Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| private int move = 0;
public int distributeCoins(TreeNode root) {
dfs(root);
return move;
}
// excess of the subtree (#coins - #nodes)
private int dfs(TreeNode node) {
if (node == null) {
return 0;
}
int left = dfs(node.left), right = dfs(node.right);
move += Math.abs(left) + Math.abs(right);
return left + right + node.val - 1;
}
|
Equal Tree Partition
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| private Set<Integer> set = new HashSet<>();
public boolean checkEqualTree(TreeNode root) {
int sum = root.val + dfs(root.left) + dfs(root.right);
// the root sum is not added to the set
return sum % 2 == 0 && set.contains(sum / 2);
}
private int dfs(TreeNode node) {
// 0 from null node is not added to the set
// so trees like [0], [0, 1, -1] will return correct answer
if (node == null) {
return 0;
}
int sum = node.val + dfs(node.left) + dfs(node.right);
set.add(sum);
return sum;
}
|
Structure
A structure global variable is used for maintaining a data structure, like linked list. Traverse order usally plays an important role.
Flatten Binary Tree to Linked List
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // the last node of the already formed linked list
private TreeNode last = null;
public void flatten(TreeNode root) {
if (root == null) {
return;
}
if (last != null) {
last.left = null;
last.right = root;
}
last = root;
// memorizes the right child before the left subtree recursion,
// which could modify the right child of root (last)
TreeNode right = root.right;
flatten(root.left);
flatten(right);
}
|
Flatten a Multilevel Doubly Linked List
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| private Node tail = null;
public Node flatten(Node head) {
if (head == null) {
return null;
}
head.prev = tail;
tail = head;
Node next = head.next;
head.next = flatten(head.child);
head.child = null;
tail.next = flatten(next);
return head;
}
|
Minimum Absolute Difference in BST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| private int d = Integer.MAX_VALUE;
private TreeNode prev = null;
public int getMinimumDifference(TreeNode root) {
if (root == null) {
return Integer.MAX_VALUE;
}
getMinimumDifference(root.left);
// inorder
if (prev != null) {
d = Math.min(d, root.val - prev.val);
}
prev = root;
getMinimumDifference(root.right);
return d;
}
|
Find Bottom Left Tree Value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| int bottomLeft = 0, depth = -1;
void dfs(TreeNode* node, int d) {
if (depth < d) {
bottomLeft = node->val;
depth = d;
}
if (node->left != nullptr) {
dfs(node->left, d + 1);
}
if (node->right != nullptr) {
dfs(node->right, d + 1);
}
}
public:
int findBottomLeftValue(TreeNode* root) {
dfs(root, 0);
return bottomLeft;
}
|
Another solution is right-to-left-BFS.
Maximum Width of Binary Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| public int widthOfBinaryTree(TreeNode root) {
return dfs(root, 0, 0, new ArrayList<>());
}
private int dfs(TreeNode node, int level, int index, List<Integer> starts) {
if (node == null) {
return 0;
}
// the first node visited in this level
if (starts.size() == level) {
starts.add(index);
}
int curr = index - starts.get(level) + 1;
int left = dfs(node.left, level + 1, index * 2 + 1, starts);
int right = dfs(node.right, level + 1, index * 2 + 2, starts);
return Math.max(curr, Math.max(left, right));
}
|
DFS Parameter
Count Paths That Can Form a Palindrome in a Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| private List<Integer>[] tree;
private String s;
public long countPalindromePaths(List<Integer> parent, String s) {
int n = parent.size();
tree = new List[n];
for (int i = 0; i < n; i++) {
tree[i] = new ArrayList<>();
}
for (int i = 1; i < n; i++) {
tree[parent.get(i)].add(i);
}
this.s = s;
Map<Integer, Long> freqs = new HashMap<>();
freqs.put(0, 1l);
return dfs(0, freqs, 0);
}
private long dfs(int node, Map<Integer, Long> freqs, int mask) {
long c = 0;
if (node > 0) {
mask ^= (1 << (s.charAt(node) - 'a'));
for (int i = 0; i < 26; i++) {
c += freqs.getOrDefault(mask ^ (1 << i), 0l);
}
long v = freqs.getOrDefault(mask, 0l);
freqs.put(mask, v + 1);
c += v;
}
for (int child : tree[node]) {
c += dfs(child, freqs, mask);
}
return c;
}
|
DFS Return Value
House Robber III
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| public int rob(TreeNode root) {
int[] dp = dfs(root);
return Math.max(dp[0], dp[1]);
}
private int[] dfs(TreeNode root) {
// {not robbed, robbed}
int[] dp = new int[2];
if (root == null) {
return dp;
}
int[] left = dfs(root.left), right = dfs(root.right);
dp[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
dp[1] = root.val + left[0] + right[0];
return dp;
}
|
Maximum Score After Applying Operations on a Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| // score, subtree sum
pair<long long, long long> dfs(vector<vector<int>>& tree, vector<int>& values, int node, int parent) {
// When the subtree is rooted at leaf, the node can't be reset
if (tree[node].size() == 1 && node > 0) {
return {0, values[node]};
}
// pick: reset current node
long long pick = values[node], sum = values[node];
for (auto& neighbor : tree[node]) {
if (neighbor != parent) {
auto v = dfs(tree, values, neighbor, node);
pick += v.first;
sum += v.second;
}
}
// sum - values[node]: keep current node
return {max(pick, sum - values[node]), sum};
}
public:
long long maximumScoreAfterOperations(vector<vector<int>>& edges, vector<int>& values) {
int n = values.size();
vector<vector<int>> tree(n);
for (auto& e : edges) {
tree[e[0]].push_back(e[1]);
tree[e[1]].push_back(e[0]);
}
return dfs(tree, values, 0, -1).first;
}
|
Largest BST Subtree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| public int largestBSTSubtree(TreeNode root) {
return dfs(root)[0];
}
private int[] dfs(TreeNode node) {
if (node == null) {
// [0]: number of nodes of the largest BST in this subtree
// [1]: min value of the subtree
// [2]: max value of the subtree
// it's a hack to assign null node in this way
// so any node can be a valid parent of it to form a BST
return new int[]{0, Integer.MAX_VALUE, Integer.MIN_VALUE};
}
int[] left = dfs(node.left), right = dfs(node.right);
return node.val > left[2] && node.val < right[1] ?
// valid BST
// [1] is min(node.val, left[1]) rather than left[1] to handle the corner case
// when left child node is null
// same for [2]
new int[]{1 + left[0] + right[0], Math.min(node.val, left[1]), Math.max(node.val, right[2])} :
// invalid BST
// assign min and max in this way so no node can be a valid parent of it to form a BST
new int[]{Math.max(left[0], right[0]), Integer.MIN_VALUE, Integer.MAX_VALUE};
}
|
Split BST
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| public TreeNode[] splitBST(TreeNode root, int V) {
TreeNode[] result = new TreeNode[2];
if (root != null) {
if (root.val <= V) {
result = splitBST(root.right, V);
root.right = result[0];
result[0] = root;
} else {
result = splitBST(root.left, V);
root.left = result[1];
result[1] = root;
}
}
return result;
}
|
Difference Between Maximum and Minimum Price Sum
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
| private long maxCost;
public long maxOutput(int n, int[][] edges, int[] price) {
List<Integer>[] tree = new List[n];
for (int i = 0; i < n; i++) {
tree[i] = new ArrayList<>();
}
for (int[] e : edges) {
tree[e[0]].add(e[1]);
tree[e[1]].add(e[0]);
}
// roots the tree at node 0
dfs(tree, 0, -1, price);
// min price sum is always the node itself
// so for a particular node as root, its cost is the max of its child paths (excluding itself)
// also, to maximize the cost, the root node must be a leaf node of the tree (degree == 1)
return maxCost;
}
private long[] dfs(List<Integer>[] tree, int node, int parent, int[] price) {
// postorder
// curr[0]: max path sum from `node` to a leaf node
// curr[1]: max path sum from `node` to the parent of a leaf node
long[] curr = {price[node], 0};
for (int child : tree[node]) {
if (child != parent) {
long[] next = dfs(tree, child, node, price);
// to exclude the leaf node, there are two options:
// curr[0] + next[1]: max sum among all previous child paths from `node` + `child` path without a leaf
// curr[1] + next[0]: max sum among all previous child paths from `node` excluding leaves + `child` path with a leaf
maxCost = Math.max(maxCost, Math.max(curr[0] + next[1], curr[1] + next[0]));
curr[0] = Math.max(curr[0], next[0] + price[node]);
curr[1] = Math.max(curr[1], next[1] + price[node]);
}
}
return curr;
}
|
A more complex but general approach is by rerooting.
Second Minimum Node in a Binary Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| private int min;
private long secondMin = Long.MAX_VALUE;
public int findSecondMinimumValue(TreeNode root) {
// root has the minimum value
min = root.val;
dfs(root);
return secondMin < Long.MAX_VALUE ? (int)secondMin : -1;
}
private void dfs(TreeNode node) {
if (node == null) {
return;
}
if (min < node.val && node.val < secondMin) {
secondMin = node.val;
} else if (min == node.val) {
dfs(node.left);
dfs(node.right);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| public int findSecondMinimumValue(TreeNode root) {
// tree leaf
if (root.left == null) {
return -1;
}
// The second minimum value on left path including root
int left = root.left.val == root.val ? findSecondMinimumValue(root.left) : root.left.val;
// The second minimum value on right path including root
int right = root.right.val == root.val ? findSecondMinimumValue(root.right) : root.right.val;
// if left == -1 && right == -1, returns -1
// else left == -1 || right == -1, returns none -1
// else returns the lesser of the two second minimum values
return left == -1 || right == -1 ? Math.max(left, right) : Math.min(left, right);
}
|
Top-down Local State
Count Good Nodes in Binary Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| public int goodNodes(TreeNode root) {
return dfs(root, root.val);
}
private int dfs(TreeNode node, int max) {
if (node == null) {
return 0;
}
int count = node.val >= max ? 1 : 0;
max = Math.max(max, node.val);
count += dfs(node.left, max);
count += dfs(node.right, max);
return count;
}
|
Maximum Difference Between Node and Ancestor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| private int diff = 0;
public int maxAncestorDiff(TreeNode root) {
dfs(root, root.val, root.val);
return diff;
}
private void dfs(TreeNode node, int min, int max) {
if (node == null) {
return;
}
min = Math.min(min, node.val);
max = Math.max(max, node.val);
diff = Math.max(diff, max - min);
dfs(node.left, min, max);
dfs(node.right, min, max);
}
|
Pseudo-Palindromic Paths in a Binary Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| public int pseudoPalindromicPaths (TreeNode root) {
return dfs(root, 0);
}
/**
* Count pseudo palindromic paths by DFS.
* In a pseudo palindromic path, each digit appears at most once.
* @param node current node
* @param vector an integer whose i-th bit indicates the presence of digit i
* @return count of pseudo palinddromic paths
*/
private int dfs(TreeNode node, int vector) {
if (node == null) {
return 0;
}
vector ^= 1 << (node.val - 1);
int count = dfs(node.left, vector) + dfs(node.right, vector);
// leaf node
if (node.left == node.right && (vector & (vector - 1)) == 0) {
count++;
}
return count;
}
|
Reverse Odd Levels of Binary Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| public TreeNode reverseOddLevels(TreeNode root) {
dfs(root.left, root.right, 0);
return root;
}
// node1 and node2 are reflection symmetrical
private void dfs(TreeNode node1, TreeNode node2, int level) {
if (node1 == node2) {
return;
}
if (level % 2 == 0) {
int tmp = node1.val;
node1.val = node2.val;
node2.val = tmp;
}
dfs(node1.left, node2.right, level + 1);
dfs(node1.right, node2.left, level + 1);
}
|
Reorder Routes to Make All Paths Lead to the City Zero
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| private List<Integer>[] list;
public int minReorder(int n, int[][] connections) {
this.list = new List[n];
for (int i = 0; i < n; i++) {
list[i] = new ArrayList<>();
}
for (int[] c : connections) {
list[c[0]].add(c[1]);
list[c[1]].add(-c[0]);
}
return dfs(0, 0);
}
private int dfs(int prev, int node) {
int sum = 0;
for (int next : list[node]) {
if (Math.abs(next) != prev) {
// next > 0 means the direction is node -> next
sum += dfs(node, Math.abs(next)) + (next > 0 ? 1 : 0);
}
}
return sum;
}
|
Nested DFS
Path Sum III
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| public int pathSum(TreeNode root, int sum) {
if (root == null) {
return 0;
}
return pathSumFromNode(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
}
/**
* Counts paths with target sum which start from node.
* @param node the root of the subtree
* @param sum target sum
* @return the count of paths with target sum which start from node
*/
private int pathSumFromNode(TreeNode node, int sum) {
if (node == null) {
return 0;
}
return (sum == node.val ? 1 : 0) + pathSumFromNode(node.left, sum - node.val) + pathSumFromNode(node.right, sum - node.val);
}
|
Backtracking
Path Sum III
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| public int pathSum(TreeNode root, int sum) {
Map<Integer, Integer> prefixSum = new HashMap<>();
prefixSum.put(0,1);
return dfs(root, 0, sum, prefixSum);
}
public int dfs(TreeNode node, int currSum, int target, Map<Integer, Integer> prefixSum) {
if (node == null) {
return 0;
}
currSum += node.val;
int count = prefixSum.getOrDefault(currSum - target, 0);
// backtracking
prefixSum.compute(currSum, (k, v) -> v == null ? 1 : v + 1);
count += dfs(node.left, currSum, target, prefixSum) + dfs(node.right, currSum, target, prefixSum);
prefixSum.compute(currSum, (k, v) -> v - 1);
return count;
}
|
All Nodes Distance K in Binary Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| private List<Integer> list = new ArrayList<>();
private Map<TreeNode, Integer> map = new HashMap<>();
private TreeNode target;
private int k;
public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
this.target = target;
this.k = k;
distance(root);
dfs(root, map.get(root));
return list;
}
/**
* Returns the distance from node to target if the node is on the path from root to target;
* otherwise returns -1.
* @param node a node in the tree
* @return distance from node to target if node is on the path from root to target, otherwise -1
*/
private int distance(TreeNode node) {
if (node == null) {
return -1;
}
if (node == target) {
map.put(node, 0);
return 0;
}
int left = distance(node.left);
if (left >= 0) {
map.put(node, left + 1);
return left + 1;
}
int right = distance(node.right);
if (right >= 0) {
map.put(node, right + 1);
return right + 1;
}
return -1;
}
private void dfs(TreeNode node, int d) {
if (node == null) {
return;
}
if (map.containsKey(node)) {
d = map.get(node);
}
if (d == k) {
list.add(node.val);
}
dfs(node.left, d + 1);
dfs(node.right, d + 1);
}
|
Multiple DFS
Sum of Distances in Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| private List<List<Integer>> tree = new ArrayList<>();
// count[i]: count of all nodes in the subtree i
private int[] answer, count;
public int[] sumOfDistancesInTree(int n, int[][] edges) {
this.answer = new int[n];
this.count = new int[n];
for (int i = 0; i < n; i++) {
tree.add(new ArrayList<>());
}
for (int[] e : edges) {
tree.get(e[0]).add(e[1]);
tree.get(e[1]).add(e[0]);
}
dfs(0, -1);
dfs2(0, -1);
return answer;
}
// postorder, computes count[] and answer[]
private void dfs(int node, int parent) {
for (int child : tree.get(node)) {
if (child == parent) {
continue;
}
dfs(child, node);
count[node] += count[child];
// sum of distances between this node and all the other nodes in the subtree
answer[node] += answer[child] + count[child];
}
count[node]++;
}
// preorder, updates answer[]
private void dfs2(int node, int parent) {
for (int child : tree.get(node)) {
if (child == parent) {
continue;
}
// when we move node to child:
// * count[child] nodes get 1 closer to node
// * n - count[i] nodes get 1 further to node
answer[child] = answer[node] - count[child] + count.length - count[child];
dfs2(child, node);
}
}
|
Postorder
In postorder, we don’t have to pass parent node as a parameter of dfs().
Delete Nodes And Return Forest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| private List<TreeNode> list = new ArrayList<>();
private Set<Integer> set = new HashSet<>();
public List<TreeNode> delNodes(TreeNode root, int[] to_delete) {
for (int i : to_delete) {
set.add(i);
}
if (!set.contains(root.val)) {
list.add(root);
}
dfs(root);
return list;
}
private TreeNode dfs(TreeNode node) {
if (node == null) {
return null;
}
node.left = dfs(node.left);
node.right = dfs(node.right);
// postorder
if (set.contains(node.val)) {
if (node.left != null) {
list.add(node.left);
}
if (node.right != null) {
list.add(node.right);
}
return null;
}
return node;
}
|
Greedy
Binary Tree Cameras
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| private int count = 0;
private enum Camera {
NOT_MONITORED, // not monitored
HAS_CAMERA, // has camera
MONITORED // monitored, no camera
};
/*Return 0 if it's a leaf.
Return 1 if it's a parent of a leaf, with a camera on this node.
Return 2 if it's coverd, without a camera on this node.*/
public int minCameraCover(TreeNode root) {
// installs cameras on parents of all leaves
// then removes all monitored nodes
// installs a camera at root if it's not monitored
return dfs(root) == Camera.NOT_MONITORED ? ++count : count;
}
public Camera dfs(TreeNode root) {
// if there's no node, it's already monitored
if (root == null) {
return Camera.MONITORED;
}
Camera left = dfs(root.left), right = dfs(root.right);
// if either child node is not monitored,
// installs a camera at the current node
if (left == Camera.NOT_MONITORED || right == Camera.NOT_MONITORED) {
count++;
return Camera.HAS_CAMERA;
}
return left == Camera.HAS_CAMERA || right == Camera.HAS_CAMERA ? Camera.MONITORED : Camera.NOT_MONITORED;
}
|
Smallest Missing Genetic Value in Each Subtree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| private Map<Integer, List<Integer>> tree = new HashMap<>();
private Set<Integer> set = new HashSet<>();
public int[] smallestMissingValueSubtree(int[] parents, int[] nums) {
int n = parents.length;
for (int i = 0; i < n; i++) {
tree.computeIfAbsent(parents[i], k -> new ArrayList<>()).add(i);
}
// only the node with genetic value 1 and its ancestors have missing values > 1
int miss = 1;
int[] ans = new int[n];
Arrays.fill(ans, miss);
// finds the node with genetic value 1
int node = -1;
for (int i = 0; i < n; i++) {
if (nums[i] == 1) {
node = i;
break;
}
}
if (node < 0) {
return ans;
}
int prev = -1;
while (node >= 0) {
if (tree.containsKey(node)) {
for (int child : tree.get(node)) {
// skips previously visited child
if (child != prev) {
dfs(nums, child);
}
}
}
set.add(nums[node]);
// finds next missing genetic value
while (set.contains(miss)) {
miss++;
}
ans[node] = miss;
prev = node;
// goes up by one node
node = parents[node];
}
return ans;
}
// adds all descendants to the set
private void dfs(int[] nums, int node) {
set.add(nums[node]);
if (tree.containsKey(node)) {
for (int child : tree.get(node)) {
dfs(nums, child);
}
}
}
|
Number Of Ways To Reconstruct A Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
| public int checkWays(int[][] pairs) {
// builds graph
Map<Integer, Set<Integer>> graph = new HashMap<>();
for (int[] p : pairs) {
graph.computeIfAbsent(p[0], v -> new HashSet<>()).add(p[1]);
graph.computeIfAbsent(p[1], v -> new HashSet<>()).add(p[0]);
}
// {node, degree}
// the degree is of the graph node, not of the tree node
Queue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> -a[1]));
for (var e : graph.entrySet()) {
pq.offer(new int[]{e.getKey(), e.getValue().size()});
}
// number of nodes
int n = graph.size();
Set<Integer> visited = new HashSet<>();
boolean isMultiple = false;
// selects the node with the greatest graph degree greedily
while (!pq.isEmpty()) {
int[] curr = pq.poll();
// a node's parent's pairs always contain all of the node's pairs
// so parent's degree is always >= the child node's degree
//
// we are processing from max degree to min in descending order
// so the already visited neighbors are the node's ancestors
// and the one with least degree is its parent
int parent = 0, minDegree = n;
for (int neighbor : graph.get(curr[0])) {
if (visited.contains(neighbor) && graph.get(neighbor).size() < minDegree) {
parent = neighbor;
minDegree = graph.get(neighbor).size();
}
}
visited.add(curr[0]);
// current node is a root candidate (parent == 0)
if (parent == 0) {
// if the node has degree < n - 1, there's no root
if (curr[1] != n - 1) {
return 0;
}
continue;
}
// parent's pairs must contain the current node's neighbor
for (int neighbor : graph.get(curr[0])) {
if (neighbor != parent && !graph.get(parent).contains(neighbor)) {
return 0;
}
}
// parent's degree = current node's degree
if (minDegree == curr[1]) {
isMultiple = true;
}
}
return isMultiple ? 2 : 1;
}
|
Dynamic Programming
Minimum Edge Reversals So Every Node Is Reachable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
| map<pair<int, int>, int> memo;
int dfs(const vector<vector<int>> &graph, const vector<vector<int>> &revGraph, int node, int parent) {
pair<int, int> key{node, parent};
if (memo.contains(key))
{
return memo[key];
}
int ans = 0;
for (auto &neighbor : graph[node]) {
if (neighbor != parent)
{
ans += dfs(graph, revGraph, neighbor, node);
}
}
for (auto &neighbor : revGraph[node]) {
if (neighbor != parent)
{
ans += dfs(graph, revGraph, neighbor, node) + 1;
}
}
return memo[key] = ans;
}
public:
vector<int> minEdgeReversals(int n, vector<vector<int>>& edges) {
vector<vector<int>> graph(n);
vector<vector<int>> revGraph(n);
for (auto e : edges) {
graph[e[0]].push_back(e[1]);
revGraph[e[1]].push_back(e[0]);
}
vector<int> answer;
for (int i = 0; i < n; i++) {
answer.push_back(dfs(graph, revGraph, i, -1));
}
return answer;
}
|