5

I'm fairly new to Python and wanted to implement some graph algorithms just for practice. So I implemented Depth First Search both iterative and recursively and they work fine. But in the examples I found in the web I saw different approaches for recursive DFS that seems to have have different memory usages. The question is which one is better overall.

I think they have same time complexity but I don't know how to measure memory usage. And one of them should be in accord to python programming conventions.

from collections import deque


class Graph:

    def __init__(self, n, vlist, directed=False):
        self.size = n
        self.adjlist = [[0 for _ in range(n)] for __ in range(n)]
        for x in vlist:
            self.adjlist[x[0]][x[1]] = 1
        if not directed:
            for x in vlist:
                self.adjlist[x[1]][x[0]] = 1

    # iterative DFS
    def idfs(self, start=0):
        discovered = [False for _ in range(self.size)]
        stk = deque()
        stk.append(start)
        while len(stk) > 0:
            v = stk.pop()
            if not discovered[v]:
                discovered[v] = True
                print(f"Discovered: {v}")
                for x in range(self.size):
                    if self.adjlist[v][x] == 1:
                        # print(f"x{x}")
                        stk.append(x)

    # recursive dfs style 1 with 2 methods
    def rdfs1(self, start=0):
        self.discovered = [False for _ in range(self.size)]
        self.rdfs1util()
        print("end")
        del(self.discovered)

    def rdfs1util(self, v=0):
        self.discovered[v] = True
        print(f"Discovered: {v}")
        for x in range(self.size):
            if self.adjlist[v][x] == 1:
                if not self.discovered[x]:
                    self.rdfs1util(x)

    # recursive dfs style 2
    def rdfs2(self, start=0):
        discovered = [False for _ in range(self.size)]

        def rdfsi(v):
            discovered[v] = True
            print(f"Discovered: {v}")
            for x in range(self.size):
                if self.adjlist[v][x] == 1:
                    if not discovered[x]:
                        rdfsi(x)
        rdfsi(start)
        print("end")

    # recursive dfs style 3
    def rdfs3(self, discovered, start=0):
        discovered[start] = True
        print(f"Discovered: {start}")
        for x in range(self.size):
            if self.adjlist[start][x] == 1:
                if not discovered[x]:
                    self.rdfs3(discovered, x)

    def bfs(self):
        pass

    def spf(self, vid):
        pass

    def view(self):
        for i in range(self.size):
            x = [j for j in range(self.size) if self.adjlist[i][j] > 0]
            if x is not None:
                print(str(i)+" : "+" ".join(map(str, x)))


######################
v = 8
e = [(0, 5), (5, 3),
     (0, 1), (1, 2), (6, 2), (0, 3), (2, 6), (3, 5), (3, 6), (4, 7)]
graph1 = Graph(v, e)
graph1.view()

print("iterative dfs:")
graph1.idfs()
print("__")
print("recursive dfs1:")
graph1.rdfs1()
print("__")
print("recursive dfs2:")
graph1.rdfs2()
print("__")
print("recursive dfs3:")
visits = [False for _ in range(v)]
graph1.rdfs3(visits)
optimum
  • 51
  • 3
  • 4
    You'll need to define "better overall". If you just mean "faster", have you tried benchmarking them? – glibdud Oct 16 '19 at 12:22
  • @glibdud I think they have same time complexity but I don't know how to measure memory usage. And one of them should be in accord to python programming conventions. – optimum Oct 16 '19 at 12:52
  • You'll want to add that information to the body of the question. – glibdud Oct 16 '19 at 12:54
  • Memory complexity is related to the elements your algorithm has to store over runtime. Read e.g. this thorough explanation: https://stackoverflow.com/a/20794101/3757672 – Markus Oct 16 '19 at 13:31

0 Answers0