You are given a 0-indexed integer array nums
, and you are allowed to traverse between its indices. You can traverse between index i
and index j
, i != j
, if and only if gcd(nums[i], nums[j]) > 1
, where gcd
is the greatest common divisor.
Your task is to determine if for every pair of indices i
and j
in nums, where i < j
, there exists a sequence of traversals that can take us from i
to j
.
Return true
* if it is possible to traverse between all such pairs of indices,* or false
* otherwise.*
Example 1:
Input: nums = [2,3,6]
Output: true
Explanation: In this example, there are 3 possible pairs of indices: (0, 1), (0, 2), and (1, 2).
To go from index 0 to index 1, we can use the sequence of traversals 0 -> 2 -> 1, where we move from index 0 to index 2 because gcd(nums[0], nums[2]) = gcd(2, 6) = 2 > 1, and then move from index 2 to index 1 because gcd(nums[2], nums[1]) = gcd(6, 3) = 3 > 1.
To go from index 0 to index 2, we can just go directly because gcd(nums[0], nums[2]) = gcd(2, 6) = 2 > 1. Likewise, to go from index 1 to index 2, we can just go directly because gcd(nums[1], nums[2]) = gcd(3, 6) = 3 > 1.
Example 2:
Input: nums = [3,9,5]
Output: false
Explanation: No sequence of traversals can take us from index 0 to index 2 in this example. So, we return false.
Example 3:
Input: nums = [4,3,12,8]
Output: true
Explanation: There are 6 possible pairs of indices to traverse between: (0, 1), (0, 2), (0, 3), (1, 2), (1, 3), and (2, 3). A valid sequence of traversals exists for each pair, so we return true.
import math
class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, i):
if self.parent[i] == i:
return i
self.parent[i] = self.find(self.parent[i]) # Path compression
return self.parent[i]
def union(self, i, j):
root_i = self.find(i)
root_j = self.find(j)
if root_i != root_j:
if self.rank[root_i] < self.rank[root_j]:
self.parent[root_i] = root_j
elif self.rank[root_i] > self.rank[root_j]:
self.parent[root_j] = root_i
else:
self.parent[root_j] = root_i
self.rank[root_i] += 1
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)
def canTraverseAllPairs(nums):
n = len(nums)
if n == 1:
return True
uf = UnionFind(n)
for i in range(n):
for j in range(i + 1, n):
if gcd(nums[i], nums[j]) > 1:
uf.union(i, j)
# Check if all elements are in the same connected component
root = uf.find(0)
for i in range(1, n):
if uf.find(i) != root:
return False
return True
# Optimized solution with prime factorization
def canTraverseAllPairsOptimized(nums):
n = len(nums)
if n == 1:
return True
uf = UnionFind(n)
prime_to_index = {}
def prime_factors(num):
factors = set()
d = 2
while d * d <= num:
while num % d == 0:
factors.add(d)
num //= d
d += 1
if num > 1:
factors.add(num)
return factors
for i in range(n):
factors = prime_factors(nums[i])
for factor in factors:
if factor in prime_to_index:
uf.union(i, prime_to_index[factor])
else:
prime_to_index[factor] = i
root = uf.find(0)
for i in range(1, n):
if uf.find(i) != root:
return False
return True
# Example Usages:
nums1 = [2, 3, 6]
print(f"Input: {nums1}, Output: {canTraverseAllPairsOptimized(nums1)}") # Output: True
nums2 = [3, 9, 5]
print(f"Input: {nums2}, Output: {canTraverseAllPairsOptimized(nums2)}") # Output: False
nums3 = [4, 3, 12, 8]
print(f"Input: {nums3}, Output: {canTraverseAllPairsOptimized(nums3)}") # Output: True
nums4 = [1, 15, 7, 8, 20]
print(f"Input: {nums4}, Output: {canTraverseAllPairsOptimized(nums4)}") # Output: True
nums5 = [1,2,3]
print(f"Input: {nums5}, Output: {canTraverseAllPairsOptimized(nums5)}") # Output: false
The brute force approach involves iterating through all pairs of indices (i, j) and checking if there exists a path between them. This can be done using Depth-First Search (DFS) or Breadth-First Search (BFS). The time complexity for this approach is high because for each pair, we potentially have to explore all possible paths.
The optimized solution uses the Union-Find data structure combined with prime factorization. Here's a breakdown:
nums
, find its prime factors.nums[i]
and nums[j]
share a prime factor, we union indices i
and j
.n
takes O(sqrt(n)) time in the worst case.n
is the length of the nums
array and max(nums)
is the maximum value in the nums
array.n
is the number of elements in the input array.False
.True
.