Составление списка смежности в C ++ для ориентированного графа

Привет всем 🙂 Сегодня я совершенствую свои навыки в теории графов и структурах данных. Я решил сделать небольшой проект на C ++, потому что я давно работал с C ++.

Я хочу сделать список смежности для ориентированного графа. Другими словами, что-то похожее на:

0-->1-->3
1-->2
2-->4
3-->
4-->

Это был бы ориентированный граф с V0 (вершина 0), имеющим ребро к V1 и V3, V1, имеющее ребро к V2, и V2, имеющее ребро к V4, вот так:

V0----->V1---->V2---->V4
|
|
v
V3

Я знаю, что для этого мне нужно будет создать список смежности в C ++. Список смежности в основном массив связанных списков. Хорошо, давайте посмотрим на некоторый псевдо-код C ++:

#include <stdio>
#include <iostream>
using namespace std;

struct graph{
//The graph is essentially an array of the adjList struct.
node* List[];

};

struct adjList{
//A simple linked list which can contain an int at each node in the list.

};

struct node {
int vertex;
node* next;
};

int main() {
//insert cool graph theory sorting algorithm here
}

Как вы можете сказать, этот псевдокод в настоящее время далек от цели. И это то, что я хотел помочь — указатели и структуры в C ++ никогда не были моей сильной стороной. Прежде всего, это заботится о вершинах, на которые указывает вершина — но как насчет самой вершины? Как я могу отслеживать эту вершину? Когда я зацикливаюсь на массиве, мне бесполезно знать только то, на что указывают вершины, а не знать, на какие точки в их. Первый элемент в каждом списке, вероятно, должен быть этой вершиной, а затем элементы после него являются вершинами, на которые он указывает. Но как я могу получить доступ к этому первому элементу списка в моей основной программе? (извините, если это запутанно или запутанно, я бы с удовольствием перефразировал).

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

(Также у меня возник вопрос о списке смежности. Что отличается от простого использования списка массивов? Почему я не могу просто иметь список с массивом для каждого элемента в списке?)

10

Решение

Вы можете использовать вектор в узле, как список смежности.

class node {
int value;
vector<node*> neighbors;
};

Если график известен во время компиляции, вы можете использовать массив, но это «немного» сложнее. Если вы знаете только размер графика (во время компиляции), вы можете сделать что-то подобное.

template<unsigned int N>
class graph {
array<node, N> nodes;
}

Чтобы добавить соседа, вы делаете что-то подобное (не забывайте нумерацию с нуля):

nodes[i].neighbors.push_back(nodes+j); //or &nodes[j]

Конечно, вы можете сделать список смежности без указателей и работать «над» таблицей. Чем у тебя есть vector<int> в узле и вы нажимаете номер соседа. Используя оба представления графа, вы можете реализовать все алгоритмы, которые используют список смежности.

И, наконец, я мог бы добавить. Некоторые используют список вместо вектора, потому что удаление в O (1) время. Ошибка. Для большинства алгоритмов порядок в списке смежности не важен. Таким образом, вы можете стереть любой элемент из вектора в O (1) время. Просто поменяйте местами с последним элементом, pop_back является O (1) сложность. Что-то вроде того:

if(i != number_of_last_element_in_list) //neighbors.size() - 1
swap(neighbors[i], neighbor.back());
neighbors.pop_back();

Конкретный пример (у вас есть вектор в узле, C ++ 11 (!)):

//creation of nodes, as previously
constexpr unsigned int N = 3;
array<node,N> nodes; //or array<node, 3> nodes;
//creating edge (adding neighbors), in the constructor, or somewhere
nodes[0].neighbors = {&nodes[1]};
nodes[1].neighbors = {&nodes[0], &nodes[1]};
//adding runtime, i,j from user, eg. i = 2, j = 0
nodes[i].neighbors.push_back(&nodes[j]); //nodes[2].neighbors = {&nodes[0]};

