Python/graphs/basic_graphs.py

434 lines
12 KiB
Python
Raw Normal View History

from collections import deque
def _input(message):
return input(message).strip().split(" ")
def initialize_unweighted_directed_graph(
node_count: int, edge_count: int
) -> dict[int, list[int]]:
"""
Example:
Edge 1: <node1> <node2> 1 2
Edge 2: <node1> <node2> 3 2
Edge 3: <node1> <node2> 2 4
>>> import io, sys ; sys.stdin = io.StringIO(chr(10).join(['1 2','3 2', '2 4'])) # input
>>> initialize_unweighted_directed_graph(4, 3)
Edge 1: <node1> <node2> Edge 2: <node1> <node2> Edge 3: <node1> <node2> {1: [2], 2: [4], 3: [2], 4: []}
"""
graph: dict[int, list[int]] = {}
for i in range(node_count):
graph[i + 1] = []
for e in range(edge_count):
x, y = (int(i) for i in _input(f"Edge {e + 1}: <node1> <node2> "))
graph[x].append(y)
return graph
def initialize_unweighted_undirected_graph(
node_count: int, edge_count: int
) -> dict[int, list[int]]:
"""
Example:
Edge 1: <node1> <node2> 1 2
Edge 2: <node1> <node2> 3 2
Edge 3: <node1> <node2> 2 4
>>> import io, sys ; sys.stdin = io.StringIO(chr(10).join(['1 2','3 2', '2 4'])) # input
>>> initialize_unweighted_undirected_graph(4, 3)
Edge 1: <node1> <node2> Edge 2: <node1> <node2> Edge 3: <node1> <node2> {1: [2], 2: [1, 3, 4], 3: [2], 4: [2]}
"""
graph: dict[int, list[int]] = {}
for i in range(node_count):
graph[i + 1] = []
for e in range(edge_count):
x, y = (int(i) for i in _input(f"Edge {e + 1}: <node1> <node2> "))
graph[x].append(y)
graph[y].append(x)
return graph
def initialize_weighted_undirected_graph(
node_count: int, edge_count: int
) -> dict[int, list[tuple[int, int]]]:
"""
Example:
Edge 1: <node1> <node2> <weight> 1 2 1
Edge 2: <node1> <node2> <weight> 3 2 6
Edge 3: <node1> <node2> <weight> 2 4 10
Edge 4: <node1> <node2> <weight> 4 1 7
Edge 5: <node1> <node2> <weight> 4 3 12
>>> import io, sys ; sys.stdin = io.StringIO(chr(10).join(['1 2 1','3 2 6', '2 4 10', '4 1 7', '4 3 12'])) # input
>>> initialize_weighted_undirected_graph(4, 5)
Edge 1: <node1> <node2> <weight> Edge 2: <node1> <node2> <weight> Edge 3: <node1> <node2> <weight> Edge 4: <node1> <node2> <weight> Edge 5: <node1> <node2> <weight> {1: [(2, 1), (4, 7)], 2: [(1, 1), (3, 6), (4, 10)], 3: [(2, 6), (4, 12)], 4: [(2, 10), (1, 7), (3, 12)]}
"""
graph: dict[int, list[tuple[int, int]]] = {}
for i in range(node_count):
graph[i + 1] = []
for e in range(edge_count):
x, y, w = (int(i) for i in _input(f"Edge {e + 1}: <node1> <node2> <weight> "))
graph[x].append((y, w))
graph[y].append((x, w))
return graph
if __name__ == "__main__":
n, m = (int(i) for i in _input("Number of nodes and edges: "))
graph_choice = int(
_input(
"Press 1 or 2 or 3 \n"
"1. Unweighted directed \n"
"2. Unweighted undirected \n"
"3. Weighted undirected \n"
)[0]
)
g = {
1: initialize_unweighted_directed_graph,
2: initialize_unweighted_undirected_graph,
3: initialize_weighted_undirected_graph,
}[graph_choice](n, m)
2018-10-19 12:48:28 +00:00
"""
--------------------------------------------------------------------------------
Depth First Search.
Args : G - Dictionary of edges
s - Starting Node
Vars : vis - Set of visited nodes
S - Traversal Stack
--------------------------------------------------------------------------------
"""
def dfs(g, s):
"""
>>> dfs({1: [2, 3], 2: [4, 5], 3: [], 4: [], 5: []}, 1)
1
2
4
5
3
"""
vis, _s = {s}, [s]
2018-10-19 12:48:28 +00:00
print(s)
while _s:
2018-10-19 12:48:28 +00:00
flag = 0
for i in g[_s[-1]]:
2018-10-19 12:48:28 +00:00
if i not in vis:
_s.append(i)
2018-10-19 12:48:28 +00:00
vis.add(i)
flag = 1
print(i)
break
if not flag:
_s.pop()
2018-10-19 12:48:28 +00:00
"""
--------------------------------------------------------------------------------
Breadth First Search.
Args : G - Dictionary of edges
s - Starting Node
Vars : vis - Set of visited nodes
Q - Traversal Stack
2018-10-19 12:48:28 +00:00
--------------------------------------------------------------------------------
"""
def bfs(g, s):
"""
>>> bfs({1: [2, 3], 2: [4, 5], 3: [6, 7], 4: [], 5: [8], 6: [], 7: [], 8: []}, 1)
1
2
3
4
5
6
7
8
"""
vis, q = {s}, deque([s])
2018-10-19 12:48:28 +00:00
print(s)
while q:
u = q.popleft()
for v in g[u]:
2018-10-19 12:48:28 +00:00
if v not in vis:
vis.add(v)
q.append(v)
2018-10-19 12:48:28 +00:00
print(v)
"""
--------------------------------------------------------------------------------
Dijkstra's shortest path Algorithm
Args : G - Dictionary of edges
s - Starting Node
Vars : dist - Dictionary storing shortest distance from s to every other node
known - Set of knows nodes
path - Preceding node in path
--------------------------------------------------------------------------------
"""
def dijk(g, s):
"""
dijk({1: [(2, 7), (3, 9), (6, 14)], 2: [(1, 7), (3, 10), (4, 15)], 3: [(1, 9), (2, 10), (4, 11), (6, 2)], 4: [(2, 15), (3, 11), (5, 6)], 5: [(4, 6), (6, 9)], 6: [(1, 14), (3, 2), (5, 9)]}, 1)
7
9
11
20
20
"""
2018-10-19 12:48:28 +00:00
dist, known, path = {s: 0}, set(), {s: 0}
while True:
if len(known) == len(g) - 1:
2018-10-19 12:48:28 +00:00
break
mini = 100000
for i in dist:
if i not in known and dist[i] < mini:
mini = dist[i]
u = i
known.add(u)
for v in g[u]:
if v[0] not in known and dist[u] + v[1] < dist.get(v[0], 100000):
dist[v[0]] = dist[u] + v[1]
path[v[0]] = u
2018-10-19 12:48:28 +00:00
for i in dist:
if i != s:
print(dist[i])
"""
--------------------------------------------------------------------------------
Topological Sort
--------------------------------------------------------------------------------
"""
def topo(g, ind=None, q=None):
if q is None:
q = [1]
2018-10-19 12:48:28 +00:00
if ind is None:
ind = [0] * (len(g) + 1) # SInce oth Index is ignored
for u in g:
for v in g[u]:
2018-10-19 12:48:28 +00:00
ind[v] += 1
q = deque()
for i in g:
2018-10-19 12:48:28 +00:00
if ind[i] == 0:
q.append(i)
if len(q) == 0:
2018-10-19 12:48:28 +00:00
return
v = q.popleft()
2018-10-19 12:48:28 +00:00
print(v)
for w in g[v]:
2018-10-19 12:48:28 +00:00
ind[w] -= 1
if ind[w] == 0:
q.append(w)
topo(g, ind, q)
2018-10-19 12:48:28 +00:00
"""
--------------------------------------------------------------------------------
Reading an Adjacency matrix
--------------------------------------------------------------------------------
"""
def adjm():
r"""
Reading an Adjacency matrix
Parameters:
None
Returns:
tuple: A tuple containing a list of edges and number of edges
Example:
>>> # Simulate user input for 3 nodes
>>> input_data = "4\n0 1 0 1\n1 0 1 0\n0 1 0 1\n1 0 1 0\n"
>>> import sys,io
>>> original_input = sys.stdin
>>> sys.stdin = io.StringIO(input_data) # Redirect stdin for testing
>>> adjm()
([(0, 1, 0, 1), (1, 0, 1, 0), (0, 1, 0, 1), (1, 0, 1, 0)], 4)
>>> sys.stdin = original_input # Restore original stdin
"""
n = int(input().strip())
a = []
Add flake8 pluin flake8 bugbear to pre-commit (#7132) * ci(pre-commit): Add ``flake8-builtins`` additional dependency to ``pre-commit`` (#7104) * refactor: Fix ``flake8-builtins`` (#7104) * fix(lru_cache): Fix naming conventions in docstrings (#7104) * ci(pre-commit): Order additional dependencies alphabetically (#7104) * fix(lfu_cache): Correct function name in docstring (#7104) * Update strings/snake_case_to_camel_pascal_case.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update data_structures/stacks/next_greater_element.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update digital_image_processing/index_calculation.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update graphs/prim.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update hashes/djb2.py Co-authored-by: Christian Clauss <cclauss@me.com> * refactor: Rename `_builtin` to `builtin_` ( #7104) * fix: Rename all instances (#7104) * refactor: Update variable names (#7104) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ci: Create ``tox.ini`` and ignore ``A003`` (#7123) * revert: Remove function name changes (#7104) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename tox.ini to .flake8 * Update data_structures/heap/heap.py Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com> * refactor: Rename `next_` to `next_item` (#7104) * ci(pre-commit): Add `flake8` plugin `flake8-bugbear` (#7127) * refactor: Follow `flake8-bugbear` plugin (#7127) * fix: Correct `knapsack` code (#7127) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: Christian Clauss <cclauss@me.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2022-10-13 16:03:06 +00:00
for _ in range(n):
a.append(tuple(map(int, input().strip().split())))
2018-10-19 12:48:28 +00:00
return a, n
"""
--------------------------------------------------------------------------------
Floyd Warshall's algorithm
Args : G - Dictionary of edges
s - Starting Node
Vars : dist - Dictionary storing shortest distance from s to every other node
known - Set of knows nodes
path - Preceding node in path
--------------------------------------------------------------------------------
"""
def floy(a_and_n):
(a, n) = a_and_n
dist = list(a)
path = [[0] * n for i in range(n)]
for k in range(n):
for i in range(n):
for j in range(n):
2018-10-19 12:48:28 +00:00
if dist[i][j] > dist[i][k] + dist[k][j]:
dist[i][j] = dist[i][k] + dist[k][j]
path[i][k] = k
print(dist)
"""
--------------------------------------------------------------------------------
Prim's MST Algorithm
Args : G - Dictionary of edges
s - Starting Node
Vars : dist - Dictionary storing shortest distance from s to nearest node
known - Set of knows nodes
path - Preceding node in path
--------------------------------------------------------------------------------
"""
def prim(g, s):
2018-10-19 12:48:28 +00:00
dist, known, path = {s: 0}, set(), {s: 0}
while True:
if len(known) == len(g) - 1:
2018-10-19 12:48:28 +00:00
break
mini = 100000
for i in dist:
if i not in known and dist[i] < mini:
mini = dist[i]
u = i
known.add(u)
for v in g[u]:
if v[0] not in known and v[1] < dist.get(v[0], 100000):
dist[v[0]] = v[1]
path[v[0]] = u
return dist
2018-10-19 12:48:28 +00:00
"""
--------------------------------------------------------------------------------
Accepting Edge list
Vars : n - Number of nodes
m - Number of edges
Returns : l - Edge list
n - Number of Nodes
--------------------------------------------------------------------------------
"""
def edglist():
r"""
Get the edges and number of edges from the user
Parameters:
None
Returns:
tuple: A tuple containing a list of edges and number of edges
Example:
>>> # Simulate user input for 3 edges and 4 vertices: (1, 2), (2, 3), (3, 4)
>>> input_data = "4 3\n1 2\n2 3\n3 4\n"
>>> import sys,io
>>> original_input = sys.stdin
>>> sys.stdin = io.StringIO(input_data) # Redirect stdin for testing
>>> edglist()
([(1, 2), (2, 3), (3, 4)], 4)
>>> sys.stdin = original_input # Restore original stdin
"""
n, m = tuple(map(int, input().split(" ")))
edges = []
Add flake8 pluin flake8 bugbear to pre-commit (#7132) * ci(pre-commit): Add ``flake8-builtins`` additional dependency to ``pre-commit`` (#7104) * refactor: Fix ``flake8-builtins`` (#7104) * fix(lru_cache): Fix naming conventions in docstrings (#7104) * ci(pre-commit): Order additional dependencies alphabetically (#7104) * fix(lfu_cache): Correct function name in docstring (#7104) * Update strings/snake_case_to_camel_pascal_case.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update data_structures/stacks/next_greater_element.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update digital_image_processing/index_calculation.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update graphs/prim.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update hashes/djb2.py Co-authored-by: Christian Clauss <cclauss@me.com> * refactor: Rename `_builtin` to `builtin_` ( #7104) * fix: Rename all instances (#7104) * refactor: Update variable names (#7104) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ci: Create ``tox.ini`` and ignore ``A003`` (#7123) * revert: Remove function name changes (#7104) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Rename tox.ini to .flake8 * Update data_structures/heap/heap.py Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com> * refactor: Rename `next_` to `next_item` (#7104) * ci(pre-commit): Add `flake8` plugin `flake8-bugbear` (#7127) * refactor: Follow `flake8-bugbear` plugin (#7127) * fix: Correct `knapsack` code (#7127) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: Christian Clauss <cclauss@me.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Dhruv Manilawala <dhruvmanila@gmail.com>
2022-10-13 16:03:06 +00:00
for _ in range(m):
edges.append(tuple(map(int, input().split(" "))))
return edges, n
2018-10-19 12:48:28 +00:00
"""
--------------------------------------------------------------------------------
Kruskal's MST Algorithm
Args : E - Edge list
n - Number of Nodes
Vars : s - Set of all nodes as unique disjoint sets (initially)
--------------------------------------------------------------------------------
"""
def krusk(e_and_n):
"""
Sort edges on the basis of distance
"""
(e, n) = e_and_n
e.sort(reverse=True, key=lambda x: x[2])
s = [{i} for i in range(1, n + 1)]
2018-10-19 12:48:28 +00:00
while True:
if len(s) == 1:
break
print(s)
x = e.pop()
for i in range(len(s)):
2018-10-19 12:48:28 +00:00
if x[0] in s[i]:
break
for j in range(len(s)):
2018-10-19 12:48:28 +00:00
if x[1] in s[j]:
if i == j:
break
s[j].update(s[i])
s.pop(i)
break
def find_isolated_nodes(graph):
"""
Find the isolated node in the graph
Parameters:
graph (dict): A dictionary representing a graph.
Returns:
list: A list of isolated nodes.
Examples:
>>> graph1 = {1: [2, 3], 2: [1, 3], 3: [1, 2], 4: []}
>>> find_isolated_nodes(graph1)
[4]
>>> graph2 = {'A': ['B', 'C'], 'B': ['A'], 'C': ['A'], 'D': []}
>>> find_isolated_nodes(graph2)
['D']
>>> graph3 = {'X': [], 'Y': [], 'Z': []}
>>> find_isolated_nodes(graph3)
['X', 'Y', 'Z']
>>> graph4 = {1: [2, 3], 2: [1, 3], 3: [1, 2]}
>>> find_isolated_nodes(graph4)
[]
>>> graph5 = {}
>>> find_isolated_nodes(graph5)
[]
"""
isolated = []
for node in graph:
if not graph[node]:
isolated.append(node)
return isolated