From 9c94737d8b836db967b10fcf7462365e2fc591a7 Mon Sep 17 00:00:00 2001
From: Martin Mares <mj@ucw.cz>
Date: Wed, 29 Apr 2020 11:37:08 +0200
Subject: [PATCH] =?UTF-8?q?Grafy:=20P=C5=99=C3=ADklady=20na=20BFS=20a=20ab?=
 =?UTF-8?q?straktn=C3=AD=20reprezentaci=20grafu?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 09-grafy/abstraktni-matice.py  | 84 ++++++++++++++++++++++++++++++++++
 09-grafy/abstraktni-seznamy.py | 53 +++++++++++++++++++++
 09-grafy/do-sirky-matice.py    | 37 +++++++++++++++
 09-grafy/do-sirky-seznamy.py   | 34 ++++++++++++++
 09-grafy/iterator.py           | 40 ++++++++++++++++
 09-grafy/test.in               |  6 +++
 6 files changed, 254 insertions(+)
 create mode 100755 09-grafy/abstraktni-matice.py
 create mode 100755 09-grafy/abstraktni-seznamy.py
 create mode 100755 09-grafy/do-sirky-matice.py
 create mode 100755 09-grafy/do-sirky-seznamy.py
 create mode 100755 09-grafy/iterator.py
 create mode 100644 09-grafy/test.in

diff --git a/09-grafy/abstraktni-matice.py b/09-grafy/abstraktni-matice.py
new file mode 100755
index 0000000..1dfa32d
--- /dev/null
+++ b/09-grafy/abstraktni-matice.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python3
+# Abstraktní reprezentace grafu třídou,
+# implementace třídy pomocí matice sousednosti
+
+from collections import deque
+
+class Graf:
+    """Reprezentace grafu maticí sousednosti."""
+
+    def __init__(self, n):
+        self.n = n
+        self.matice = [[0]*n for _ in range(n)]
+
+    def __repr__(self):
+        return str(self.matice)
+
+    def pridej_hranu(self, i, j):
+        self.matice[i][j] = 1
+
+    # Na cyklus přes všechny vrcholy tentokrát potřebujeme vyrobit
+    # iterátor. Pozor, nestačí definovat metodu __iter__, protože
+    # potřebujeme předat jako parametr vrchol, jehož sousedy chceme
+    # vyjmenovat.
+    def sousede(self, i):
+        return SousedeIt(self, i)
+
+class SousedeIt:
+    """Iterátor přes sousedy zadaného vrcholu."""
+
+    def __init__(self, graf, i):
+        # Iterátor si pamatuje:
+        self.graf = graf    # ke kterému grafu patří
+        self.i = i          # sousedy kterého vrcholu vyjmenovává
+        self.j = 0          # aktuální polohu
+
+    # Pozor: Pokud napíšeme "for v in graf.sousede(u)", cyklus for bude chtít
+    # po objektu vráceném z metody sousede, aby vyrobil svůj iterátor. Jenže
+    # v našem případě to už iterátor je, tak ho musíme naučit, aby vrátil sám
+    # sebe. To je v Pythonu standardní postup: když iterátoru řeknete, že má
+    # vyrobit iterátor, vrátí sebe sama.
+    def __iter__(self):
+        return self
+
+    # Vrátí dalšího souseda v pořadí
+    def __next__(self):
+        # Kde je další jednička v řádku?
+        while self.j < self.graf.n and self.graf.matice[self.i][self.j] == 0:
+            self.j += 1
+        if self.j < self.graf.n:
+            self.j += 1
+            return self.j - 1
+        else:
+            raise StopIteration
+
+
+# Původní příklad na prohledávání do šířky jsme přepsali tak,
+# aby s grafem pracoval výhradně pomocí třídy Graf.
+
+n = int(input())
+graf = Graf(n)
+for i in range(n):
+    for j in input().split():
+        graf.pridej_hranu(i, int(j))
+
+print(graf)
+
+def prohledej(v0):
+    byl_jsem = [False] * n
+    byl_jsem[v0] = True
+    vzdalenost = [None] * n
+    vzdalenost[v0] = 0
+    fronta = deque()
+    fronta.append(v0)
+
+    while fronta:
+        u = fronta.popleft()
+        print(u, vzdalenost[u])
+        for v in graf.sousede(u):
+            if not byl_jsem[v]:
+                byl_jsem[v] = True
+                vzdalenost[v] = vzdalenost[u] + 1
+                fronta.append(v)
+
+prohledej(3)
diff --git a/09-grafy/abstraktni-seznamy.py b/09-grafy/abstraktni-seznamy.py
new file mode 100755
index 0000000..b0f5ca7
--- /dev/null
+++ b/09-grafy/abstraktni-seznamy.py
@@ -0,0 +1,53 @@
+#!/usr/bin/python3
+# Abstraktní reprezentace grafu třídou,
+# implementace třídy pomocí seznamů sousedů.
+
+from collections import deque
+
+class Graf:
+    """Reprezentace grafu seznamy sousedů."""
+
+    def __init__(self, n):
+        self.n = n
+        self.seznam_sousedu = [[] for _ in range(n)]
+
+    def __repr__(self):
+        return str(self.seznam_sousedu)
+
+    def pridej_hranu(self, i, j):
+        self.seznam_sousedu[i].append(j)
+
+    # Používáme jako "for v in graf.sousede(u)"
+    def sousede(self, i):
+        return self.seznam_sousedu[i]
+
+
+# Původní příklad na prohledávání do šířky jsme přepsali tak,
+# aby s grafem pracoval výhradně pomocí třídy Graf.
+
+n = int(input())
+graf = Graf(n)
+for i in range(n):
+    for j in input().split():
+        graf.pridej_hranu(i, int(j))
+
+print(graf)
+
+def prohledej(v0):
+    byl_jsem = [False] * n
+    byl_jsem[v0] = True
+    vzdalenost = [None] * n
+    vzdalenost[v0] = 0
+    fronta = deque()
+    fronta.append(v0)
+
+    while fronta:
+        u = fronta.popleft()
+        print(u, vzdalenost[u])
+        for v in graf.sousede(u):
+            if not byl_jsem[v]:
+                byl_jsem[v] = True
+                vzdalenost[v] = vzdalenost[u] + 1
+                fronta.append(v)
+
+prohledej(3)
diff --git a/09-grafy/do-sirky-matice.py b/09-grafy/do-sirky-matice.py
new file mode 100755
index 0000000..af3b367
--- /dev/null
+++ b/09-grafy/do-sirky-matice.py
@@ -0,0 +1,37 @@
+#!/usr/bin/python3
+# Prohledávání grafu do šířky
+# Graf je reprezentovaný pomocí matice sousednosti
+
+from pprint import pprint
+from collections import deque
+
+# Načteme vstup: nejprve počet vrcholů, pak n řádků se sousedy jednotlivých vrcholů
+n = int(input())
+matice = [[0]*n for _ in range(n)]
+for i in range(n):
+    for j in input().split():
+        j = int(j)
+        matice[i][j] = 1
+
+# Pro kontrolu vypíšeme reprezentaci grafu (pprint = pretty print)
+pprint(matice)
+
+# Prohledávání do šířky
+def prohledej(v0):
+    byl_jsem = [False] * n
+    byl_jsem[v0] = True
+    vzdalenost = [None] * n
+    vzdalenost[v0] = 0
+    fronta = deque()
+    fronta.append(v0)
+
+    while fronta:
+        u = fronta.popleft()
+        print(u, vzdalenost[u])
+        for v in range(n):
+            if matice[u][v] == 1 and not byl_jsem[v]:
+                byl_jsem[v] = True
+                vzdalenost[v] = vzdalenost[u] + 1
+                fronta.append(v)
+
+prohledej(3)
diff --git a/09-grafy/do-sirky-seznamy.py b/09-grafy/do-sirky-seznamy.py
new file mode 100755
index 0000000..526a4cf
--- /dev/null
+++ b/09-grafy/do-sirky-seznamy.py
@@ -0,0 +1,34 @@
+#!/usr/bin/python3
+# Prohledávání grafu do šířky
+# Graf je reprezentovaný pomocí seznamů sousedů
+
+from collections import deque
+
+# Načteme vstup: nejprve počet vrcholů, pak n řádků se sousedy jednotlivých vrcholů
+n = int(input())
+sousede = []
+for i in range(n):
+    sousede.append([ int(j) for j in input().split() ])
+
+# Pro kontrolu vypíšeme reprezentaci grafu
+print(sousede)
+
+# Prohledávání do šířky
+def prohledej(v0):
+    byl_jsem = [False] * n
+    byl_jsem[v0] = True
+    vzdalenost = [None] * n
+    vzdalenost[v0] = 0
+    fronta = deque()
+    fronta.append(v0)
+
+    while fronta:
+        u = fronta.popleft()
+        print(u, vzdalenost[u])
+        for v in sousede[u]:
+            if not byl_jsem[v]:
+                byl_jsem[v] = True
+                vzdalenost[v] = vzdalenost[u] + 1
+                fronta.append(v)
+
+prohledej(3)
diff --git a/09-grafy/iterator.py b/09-grafy/iterator.py
new file mode 100755
index 0000000..3694179
--- /dev/null
+++ b/09-grafy/iterator.py
@@ -0,0 +1,40 @@
+#!/usr/bin/python3
+# Příklad na iterátory: vlastní implementace range(n)
+
+class MyRange:
+
+    def __init__(self, n):
+        self.n = n
+
+    # Touto magickou metodou objektu řekneme, aby vyrobil iterátor
+    def __iter__(self):
+        return MyIter(self)
+
+class MyIter:
+
+    # Iterátoru při inicializaci říkáme, ke kterému MyRange patří
+    def __init__(self, rng):
+        # Iterátor si pamatuje range a poslední vygenerované číslo
+        self.rng = rng
+        self.i = -1
+
+    # Touto magickou metodou po iterátoru chceme, aby vydal další prvek,
+    # nebo vyvolal výjimku StopIteration, pokud už žádný další neexistuje.
+    def __next__(self):
+        self.i += 1
+        if self.i < self.rng.n:
+            return self.i
+        else:
+            raise StopIteration
+
+for x in MyRange(10):
+    print(x)
+
+# Uvnitř for-u se stane toto:
+#
+#   r = MyRange(10)
+#   it = r.__iter__()    <- řekneme objektu MyRange, aby vyrobil iterátor
+#   while True:
+#       x = it.__next__()   <- řekneme iterátoru, aby vydal další prvek
+#       pokud nastala výjimka StopIteration, skonči
+#       print(x)         <- tělo cyklu
diff --git a/09-grafy/test.in b/09-grafy/test.in
new file mode 100644
index 0000000..d3f40aa
--- /dev/null
+++ b/09-grafy/test.in
@@ -0,0 +1,6 @@
+5
+1
+2
+3
+1 4
+
-- 
GitLab