Я верю, что это понятно. От 0 ты можешь пойти в 1, от 1 в 0 и к себе, и (как, например,) из 2 в 0, Это ориентированный граф. Если вы хотите ненаправленный, вы должны добавить к соседним узлам адреса соседей. Вы можете использовать цифры вместо указателей. vector<unsigned int> в class node и отталкивая номера, без адресов.


Как мы знаем, вам не нужно использовать указатели. Вот пример этого тоже.

Когда количество вершин может измениться, вы можете использовать вектор узлов (vector<node>) вместо массива, и просто изменение размера. Остальное остается без изменений. Например:

vector<node> nodes(n); //you have n nodes
nodes.emplace_back(); //you added new node, or .resize(n+1)
//here is place to runtime graph generate
//as previously, i,j from user, but now you have 'vector<unsigned int>' in node
nodes[i].neighbors.push_back(j);

Но ты не может стереть узел, это нарушает нумерацию! Если вы хотите что-то стереть, вы должны использовать список (list<node*>) указателей. В противном случае вы должны оставить несуществующие вершины. Здесь порядок имеет значение!


По поводу линии nodes.emplace_back(); //adding node, Это безопасно с графом без указателей. Если вы хотите использовать указатели, вы преимущественно не должен изменить размер графика.
Вы можете случайно изменить адрес некоторых узлов при добавлении вершины, когда vector будет скопирован в новое место (из космоса).

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

node* const graph = new node[size]; //<-- It is your graph.
//Here no address change accidentally.

С помощью vector как список смежности всегда отлично! Нет возможности изменить адрес узла.

15

Другие решения

Это может быть не очень общий подход, но именно так я обрабатываю список смежности в большинстве случаев. C ++ имеет библиотеку STL, которая поддерживает структуру данных для связанного списка, названного как list,

Скажи у тебя N узлы на графике, создать связанный список для каждого узла.

list graph[N];

Сейчас graph[i] представляют соседей узла i. Для каждого ребра от i до j сделать

graph[i].push_back(j);

Наилучший комфорт — отсутствие обработки указателей, а также ошибок ошибок сегментации.

Для получения дополнительной информации http://www.cplusplus.com/reference/list/list/

3

Я предлагаю вам добавить в структуру узла Список смежности.
И определите структуру графа как список узлов вместо списка смежных списков 🙂

struct node {
int vertex;
node* next;
adjList m_neighbors;
};
struct graph{
//List of nodes
};
1

Я бы порекомендовал более общий и простой подход использования вектора и пар:
#включают
#включают

typedef std::pair<int, int> ii; /* the first int is for the data, and the second is for the weight of the Edge - Mostly usable for Dijkstra */
typedef std::vector<ii> vii;
typedef std::vector <vii> WeightedAdjList; /* Usable for Dijkstra -for example */
typedef std::vector<vi> AdjList; /*use this one for DFS/BFS */

Или стиль псевдонима (> = C ++ 11):

using ii = std::pair<int,int>;
using vii = std::vector<ii>;
using vi = std::vector<int>;
using WeightedAdjList = std::vector<vii>;
using AdjList = std::vector<vi>;

Отсюда:
используя вектор и пары (из ответа tejas)

За дополнительной информацией вы можете обратиться к очень хорошему резюме topcoder:
Включите C ++ с STL

0

Мой подход заключается в использовании хэш-карты для хранения списка узлов в графе

class Graph {
private:
unordered_map<uint64_t, Node> nodeList;
...
}

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

Узел содержит список смежности, в данном случае в виде вектора c ++ 11. Это также может быть связанный список, хотя для этого варианта использования я не вижу разницы в эффективности. Может быть, список будет лучше, если вы захотите как-то его отсортировать.

class Node{
uint64_t id;     // Node ID
vector<uint64_t> adjList;
...
}

При таком подходе вы должны пройти через список смежности, а затем найти карту по идентификатору, чтобы получить узел.

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

Как вы можете видеть, существует множество компромиссных решений, которые вы должны принять при реализации графика, все зависит от ваших вариантов использования.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector