I coded a solution for DFS non-recursive, but i can't modify it to make a topological sort:
def dfs(graph,start):
path = []
stack = [start]
while stack != []:
v = stack.pop()
if v not in path: path.append(v)
for w in reversed(graph[v]):
if w not in path and not w in stack:
stack.append(w)
return path
Any ideas how to modify it?
With the recursive version i can easy have the sorting:
def dfs_rec(graph,start,path):
path = path + [start]
for edge in graph[start]:
if edge not in path:
path = dfs_rec(graph, edge,path)
print start
return path
Input:
>>> graph = {
1: [2, 3],
2: [4, 5, 6],
3: [4,6],
4: [5,6],
5: [6],
6: []
}
>>> dfs_rec(graph,1,[])
6
5
4
2
3
1
[1, 2, 4, 5, 6, 3]
>>> dfs(graph,1)
[1, 2, 4, 5, 6, 3]
>>> graph = {
1: [3],
3: [5,6],
5: [4],
4: [7],
7: [],
6: []
}
>>> print dfs_rec(graph,1,[])
7
4
5
6
3
1
[1, 3, 5, 4, 7, 6]
>>> print dfs(graph,1)
[1, 3, 5, 4, 7, 6]
so i need to get this ordering in the non-recursive also.
Non-recursive solution:
I think that this also could be the solution, mark me if i am wrong.
def dfs(graph,start):
path = []
stack = [start]
label = len(graph)
result = {}
while stack != []:
#this for loop could be done in other ways also
for element in stack:
if element not in result:
result[element] = label
label = label - 1
v = stack.pop()
if v not in path: path.append(v)
for w in reversed(graph[v]):
if w not in path and not w in stack:
stack.append(w)
result = {v:k for k, v in result.items()}
return path,result
Input:
graph = { 1: [3], 3:[5,6] , 5:[4] , 4:[7], 7:[],6:[]}
print dfs(graph,1)
Output:
([1, 3, 5, 4, 7, 6], {1: 7, 2: 4, 3: 5, 4: 6, 5: 3, 6: 1})
1
/
3
/\
5 6
/
4
/
7
FWIW, here is some code I worked up for a non-recursive topological sort.
from collections import defaultdict, namedtuple
from itertools import islice
Results = namedtuple('Results', ['sorted', 'cyclic'])
def topological_sort(dependency_pairs):
'Sort values subject to dependency constraints'
num_heads = defaultdict(int) # num arrows pointing in
tails = defaultdict(list) # list of arrows going out
heads = [] # unique list of heads in order first seen
for h, t in dependency_pairs:
num_heads[t] += 1
if h in tails:
tails[h].append(t)
else:
tails[h] = [t]
heads.append(h)
ordered = [h for h in heads if h not in num_heads]
for h in ordered:
for t in tails[h]:
num_heads[t] -= 1
if not num_heads[t]:
ordered.append(t)
cyclic = [n for n, heads in num_heads.items() if heads]
return Results(ordered, cyclic)
if __name__ == '__main__':
print( topological_sort('aa'.split()) )
print( topological_sort('ah bg cf ch di ed fb fg hd he ib'.split()) )
from collections import defaultdict, deque
class Graph:
def __init__(self, directed=False, nodes=None, edges=None):
self.graph = defaultdict(list)
self.directed = directed
self.add_nodes(nodes)
self.add_edges(edges)
#property
def nodes(self):
if not self.directed:
return list(self.graph.keys())
elif self.directed:
nodes = set()
nodes.update(self.graph.keys())
for node in self.graph.keys():
for neighbor in self.graph[node]:
nodes.add(neighbor)
return list(nodes)
def add_node(self, node):
if node not in self.nodes:
self.graph[node] = list()
def add_nodes(self, nodes):
if nodes is None:
return None
for node in nodes:
self.add_node(node)
#property
def edges(self):
edges = list()
for source, neighbors in self.graph.items():
for neighbor in neighbors:
edges.append((source, neighbor))
return edges
def add_edge(self, edge):
node1, node2 = edge
self.graph[node1].append(node2)
if not self.directed:
self.graph[node2].append(node1)
def add_edges(self, edges):
if edges is None:
return None
for edge in edges:
self.add_edge(edge)
def topological_util(self, node, visited, label):
visited[node] = True
for edge in self.graph[node]:
if not visited[edge]:
self.topological_util(edge, visited, label)
label.appendleft(node)
def topological_sort(self):
visited = dict.fromkeys(self.nodes, False)
# store all nodes in topological order, the index is the position
label = deque()
for node in self.nodes:
if not visited[node]:
self.topological_util(node, visited, label)
return label
#this algorithm gives the logic of topological sorting..if u want to run this
#give adjacency mat of your choice and this algorithm works on graph elements ranging from 0 to n
a=[[0,0,1,0,0,0],[0,0,1,0,0,0],[0,0,0,1,1,0],[0,0,0,0,1,0],[0,0,0,0,0,0],[0,0,1,0,0,0]]
vis=[0 for i in range(0,len(a))]
s=[]
orderstack=[]#stores the reverse order of topological sorted elements
def dfs_for_topological_sorting(a,vis,i):
vis[i]=1
x=0
for j in range(0,len(a[0])):
if(a[i][j]==1 and vis[j]==0):
x=1
s.append(j)
#print(s)
dfs_for_topological_sorting(a,vis,j)
if(x==0 and len(s)!=0):
orderstack.append(s[len(s)-1])
if(len(s)>0):
dfs_for_topological_sorting(a,vis,s.pop())
for i in range(0,len(a)):
if(i not in orderstack):
s.append(i)
dfs_for_topological_sorting(a,vis,i)
print(orderstack[len(orderstack)-1::-1])
from collections import defaultdict # importing defaultdict
def topological_sort(graph,b,a): # defining function
T = []
visited = []
in_degree = []
for i in range(a+1):
in_degree.append(0) # initialising the degree of each vertex =0
visited.append(0) # initialising all the vertics unvisited
for i in range(1,a+1):
for j in graph[i]:
in_degree[j] = in_degree[j] + 1 # now assigning and incrementing
Queue=[] # the degree of each vertex acc.
for i in range(1,a+1):
if in_degree[i]==0:
Queue.append(i) # appending those vertices which have zero
visited[i] = 1 # degree and making it as visited
while Queue :
vertex = Queue.pop(Queue.index(min(Queue))) # popping each element in
T.append(vertex) # lexicographical order and
for j in graph[vertex]: # appending to T
if visited[j]==0:
in_degree[j] = in_degree[j] - 1
if in_degree[j] == 0:
Queue.append(j) #according to each popped vertex
visited[j] = 1 #as key in graph check whether
return T #in list corresponding to key
# as value,is it not visited and
#decreasing its value when it
#becomes zero,append it to queue
#and mark it as visited
graph=defaultdict(list)
a,b=list(map(int,input().split())) #a=no. of vertices
for i in range(b): #b=no. of edges
p,q=list(map(int,input().split()))
graph[p].append(q) # we take input in graph as DAG
ss=topological_sort(graph,b,a) # calling function
for i in ss:
print(i,end=" ")
'''Input
5 6
1 2
1 3
2 3
2 4
3 4
3 5
Your Code's Output
1 2 3 4 5
Expected Correct Output
1 2 3 4 5 '''
Related
I am trying to get the following code working. After every for ends heappop gives me an integer instead of Vertex. In addition when I got it working, with changing the Vertex in the priority queue with integer. I have wrong result. Please help.
Thanks in advance
import heapq
class Vertex:
def __init__(self, id):
self.id = id
self.adjList = []
self.adjWeights = []
def shortestPath(vertices, N, source, destination):
distTo = [float('inf') for _ in range(N+1)]
edgeTo = [float('inf') for _ in range(N+1)]
# Set initial distance from source
# to the highest value
distTo[source] = 0.0
edgeTo[source] = float('inf')
pq = [vertices[source]]
heapq.heapify(pq)
while True:
closest = heapq.heappop(pq)
for i in range(len(closest.adjList)):
# Checks if the edges are decreasing and
# whether the current directed edge will
# create a shorter path
if closest.adjWeights[i] < edgeTo[closest.id] and distTo[closest.id] + closest.adjWeights[i] < distTo[closest.adjList[i]]:
edgeTo[closest.adjList[i]] = closest.adjWeights[i]
distTo[closest.adjList[i]] = closest.adjWeights[i] + distTo[closest.id];
heapq.heappush(pq, closest.adjList[i])
print(distTo)
print(distTo[destination])
def main ()
N = 6
M = 9
'''
edges = {{0, 2, 1.1}, {0, 4, 2}, {0, 5, 3.3}, {1, 4, 2.7},
{2, 3, 2}, {2, 4, 1.1}, {3, 1, 2.3}, {4, 5, 2.4}, {5, 1, 3}}
'''
# Create an array of vertices
vertices = [Vertex(i) for i in range(0, N)]
i=0
vertices[0].adjList.append(2)
vertices[0].adjWeights.append(1.1)
vertices[0].adjList.append(4)
vertices[0].adjWeights.append(2.0)
vertices[0].adjList.append(5)
vertices[0].adjWeights.append(3.3)
vertices[1].adjList.append(4)
vertices[1].adjWeights.append(2.7)
vertices[2].adjList.append(3)
vertices[2].adjWeights.append(2.0)
vertices[2].adjList.append(4)
vertices[2].adjWeights.append(1.1)
vertices[3].adjList.append(1)
vertices[3].adjWeights.append(2.3)
vertices[4].adjList.append(5)
vertices[4].adjWeights.append(2.4)
vertices[5].adjList.append(1)
vertices[5].adjWeights.append(3.0)
# Source and destination vertices
src = 0
target = 1
print(shortestPath(vertices, N, src, target))
I am not able to get that what's wrong with the below code it should show me the output as
0 : [1, 4]
1 : [0, 2, 4, 3]
2 : [1, 3]
3 : [2, 4, 1]
4 : [3, 0, 1]
But it showing error as below :
Traceback (most recent call last):
File "C:\Users\sanja\PycharmProjects\DSA\pythonProject\main.py", line 19, in <module>
g1 = Graph(num_nodes,edges)
File "C:\Users\sanja\PycharmProjects\DSA\pythonProject\main.py", line 10, in __init__
self.data[v1].append(v2)
IndexError: list index out of range
And the program look like below :
edges = list(tuple(map(int, input("enter edges: ").split())) for r in range(int(input("enter no of nodes: "))))
num_nodes =len(edges)
class Graph:
def __init__(self, num_nodes, edges):
self.data = [[] for _ in range(num_nodes)]
for v1, v2 in edges:
self.data[v1].append(v2)
self.data[v2].append(v1)
def __repr__(self):
return "\n".join(["{} : {}".format(i, neighbors) for (i, neighbors) in enumerate(self.data)])
def __str__(self):
return repr(self)
g1 = Graph(num_nodes,edges)
print(g1)
Here in the above program i am taking num of nodes as well as edges an showing the proper format as shown above in the 1st section but getting error.
The program below is doing same work that i need to do the only thing is i need to implement with user input means at run time the inputs to be given .
num_nodes1 = 5
edges1 = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (1, 4), (1, 3)]
num_nodes1, len(edges1)
class Graph:
def __init__(self, num_nodes, edges):
self.data = [[] for _ in range(num_nodes)]
for v1, v2 in edges:
self.data[v1].append(v2)
self.data[v2].append(v1)
def __repr__(self):
return "\n".join(["{} : {}".format(i, neighbors) for (i, neighbors) in enumerate(self.data)])
def __str__(self):
return repr(self)
g1 = Graph(num_nodes1, edges1)
print(g1)
Issues: There are a couple of issues in the code.
The edges specified can be more than the number of nodes so you will need to know how many edges to read in (e.g. 7 edges for 5 nodes)
You need unique edges in self.data for each node. You will need to remove duplicates.
Solution: Please find the code for the __init__ function with the corrections below.
def __init__(self, num_nodes, edges):
self.data = [[] for _ in range(num_nodes)]
# link nodes
for v1, v2 in edges:
for edge in edges:
self.data[v1].append(v2)
self.data[v2].append(v1)
# remove duplicates
self.data = [list(set(entry)) for entry in self.data]
You would invoke the above function as follows:
num_nodes = int(input("enter no of nodes: "))
num_edges = int(input("how many edges?: "))
edges = [tuple(map(int, input("enter edge: ").split())) for r in range(num_edges)]
g1 = Graph(num_nodes, edges)
print(g1)
Output: Running the above with your sample data will produce the following when you run print(self.data):
0 : [1, 4]
1 : [0, 2, 3, 4]
2 : [1, 3]
3 : [1, 2, 4]
4 : [0, 1, 3]
You are given a sequence of positive ints where every element appears three times, except one that appears only once (let's call it x) and one that appears only twice (let's call it y).
Your task is to find x * x * y.
e.g.
arr = [1,1,1,2,2,2,3,3,4] -> 4 x 4 x 3
I have written some code below. I have a question regarding the final part of the code- so after the completion of the loop, there should be one integer left in seen_once and one integer left in seen_twice, but how do I then multiply these numbers, as they are now sitting in a set()?
def Missing_Values(arr):
seen_once = set()
seen_twice = set()
seen_thrice = set()
for i in arr:
if i not in seen_once or seen_twice or seen_thrice:
seen_once.add(i)
elif i in seen_once:
seen_twice.add(i)
seen_once.remove(i)
elif i in seen_twice:
seen_thrice.add(i)
seen_twice.remove(i)
return seen_once*seen_once*seen_twice
Missing_Values(arr)
One way would be to pop the values.
x = seen_once.pop()
y = seen_twice.pop()
return x * x * y
You can use counter from collections for better performance that also improves readability.
Following is the code:
from collections import Counter
arr = [1, 1, 1, 2, 2, 2, 3, 3, 4]
d = Counter(arr)
ans = 1
for x, cnt in d.items():
if cnt == 2:
ans *= x
elif cnt == 1:
ans *= (x * x)
print(ans)
You can also use list comprehension as a generator as follows:
from collections import Counter
arr = [1, 1, 1, 2, 4, 4, 3, 3, 4]
d = Counter(arr)
x, y = (x**(3 - cnt) for x, cnt in d.items() if(cnt <= 2))
print(x*y)
Counter Explanation:
Counter returns a dictionary where array item as a Key and item frequency as a value. For example, if array arr = [1, 1, 1, 2, 2, 2, 3, 3, 4] then counter provide following dictionary:
d = {
1: 3,
2: 3,
3: 2,
4: 1
}
You have a bug in your code, this is a working piece:
def Missing_Values(arr):
seen_once = set()
seen_twice = set()
seen_thrice = set()
for i in arr:
if i not in seen_once and i not in seen_twice and i not in seen_thrice: # Note this line!
seen_once.add(i)
elif i in seen_once:
seen_twice.add(i)
seen_once.remove(i)
elif i in seen_twice:
seen_thrice.add(i)
seen_twice.remove(i)
return next(iter(seen_once))*next(iter(seen_once))*next(iter(seen_twice))
arr = [1,1,1,2,2,2,3,3,4]
print(Missing_Values(arr))
I'm trying to find the distance between the root and the depth of the node that is being traversed, for example if I had a the following adjancency list representing the tree { 1: [2, 3], 2: [4], 3: [5]} an associated list like the following would be created [0, 1, 1, 2, 2] denoting the level of each node.
I have the following code and can't see where I'm meant to add counting functionality etc, ideally this would deal with cross and back edges as well
def bfs(graph, root):
seen, queue = set([root]), collections.deque([root])
visit_order = []
while queue:
vertex = queue.popleft()
visit_order.append(vertex)
for node in graph[vertex]:
if node not in seen:
seen.add(node)
queue.append(node)
print(visit_order)
Instead of queuing just the nodes, you can queue the nodes and their levels as tuples, and when you enqueue a node it's always coupled with the current level plus one, so that when you dequeue a node and append the node to visit_order you also get the level of the node from the tuple:
import collections
def bfs(graph, root):
seen, queue = {root}, collections.deque([(root, 0)])
visit_order = []
levels = []
while queue:
vertex, level = queue.popleft()
visit_order.append(vertex)
levels.append(level)
for node in graph.get(vertex, []):
if node not in seen:
seen.add(node)
queue.append((node, level + 1))
print(visit_order)
print(levels)
so that:
bfs({ 1: [2, 3], 2: [4], 3: [5]}, 1)
would output:
[1, 2, 3, 4, 5]
[0, 1, 1, 2, 2]
You can use a dictionary to keep track of the current depths:
from collections import deque
d = {1: [2, 3], 2: [4], 3: [5]}
def bfs(graph, root = 1):
queue, seen, depths = deque([root]), [], {root:0}
while queue:
val = queue.popleft()
depths.update({i:depths[val] +1 for i in graph.get(val, [])})
seen.append(val)
queue.extend([i for i in graph.get(val, []) if i not in seen])
yield seen, depths
[(_all, _depths)] = bfs(d)
print([_depths[i] for i in _all])
Output:
[0, 1, 1, 2, 2]
The logic is simpler, however, when using a class, as a depth-first traversal can be applied:
class Tree:
def __init__(self, _start):
self.__dict__ = {'head':_start, 'data':[Tree(i) for i in d.get(_start, [])]}
def __contains__(self, _val):
if self.head != _val and not self.data:
return False
return True if self.head == _val else any(_val in i for i in self.data)
def get_depth(self, _val):
if self.head == _val:
return 0
return 1+[i for i in self.data if _val in i][0].get_depth(_val)
t = Tree(1)
print([t.get_depth(i) for i in set([i for a, b in d.items() for i in [a, *b]])])
Output:
[0, 1, 1, 2, 2]
I'm using this implementation for DFS to get the nodes of the root I specify in the function, but for the adjLists1 i'm using I a error when I call 2 as the root. . 1 and 3 return their child nodes but 2 doesn't. Not sure what I'm doing wrong here.
The error I get is:
Traceback (most recent call last):
2 5 File "DFS2.py", line 42, in <module>
dfs_iterative(adjLists1, 2)
File "DFS2.py", line 17, in dfs_iterative
if(not visited[w]):
IndexError: list index out of range
Program:
def dfs_iterative(adjLists, s):
stack = []
stack.append(s)
n = len(adjLists)
visited = []
for i in range(0,n):
visited.append(False)
while(len(stack)>0):
v = stack.pop()
if(not visited[v]):
visited[v] = True
print(v, " ", end='')
stack_aux = []
for w in adjLists[v]:
if(not visited[w]):
stack_aux.append(w)
while(len(stack_aux)>0):
stack.append(stack_aux.pop())
# ------------------------------------------------------------------
# 0 1 2 3 4 5 6 7 8
adjLists1 = [ [1,2,3], [4], [5,6], [7,8], [], [9,10,11], [12,13,14], [], [] ]
dfs_iterative(adjLists1, 2)
You can solve the problem without the blank child nodes, by using the maximum value in the list and guard against indexing appropriately:
Your code can also be simplified a bit:
import itertools as it
def dfs_iterative(adjLists, s):
stack = [s]
n = len(adjLists)
visited = [False] * (max(it.chain(*adjLists))+1)
while stack:
v = stack.pop()
if visited[v]:
continue
visited[v] = True
print(v, " ", end='')
if v >= n: # Guard against trying to index with v
continue
for w in adjLists[v]:
stack.append(w)
>>> adjLists1 = [ [1,2,3], [4], [5,6], [7,8], [], [9,10,11], [12,13,14], [], []]
>>> dfs_iterative(adjLists1, 2)
2 6 14 13 12 5 11 10 9
Note: You never index 0 so [1, 2, 3] is never explored.