Углубленный взгляд на матрицы смежности логики и скрытого кода.

👋 Добро пожаловать!

Привет! Графы - это чрезвычайно мощные структуры данных. Они используются для представления «сети» связанных элементов.

В этой статье вы узнаете логику, лежащую в основе матриц смежности, и то, как вы можете создавать их в Python для представления графиков. Начнем! 😀 🔮

1️⃣ Встречайте матрицы смежности

Это, вероятно, очень похоже на то, что вы представляете, когда я упоминаю слово «график», верно?

Это правда, эта диаграмма представляет собой график, но не кажется ли вам, что она слишком абстрактна, чтобы реализовать ее в вашем коде и в реальных сценариях?

1️⃣ Что, если бы в вашем графике были тысячи узлов и соединений?
2️⃣ Как бы вы представили эти связи в своем коде?

Не волнуйтесь! На помощь приходят матрицы смежности! 💥

Они дают нам возможность представить наш график в соответствии с очень эффективной и структурированной процедурой. Создавая матрицу (таблицу со строками и столбцами), вы можете очень легко представлять узлы и ребра.

Давайте посмотрим, как вы можете шаг за шагом создать матрицу смежности! 👍

🔨 Давайте создадим матрицу смежности

Мы создадим таблицу смежности для представления этого графа:

1️⃣ Нарисуйте таблицу для представления узлов

Как вы можете видеть на диаграмме ниже, строки и столбцы используются для представления узлов на графике. На графике есть узлы A, B, C и D, поэтому мы включаем их все в столбцы и ряды. (Да, повторил! 😊)

Продолжайте читать, чтобы узнать, почему мы это делаем! 😉

2️⃣ Начните заполнять пробелы

Теперь пора начать представлять связи (ребра) между узлами. Вот тогда график оживает! 🔆

Вы можете начать заполнять поля по строкам или по столбцам. В зависимости от того, что вы выберете, вам нужно будет анализировать график по-разному. Давайте посмотрим, как вы можете начать анализировать его по строкам:

Узел A - Строка:
Если вы начинаете с первой строки (Узел A), вам необходимо проверить, имеет ли этот Узел ребро, направленное к каждому из узлов в графе (включая самих себя, которые называются «петлями»).

Когда вы анализируете по строкам, вы должны спросить себя:

✅ Есть ли ребро от узла A к самому себе?
✅ Есть ли ребро от узла A к узлу B?
✅ Есть ли ребро от узла A к узлу C?

…. и так далее. Вы можете увидеть узор. 👍

⚠️ Если ответ на этот вопрос для пары узлов «да», тогда вы должны написать 1 в ячейке, которая соответствует пересечению между этой парой узлов.

Давайте посмотрим на это в действии! 😅

На диаграмме ниже вы можете видеть, что соединения от узла A идут к узлу B, узлу C и узлу D, поэтому вы добавляете 1 в ячейки, где A и B пересекаются, где A и C пересекаются и где A и D. пересекаются. Затем вы заполняете остальную часть строки нулями, поскольку от узла A к нему нет ребер.

Узел B - строка:
Из этого узла не выходят ребра, поэтому вы заполняете всю строку нулями.

Узел C - строка:
Этот узел имеет только одно ребро, выходящее к узлу D, поэтому вы добавляете 1 в соответствующую ячейку и завершаете строку нулями.

Узел D - строка:
Как и узел B, узел D не имеет ребер, ведущих к другим узлам, поэтому вся строка будет заполнена нулями.

🏅 Вуаля! - Наша Матрица готова! Теперь столбцы 👍

Хорошая работа! Теперь давайте посмотрим, как можно заполнить эту матрицу столбец за столбцом. Вы увидите, как мы получим точно такую ​​же матрицу.

Для заполнения таблицы по столбцам необходимо спросить:

✅ Какие грани подходят к узлу A?
✅ Какие грани подходят к узлу B?
✅ Какие грани подходят к узлу C?

…. и так далее. Вы можете увидеть узор. 👍

Узел A - столбец:
Поскольку никакие края не подходят к узлу A, вы заполняете столбец нулями.

Узел B - столбец:
Единственное ребро, идущее к узлу B, идет от узла A, поэтому вы указываете 1 в этой ячейке и заполняете остальную часть столбца нулями.

Узел C - столбец:
Единственное ребро, идущее к узлу C, идет от узла A, поэтому вы добавляете 1 в эту ячейку и заполняете остальную часть столбца нулями.

Узел D - столбец:
К узлу D идут два ребра от узлов A и C, поэтому вы добавляете 1 в соответствующие ячейки и заполняете остальную часть столбца нулями.

