Graf : przykład projektowania klasy --------------------------------------- Rozpatrzmy strukturę danych grafu jako przykład projektowania klasy. Graf teoretycznie definiujemy jako zbiór wierzchołków :math:`{V}` oraz zbiór łączących je krawędzi :math:`{E}`. Wydawało by się, że wystarczy ponumerować wierzchołki i przechowywać krawędzie .. literalinclude:: SimpleGraph-minimal.py :language: python :linenos: :lines: 1-14, 18-20, 24 Szybko jednak dojdziemy do wniosku, że przydatne byłoby również przechowywanie informacji dla wierzchołków: .. literalinclude:: SimpleGraph-minimal.py :language: python :linenos: .. Pierwszy dylemat: czy krawędzie dodajemy tylko do obiektów, czy też poprzez indeksy obiektów? .. To drugie jest bardziej elastyczne i szybsze, ale wymaga przeciążenia. A co w przypadku, kiedy .. dane w grafie są typu całkowitego? Jak odróżnić? Może metody ``connect_vertices()`` i ``connect_indexes()`` Powyższy kod pokazuje, jakie metody chcielibymy mieć w klasie ``SimpleGraph``. Kwestia implementacji pozostaje jednak otwarta. W zasadzie są dwie możliwości: jako macierz i jako listy Graf jako *macierz* sąsiedztwa ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ W pierwszej z tych implementacji mamy dwuwymiarową macierz ``E[][]``, w której ``E[i][j]==0`` oznacza, że wierzchołki ``i`` oraz ``j`` nie są połączone. Krawędzie grafu mogą być niezerowymi elementami tej macierzy, np. ``E[i][j] = "blue"``. Dane dla wierzchołków przechowywać wtedy można na liście. .. note:: Jakie są wady takiego rozwiązania? A jakie zalety? Graf jako *lista* sąsiedztwa ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Po przedyskutowaniu wad i zalet, wybieramy implementację list sąsiedztwa. Pojawia się przy tym kolejne pytanie. Otóż krawędzie można przechowywać w liście list: .. code-block:: python :linenos: edges = [ [3], [2, 3], [1], [0,2] ] lub w słowniku: .. code-block:: python :linenos: edges = { 0: [3], 1: [2, 3], 2: [1], 3:[0, 2] } Wybór zależy od tego, ile składowych będzie miał nasz graf. Powyżej już przyjęliśmy, że będziemy przechowywać indeksy powiązanych wierzchołków, choć można by trzymać same wierzchłoki, jak poniżej: .. code-block:: python :linenos: edges = { 'A': ['C'], 'B': ['C', 'D'], 'C': ['B'], 'D':['A', 'C'] } Następnie należy zdecydować, jak będą przechowywane wartości dla krawędzi. Dla implementacji opartej o macierz sąsiedztwa wybór był oczywisty. W przypadku list mamy takie dwie możliwości: albo razem z indeksami krawędzi: .. code-block:: python :linenos: edges_values = { 0: [(3,"green")], 1: [(2,"white"), (3,"red")], 2: [(1,"white")], 3:[(0,"green"), (2,"red")] } albo w oddzielnych strukturach danych: .. code-block:: python :linenos: edges = [ [3], [2, 3], [1], [0,2] ] e_val = [ ["green"], ["white", "red"], ["white"], ["green","red"] ] W drugim przypadku implementacja metody ``get_edges(self, vi)`` jest bardzo łatwa, w przeciwieństwie do pierwszej opcji. Pierwsza jednak gwarantuje, że danych na krawędziach jest tyle, ile krawędzi. .. literalinclude:: SimpleGraph.py :language: python :linenos: