Bit manipulation and diameter of a tree
1617. Count Subtrees With Max Distance Between Cities
Problem
1617. Count Subtrees With Max Distance Between Cities
Explanation
bit manipulation to traverse all the subsets
It only have 15 nodes, if we use bit manipulation, we can list all the possible subset of the 15 nodes by traverse from
0 to 2¹⁵
The trick is named bit manipulation
For each number, if the bit value is 1, it mean the subset contains a node.
For example, A, B, C, D, we will have
0000 →{}
0001 →{D}
0010 →{C}
0011 →{C, D}
…
1111 →{A,B,C,D}
Check whether a subset is a Tree
For each subset, it may not be a tree. For example, if we have AB connected and CD connected. Then A and C can not generate a Tree. Actually, A and C will be a forest. A way to check whether a subset is a Tree is: traverse the graph from any node, if it is a tree, then, the traverse process will collect all the nodes. If not, the traverse process will be interrupted during the process and some nodes will not be seen or visited.
We can check whether len(seen)== len(nodes)
If it is a tree, get the diameter.
This is a standard problem and we can use two BFS to solve the problem. For more details see my previous article.
Summary
Main steps:
- traverse all possible subsets
- for each subset, check whether it is a tree. We can use DFS or BFS, since the diameter of the tree part is using BFS, I am also using BFS here
- Using two BFS to get the diameter of a tree if the graph is a tree.
Code
class Solution:
# return whehter the graph is a tree, the last node visited by BFS
# and the max depth of the tree
def bfs(self, node, graph):
Q = collections.deque([(node, 0)])
last_node = None
max_depth = 0
seen = {node}
while Q:
node, depth = Q.popleft()
last_node = node
max_depth = depth
for neighbor in graph[node]:
if neighbor not in seen:
seen.add(neighbor)
Q.append((neighbor, depth + 1))
is_tree = len(seen) == len(graph)
return is_tree, last_node, max_depth
def find_diameter(self, graph):
nodes = list(graph.keys())
is_tree, last_node , _ = self.bfs(nodes[0], graph)
if not is_tree:return -1
_, _, max_depth = self.bfs(last_node, graph)
return max_depth
def countSubgraphsForEachDiameter(self, n: int, edges: List[List[int]]) -> List[int]:
graph = collections.defaultdict(set)
for a, b in edges:
graph[a].add(b)
graph[b].add(a)
res = collections.defaultdict(int)
for k in range(1, 1<<n):
nodes = set()
for node in range(n):
if (1<<node) & k:
nodes.add(node+1)
if len(nodes)==1:continue
new_graph = collections.defaultdict(set)
for node in nodes:
new_graph[node] = graph[node] & nodes
if not new_graph[node]:break
else:
diameter = self.find_diameter(new_graph)
res[diameter] += 1
return [res[diameter] for diameter in range(1,n)]
Thanks for reading.