🏆 Наконец-то! - Наша Матрица готова! ❤️

Как видите, две полученные матрицы равны, поэтому оба метода эквивалентны. 🎉

💡 Примечание. Помните, что неориентированные графы имеют двустороннюю связь между соединенной парой узлов, поэтому это также должно быть представлено в таблице. Когда есть граница между узлом A и узлом B, должна быть граница между узлом B и узлом A.

🏋 Матрицы смежности и взвешенные графы

Для взвешенных графиков, где каждое ребро имеет связанный с ним вес (значение), вы просто заменяете единицы на вес ребра, а нули на Null, потому что 0 может быть допустимым весом.

🚦 Примеры использования

  • Матрицы смежности следует использовать для плотных графов (графов с большим количеством ребер).
  • В противном случае если у графа очень мало ребер, вы будете тратить память, потому что матрица будет хранить много нулей из-за отсутствия соединений. В этом случае рекомендуется использовать списки смежности (следите за статьями по этой теме! 😄)

💻 В Кодекс!

Момент, которого вы так ждали, наконец-то настал! Узнав, что такое матрица смежности и какова ее логика, давайте погрузимся в код!

Вот пример невзвешенного ориентированного графа, представленного матрицей смежности 👇

Давайте посмотрим, как этот код работает за кулисами: 🔍

1️⃣ Настройте матрицу

2D-список, полностью заполненный нулями, создается при создании экземпляра графика благодаря этому коду:

def __init__(self, numNodes):
     self.adjacencyMatrix = []
     for i in range(numNodes): 
          self.adjacencyMatrix.append([0 for i in range(numNodes)])
     self.numNodes = numNodes

Ваша исходная матрица будет выглядеть так (сравните ее с диаграммой ниже):

self.adjacencyMatrix = [[0, 0, 0, 0], 
                        [0, 0, 0, 0], 
                        [0, 0, 0, 0], 
                        [0, 0, 0, 0]]

2️⃣ Добавить края

Затем вы начинаете добавлять края. Например:

graph1 = Graph(4)     # Create an instance of the graph with 4 nodes
graph1.addEdge(0, 1); # The 0th element is A. The 1st element is B

Это приведет к выполнению показанного ниже метода, который присваивает значение 1 соответствующей ячейке:

def addEdge(self, start, end):
    self.adjacencyMatrix[start][end] = 1

В этой строке self.adjacencyMatrix[start][end] = 1:

  • [start] выбирает строку.
  • [конец] выбирает столбец.
  • Наконец, этой позиции присваивается значение 1.

self.adjacencyMatrix = [[0, 1, 0, 0], # 0th row, 1st column.
                        [0, 0, 0, 0], 
                        [0, 0, 0, 0], 
                        [0, 0, 0, 0]]

На этом этапе наш график будет выглядеть, как на диаграмме ниже, потому что мы добавили только первое ребро.

Мы повторяем этот процесс, пока не добавим все ребра и наш график не будет выглядеть так:

3️⃣ Удалить края

Вы также можете удалить ребра из своего графика. Предположим, что после настройки матрицы вы хотите удалить границу между узлами C и D (как показано ниже).

Этот метод проверяет, есть ли граница между узлами, и, если есть, удалит ее, установив значение 0.

def removeEdge(self, start, end):
    if self.adjacencyMatrix[start][end] == 0:
        print("There is no edge between these nodes")
    else:
        self.adjacencyMatrix[start][end] = 0

4️⃣ Проверьте, подключены ли два узла

Вы также можете проверить, существует ли ребро, найдя элемент в матрице и проверив, равен ли он 0 или 1. Если значение ребра больше 0, ребро существует, и метод возвращает True. В противном случае между этими узлами нет края, и метод возвращает False.

def containsEdge(self, start, end):
     if self.adjacencyMatrix[start][end] > 0:
         return True
     else:
         return False

Предположим, вы хотите проверить, есть ли граница между узлами B и D.
Вы должны вызвать print(graph1.containsEdge(3, 1)), но, поскольку значение равно 0, между этими узлами нет грани, и возвращается False.

👋 Спасибо!

Очень надеюсь, что вам понравилась моя статья. ❤️
Искренне ценю ваши аплодисменты и комментарии.
Следуйте за мной на Medium | Twitter, чтобы найти больше подобных статей. 😃

Если вам понравилась эта статья, я предлагаю прочитать мою статью о Связанных списках:



📣 Следите за обновлениями, когда мы увидим будущую статью о Смежных списках, которые оптимальны для разреженных графов (графов с небольшим количеством ребер).