def test_connected(): union = UnionFind(3) assert_equals(union.connected(0,1), False) assert_equals(union.connected(1,2), False) union.union(0,1) assert_equals(union.connected(0,1), True) assert_equals(union.connected(1,2), False) union.union(1,2) assert_equals(union.connected(2,0), True) assert_equals(union.connected(0,2), True) assert_equals(union.connected(1,2), True)
def test_tiny(self): uf = UnionFind(10) uf.union(4, 3) uf.union(3, 8) uf.union(6, 5) uf.union(9, 4) uf.union(2, 1) uf.union(5, 0) uf.union(7, 2) uf.union(6, 1) self.assertEqual(2, uf.count) self.assertTrue(uf.connected(2, 6)) self.assertFalse(uf.connected(4, 5))
def test_tiny(self): uf = UnionFind(10) uf.union(4, 3) uf.union(3, 8) uf.union(6, 5) uf.union(9, 4) uf.union(2, 1) uf.union(5, 0) uf.union(7, 2) uf.union(6, 1) self.assertEqual(2, uf.count) self.assertTrue(uf.connected(2, 6)) self.assertFalse(uf.connected(4, 5))
def __init__(self, graph): self.graph = graph self.pq = IndexMinPriorityQueue(graph.V) self.mst = [] # Empty list of edges # Build a priority queue for edge in graph.adjacent: # for edge in graph.adj(edges): self.pq.insert(edge, graph.adj(edge)) union_find = UnionFind(graph.adjacent.keys()) while not self.pq.isEmpty() and len(self.mst) < graph.V - 1: edges = self.pq.minIndex() self.pq.delMin() # Greedily add edges to MST for edge in edges: v = edge.either() w = edge.other(v) # Edge v-w does not create a cycle if not union_find.connected(v, w): union_find.union(v, w) # Merge sets self.mst.append(edge) # Add edge to MST
def minCostToSupplyWater(self, n: int, wells: List[int], pipes: List[List[int]]) -> int: # union find has size = n + 1 to accout for the virtual node uf = UnionFind(n + 1) total_cost = 0 # add virtual node and connect it to each house with cost of # building a well in that house as its weight by appending # it to the pipes array for i, cost in enumerate(wells): pipes.append([n + 1, i + 1, cost]) # sort the pipes array by weight ascending (Kruskal's algorithm) pipes_sorted = sorted(pipes, key=lambda pipe: pipe[2]) for i, pipe in enumerate(pipes_sorted): # if all houses are connected we are done if uf.count == 1: break # if the houses connected by the pipe with the lowest weight # are not connected, connect them via UnionFind.union and # add the cost of the pipe to total if not uf.connected(pipe[0] - 1, pipe[1] - 1): uf.union(pipe[0] - 1, pipe[1] - 1) total_cost += pipe[2] return total_cost
def validTree(self, n: int, edges: List[List[int]]) -> bool: uf = UnionFind(n) for edge in edges: if uf.connected(edge[0], edge[1]): return False uf.union(edge[0], edge[1]) return uf.count == 1
def search(self): """search for MST""" # build pirority queue edge_queue = Queue.PriorityQueue() for e in self.G.edges(): edge_queue.put((e.weight, e)) uf = UnionFind(self.G.V()) while not edge_queue.empty() and \ len(self.edge_list) < self.G.V() - 1: # greedily add edges to MST _, e = edge_queue.get() v = e.either() w = e.other(v) # check if edge v-w not create cycle if not uf.connected(v, w): # merge sets uf.union(v, w) # add edge self.edge_list.append(e)
def __mst(self): uf = UnionFind(self.graph.V()) for edge in self.__sorted_edges(): u, v, w = edge if not uf.connected(u, v): self.mst.append((u, v)) uf.union(u, v) self.w += w
def best_response(self, N, clib, croad, edges): sites = UnionFind(N) if croad >= clib: return N * clib fix_roads = 0 for u, v in edges: i, j = u - 1, v - 1 if sites.connected(i, j): continue sites.union(i, j) fix_roads += 1 return fix_roads * croad + sites.count() * clib
class Graph(): """simple Graph class to run clustering algorithm""" def __init__(self, file_name): lines = self.get_edges_size(self.open_file(file_name)) self.sorted_edges = tuple(sorted(self.process_line(lines), key=lambda edge: edge[2]) ) def open_file(self, file_name): """simple lines generator""" with open(file_name) as myfile: for line in myfile: yield line def get_edges_size(self, line_seq): """get the number of vertices as given in the first line of file setup the vertices as a tuple so we don't waste memory populate the __vertecies tuple with lists of tuples of vertex,cost """ num_verticies = int(next(line_seq).split()[0]) # next(line_seq) self._union_find = UnionFind(num_verticies) for line in line_seq: yield line def process_line(self, line_seq): for line in line_seq: types = (int, int, int) yield tuple(fun(val) for fun, val in zip(types, line.split())) def clustering(self): """ """ edges = (val for val in self.sorted_edges) while self._union_find.sets > 4: edge = next(edges) if not self._union_find.connected(edge[0], edge[1]): self._union_find.union(edge[0], edge[1]) while self._union_find.connected(edge[0], edge[1]): edge = next(edges) return edge
def __cluster(self): uf = UnionFind(self.graph.V()) min_spacing = 1e100 for edge in self.__sorted_edges(): u, v, w = edge if uf.count_components() > self.k and not uf.connected(u, v): uf.union(u, v) elif not uf.connected(u, v): # once we have k clusters, # examine each cross-clusters edge and pick the minimum if min_spacing > w: min_spacing = w self.spacing = min_spacing
def kruskal_mst(graph): union_find = UnionFind(graph.num_vertices) edges = sorted(graph.edges()) for e in edges: v = e.either() w = e.other(v) if union_find.connected(v, w): continue union_find.union(v, w) yield e
def main(argv): vtotal, vertices = construct(argv[0]) vertices = sorted(vertices) uf = UnionFind(vtotal) for i in xrange(vtotal): for j in xrange(i + 1, vtotal): if not uf.connected(vertices[i][1], vertices[j][1]): if hamming_distance(vertices[i][0], vertices[j][0]) <= 2: uf.union(vertices[i][1], vertices[j][1]) print print '%s clusters' % (uf.count()) print
def main(argv): vertices = construct(argv[0]) distances = generate_distances(24, 2) + generate_distances(24, 1) uf = UnionFind(len(vertices)) for vertex in vertices: for distance in distances: candidate = vertex ^ distance if candidate in vertices: if not uf.connected(vertices[vertex], vertices[candidate]): uf.union(vertices[vertex], vertices[candidate]) print print '%s clusters' % (uf.count()) print
def karger_min_cut(G, edges): n = max(G.keys()) cuts = UnionFind(n+1) edges_map = {} edges_index = 0 for _ in xrange(n-2): edges_index = contract(G, edges, cuts, edges_index) #print G, edges assert(len(G) == 2) u, v = G.keys() #assert(len(G[u]) == len(G[v])) #before returning the cuts list, we must remove the self loops from the adjacency list return filter(lambda (k, z): not cuts.connected(u, z), G[u]) #each edge is stored twice, so we can just return one of the two vertices' adj list
def main(argv): vtotal, edges = construct_edges(argv[0]) edges = sorted(edges) uf = UnionFind(vtotal) k = int(argv[1]) T = set([]) max_spacing = 0 while uf.count() >= k: edge = edges.pop(0) if not uf.connected(edge[1], edge[2]): uf.union(edge[1], edge[2]) max_spacing = edge[0] print print 'For %s clustering: max spacing = %s' % (k, max_spacing) print
class Graph(): """simple Graph class to run Prim's MST algorithm""" def __init__(self, file_name): lines = self.get_edges_size(self.open_file(file_name)) self.sorted_edges = tuple(sorted(self.process_line(lines), key=lambda edge: edge[2]) ) def open_file(self, file_name): """simple lines generator""" with open(file_name) as myfile: for line in myfile: yield line def get_edges_size(self, line_seq): """get the number of vertices as given in the first line of file setup the vertices as a tuple so we don't waste memory populate the __vertecies tuple with lists of tuples of vertex,cost """ num_verticies = int(next(line_seq).split()[0]) next(line_seq) self.__union_find = UnionFind(num_verticies) for line in line_seq: yield line def process_line(self, line_seq): for line in line_seq: types = (int, int, float) yield tuple(fun(val) for fun, val in zip(types, line.split())) def kruskal_mst(self): """pick a random start_vertex then extract from heap until empty""" total_cost = 0 edges = (val for val in self.sorted_edges) while self.__union_find.sets > 1: edge = next(edges) if self.__union_find.connected(edge[0], edge[1]): continue total_cost += edge[2] self.__union_find.union(edge[0], edge[1]) return total_cost
def karger_min_cut(G, edges): n = max(G.keys()) cuts = UnionFind(n + 1) edges_map = {} edges_index = 0 for _ in xrange(n - 2): edges_index = contract(G, edges, cuts, edges_index) #print G, edges assert (len(G) == 2) u, v = G.keys() #assert(len(G[u]) == len(G[v])) #before returning the cuts list, we must remove the self loops from the adjacency list return filter( lambda (k, z): not cuts.connected(u, z), G[u] ) #each edge is stored twice, so we can just return one of the two vertices' adj list
def solve(self, board: List[List[str]]) -> None: if not board: return n_rows = len(board) n_cols = len(board[0]) if n_rows < 3 or n_cols < 3: return dummy = n_rows * n_cols # index for dummy node #uf = SimpleUnionFind(dummy + 1) uf = UnionFind(dummy + 1) for r in range(n_rows): for c in range(n_cols): if board[r][c] == 'O': i = r * n_cols + c # Connect border 'O' cells to dummy node. if r in (0, n_rows - 1) or c in (0, n_cols - 1): uf.union(i, dummy) else: # connect interior 'O' cells to neighbor 'O' cells if board[r - 1][c] == 'O': uf.union(i, i - n_cols) if board[r + 1][c] == 'O': uf.union(i, i + n_cols) if board[r][c - 1] == 'O': uf.union(i, i - 1) if board[r][c + 1] == 'O': uf.union(i, i + 1) for r in range(1, n_rows - 1): for c in range(1, n_cols - 1): if board[r][c] == 'O' and not uf.connected( r * n_cols + c, dummy): board[r][c] = 'X'
#! /usr/bin/python import sys from union_find import UnionFind if __name__ == '__main__': num_of_node = int(sys.stdin.readline()) edge_coll = [] for line in sys.stdin.readlines(): a, b, cost = line.split() edge_coll.append((int(cost), int(a) - 1, int(b) - 1)) edge_coll.sort(lambda x, y: x[0] - y[0]) union_find = UnionFind(num_of_node) expected_cluster_size = 4 for edge in edge_coll: cost, x, y = edge union_find.union(x, y) if union_find.num_of_cluster == expected_cluster_size: break max_clustering_map = {} #a print union_find.coll for edge in edge_coll: cost, x, y = edge if not union_find.connected(x, y): print cost break
from union_find import UnionFind if __name__ == "__main__": connections = [[4, 3], [3, 8], [6, 5], [9, 4], [2, 1], [8, 9], \ [5, 0], [7, 2], [6, 1], [1, 0], [6, 7]] nodes = set([node for links in connections for node in links]) union_find = UnionFind(nodes) for link in connections: p = link[0] q = link[1] if not union_find.connected(p, q): union_find.union(p, q) print str(p) + " " + str(q) print union_find.connected(0, 1) # (0, 8)
class PercolationGrid: ''' - percolation system model: n-by-n grid of cells. - each cell (r, c) : unblocked or blocked. - top left cell: cell(1, 1) - bottom right cell: cell(n, n) - A system percolates if: - we fill all unblocked cells connected to the top row and that process fills some open cell on the bottom row. ''' def __init__(self, n): '''Creates an n x n grid, with all cells blocked initially''' self.n = n self.cell_count = n * n self.unblocked_cell_count = 0 # Each cell has a corresponding node number IE # cell at row = 1, column = 1 -> node = 1 # cell at row = 1, column = 2 -> node = 2 # cell at row = 2, column = n -> node = 2 * n # cell at row = n, column = n -> node = n * n # - node 0 - connects to open all top row cells # - node n*n + 1 - connects to all open bottom row cells self.node_count = self.cell_count + 2 # Initializes a Tree object that: # - Efficiently connects two cells # - Efficiently finds if two cells are connected # Each cell represented as a node (number) self.UFTree = UnionFind(self.node_count) # List index: node(number) # Corresponding value: unblocked(True), blocked(False) # Initially, all nodes (cells) are blocked self.is_node_unblocked = [False for i in range(self.node_count)] # Unblock virtual top and virtual bottom nodes # So nodes can connect with it self.is_node_unblocked[0] = True self.is_node_unblocked[-1] = True def unblocked_cells_fraction(self): '''Number of unblocked cells over total number of cells''' return self.unblocked_cell_count / self.cell_count def does_percolate(self): ''' Is there a path from the cells in the top row to the cells in the bottom row? ''' return self.UFTree.connected(0, self.cell_count + 1) def is_cell_unblocked(self, r, c): '''Is cell unblocked?''' i = self.node_given_cell(r, c) return self.is_node_unblocked[i] def unblock_cell(self, r, c): ''' Unblock cell at row r and column c, if blocked. Connect this cell to all open neighboring nodes ''' # make sure that values given makes sense self.check_scope(r, c) # get node representation of cell current = self.node_given_cell(r, c) # don't do anything, if the given cell's unblocked if self.is_node_unblocked[current] is True: return # mark node as unblocked self.is_node_unblocked[current] = True # remember to update the running unblocked cell total self.unblocked_cell_count += 1 # connect the node to its neighbors self.connect_unblocked_neighbors(current, r, c) def connect_unblocked_neighbors(self, current, r, c): ''' Given: node number `current` at grid cell location (`r`, `c`) Connect the node to its left, right, top, and bottom neighbors given they exist and are not blocked ''' # connect node current to its left and right neighbors if c != 1: left = self.node_given_cell(r, c - 1) self.connect_nodes(current, left) if c != self.n: right = self.node_given_cell(r, c + 1) self.connect_nodes(current, right) # connect node to its top and bottom neighbors # if the node is at the most top row connect it to # the "virtual top node" # if the node is at the most bottom row, connect it to # the "virtual bottom node" top, bottom = 0, -1 if r != 1: top = self.node_given_cell(r - 1, c) if r != self.n: bottom = self.node_given_cell(r + 1, c) self.connect_nodes(current, top) self.connect_nodes(current, bottom) def connect_nodes(self, i, j): '''Connect two node i, j if both nodes are unblocked''' if self.is_node_unblocked[i] and self.is_node_unblocked[j]: self.UFTree.union(i, j) def node_given_cell(self, r, c): '''Node representing cell located at row r, column c''' return (r - 1) * self.n + c def cell_given_node(self, i): '''Location tuple, (row, column) representing cell that corresponds to node i''' r, c = divmod(i - 1, self.n) return r + 1, c + 1 def are_connected(self, m, n): '''Are cells m(r1, c1) and n(r2, c2) connected?''' (x, y), (i, j) = m, n a, b = self.node_given_cell(x, y), self.node_given_cell(i, j) return self.UFTree.connected(a, b) def check_scope(self, r, c): ''' Assert error if either row or column is incorrect type or not within range ''' out_of_scope_message = "%r ...Not between 0 and " + str(self.n) c_message = "c: " + out_of_scope_message r_message = "r: " + out_of_scope_message assert type(r) is int, "r: %r ...Not an integer" % r assert type(c) is int, "c: %r ...Not an integer" % c assert 0 < c <= self.n, c_message % r assert 0 < r <= self.n, r_message % r
uf = UnionFind(5) assert uf.nsets == 5 uf.union(0, 1) assert uf.nsets == 4 uf.union(2, 3) assert uf.nsets == 3 uf.union(4, 3) assert uf.nsets == 2 assert uf.connected(0, 3) == False assert uf.connected(4, 3) == True p_expected = [1, 1, 3, 3, 3] size_expected = [2, 2, 3, 3, 3] for i in range(5): assert uf.find(i) == p_expected[i] assert uf.set_size(i) == size_expected[i] uf.union(0, 3) assert uf.nsets == 1 p_expected = [3] * 5 size_expected = [5] * 5 for i in range(5): assert uf.find(i) == p_expected[i]
if random.choices([False, True], weights=[100 * (1 - P), 100 * P])[0]: mat[i] = " " # First ROW if X > i >= 0: uf.union(i, X * Y) elif X * (Y - 1) <= i < X * Y: # print(i) uf.union(i, X * Y + 1) # Above Row if i - X > 0 and mat[i - X] == " ": uf.union(i, i - X) else: # Above Row if i - X > 0 and mat[i - X] == " ": uf.union(i, i - X ) # Left Block if i - 1 > 0 and int((i - 1) / X) == int(i / X) and mat[i - 1] == " ": uf.union(i, i - 1) print("\n".join(["".join(mat[i * X: i * X + X]) for i in range(Y)])) print(uf.connected(X*Y, X * Y + 1, display=True)) print() # while (k := input()) != "n": # print(uf.connected(0, int(k) + 1, display=True)) # input() # for x in range(X): # for y in range(Y): # print(mat[x][y], end="") # print()