Узлы в связанном списке связаны через указатели. Указатели представляют собой адрес места в памяти. Порядок в связанном списке определяется указателем в каждом узле. node
в двойном круговом связанном списке содержит элемент данных и два указателя узлов, один на предыдущий узел и один на следующий узел. В двусвязном списке мы можем перемещаться в обоих направлениях.
Связано: Двусвязный список
Вот мем, чтобы понять Круговой связанный список.
Первый узел связанного списка является головным, а последний узел — хвостовым. Если head равен NULL, то список пуст.
В C++ узел можно определить с помощью struct
, которые содержат элемент данных и указатели узла.
struct Node
{
T data;
Node * next;
Node * prev;
Node(T value) : data(std::move(value)),
next(nullptr),
prev(nullptr)
{}
};
Node(T val): data(val), next(nullptr), prev(nullptr) {}
— это конструктор для struct Node
, который используется для инициализации data
, next
и prev
. T
означает, что это общая структура, и данные могут хранить значения всех типов данных.
Чтобы объявить голову и хвост: Node *head, *tail;
На приведенном выше рис. Узел, содержащий 5, является головным, а узел, содержащий 15, — хвостовым. Указатель prev
в заголовке указывает на последний узел, а указатель next
в хвосте указывает на начало.
Три основные операции, поддерживаемые связным списком, — это поиск, вставка и удаление.
Searching
В функции search
значение передается в качестве аргумента, и его узел возвращается, если он найден, в противном случае появляется сообщение "Нет такого элемента в списке" и возвращается nullptr
.
Функция начинает поиск от головы к последнему узлу, и переданное значение сопоставляется с элементом данных каждого узла.
Вот код для итеративного поиска.
struct Node *search(T value)
{
Node *node = head;
while(node->next != head)
{
if(node->data == value)
{
return node;
}
node = node->next;
}
if(node->data == value)
{
return node;
}
std::cerr << "No such element in the list \n";
return nullptr;
}
Вставка
Функция insert
вставляет узел со значением в конец связанного списка. Если связанный список не содержит ни одного узла, то новый узел становится головным и конечным, в противном случае новый узел добавляется после хвоста, и его указатель prev
указывает на хвост, а указатель next
указывает на начало. Затем новый узел становится хвостовым.
void insert(T value)
{
Node *node = new Node(value);
if(head == nullptr)
{
node->next = node;
node->prev = node;
head = node;
tail = node;
}
tail = head->prev;
tail->next = node;
node->prev = tail;
node->next = head;
head->prev = node;
tail = node;
}
Удаление
В функцию deleteNode
вводится значение, которое необходимо удалить. Функция ищет узел, содержащий значение, используя функцию search
, а затем узел удаляется.
Если искомый узел равен head
, тогда узел, следующий за головой, становится головным, а затем искомый узел удаляется. Узел удаляется, только если значение существует означает if (node != nullptr)
.
После удаления next и prev указатели предыдущего и следующего узлов обновляются.
void deleteNode(T value)
{
Node *node = search(value);
if(node == nullptr)
{
std::cerr << "No such value in the list\n";
return;
}
else
{
Node *tmp = head;
Node *tail = head->prev;
if(tmp == node)
{
tail->next = tmp->next;
tmp->prev->next->prev = tail;
head = tail->prev;
delete tmp;
return;
}
else if(tail == node)
{
Node *curr = tail;
tmp = tail->prev;
tmp->next = curr->next;
head->prev = tmp;
tail = tmp;
delete curr;
return;
}
else
{
while(tmp->next != head)
{
if(tmp == node)
{
tmp->prev->next = tmp->next;
tmp->prev->next->prev = tmp->prev;
delete tmp;
return;
}
tmp = tmp->next;
}
}
}
}
C++ реализация циклического двусвязного списка
В реализации этой программы на C++ у нас есть конструктор копирования, конструктор перемещения, оператор присваивания копирования, оператор присваивания перемещения и деструктор.
Поскольку наличие определяемого пользователем деструктора, конструктора копирования или оператора присваивания копирования предотвращает неявное определение конструктора перемещения и оператора присваивания перемещения, любой класс, для которого желательна семантика перемещения, должен объявить все пять специальных функций-членов. Это называется Правило пяти.
#include <iostream>
#include <utility>
template <typename T>
class circular_doubly_linked_list
{
struct Node
{
T data;
Node * next;
Node * prev;
Node(T value) : data(std::move(value)),
next(nullptr),
prev(nullptr)
{}
};
Node *head, *tail;
public:
circular_doubly_linked_list() : head(nullptr),
tail(nullptr)
{}
//copy constructor
circular_doubly_linked_list(const circular_doubly_linked_list &);
//copy assignment
circular_doubly_linked_list& operator=(const circular_doubly_linked_list& cdll)
{
circular_doubly_linked_list temp(cdll);
temp.swap(*this);
return *this;
}
//move constructor
circular_doubly_linked_list(circular_doubly_linked_list&&) noexcept;
//move assignment
circular_doubly_linked_list& operator=(circular_doubly_linked_list&& cdll) noexcept
{
cdll.swap(*this);
return *this;
}
~circular_doubly_linked_list();
void append_node(T);
void delete_node(T);
friend void swap(circular_doubly_linked_list& lhs, circular_doubly_linked_list& rhs)
{
std::swap(lhs.head, rhs.head);
}
template <typename U>
friend std::ostream & operator<<(std::ostream & os, const circular_doubly_linked_list<U> & cdll)
{
cdll.print_list(os);
return os;
}
private:
struct Node *search(T value)
{
Node *node = head;
while(node->next != head)
{
if(node->data == value)
{
return node;
}
node = node->next;
}
if(node->data == value)
{
return node;
}
std::cerr << "No such element in the list \n";
return nullptr;
}
void print_list(std::ostream& os = std::cout) const
{
Node *tmp = head;
while(tmp->next != head)
{
std::cout << tmp->data << ' ';
tmp = tmp->next;
}
std::cout << tmp->data << '\n';
}
};
template <typename T>
circular_doubly_linked_list<T>::circular_doubly_linked_list(const circular_doubly_linked_list & cdll)
{
if(cdll.head == nullptr)
{
head = tail = nullptr;
}
else
{
head = new Node(cdll.head->data);
Node *curr = head;
Node *tmp = head;
Node *obj_curr = cdll.head;
while(obj_curr->next != cdll.head)
{
curr->next = new Node(obj_curr->next->data);
obj_curr = obj_curr->next;
curr = curr->next;
curr->prev = tmp;
tmp = tmp->next;
}
tail = curr;
curr->next = head;
head->prev = curr;
}
}
template <typename T>
circular_doubly_linked_list<T>::circular_doubly_linked_list(circular_doubly_linked_list&& cdll) noexcept
{
head = tail = nullptr;
swap(*this, cdll);
}
template <typename T>
void circular_doubly_linked_list<T>::append_node(T value)
{
Node *node = new Node(std::move(value));
if(head == nullptr)
{
node->next = node;
node->prev = node;
head = node;
tail = node;
}
tail = head->prev;
tail->next = node;
node->prev = tail;
node->next = head;
head->prev = node;
tail = node;
}
template <typename T>
void circular_doubly_linked_list<T>::delete_node(T value)
{
Node *node = search(value);
if(node == nullptr)
{
std::cerr << "No such value in the list\n";
return;
}
else
{
Node *tmp = head;
Node *tail = head->prev;
if(tmp == node)
{
tail->next = tmp->next;
tmp->prev->next->prev = tail;
head = tail->prev;
delete tmp;
return;
}
else if(tail == node)
{
Node *curr = tail;
tmp = tail->prev;
tmp->next = curr->next;
head->prev = tmp;
tail = tmp;
delete curr;
return;
}
else
{
while(tmp->next != head)
{
if(tmp == node)
{
tmp->prev->next = tmp->next;
tmp->prev->next->prev = tmp->prev;
delete tmp;
return;
}
tmp = tmp->next;
}
}
}
}
template <typename T>
circular_doubly_linked_list<T>::~circular_doubly_linked_list()
{
if(head)
{
Node *tmp = head;
while(tmp->next != head)
{
Node *t = tmp;
tmp = tmp->next;
delete t;
}
delete tmp;
head = nullptr;
}
}
int main()
{
circular_doubly_linked_list<int> cdll1;
cdll1.append_node(3);
cdll1.append_node(4);
cdll1.append_node(5);
cdll1.append_node(6);
cdll1.append_node(7);
cdll1.append_node(8);
std::cout << cdll1;
cdll1.delete_node(6);
std::cout << cdll1;
circular_doubly_linked_list<int> cdll2(cdll1); // using copy constructor
std::cout << "Linked List 2: " << cdll2;
circular_doubly_linked_list<int> cdll3 = cdll1; //using copy assignment
std::cout << "Linked List 3: " << cdll3;
circular_doubly_linked_list<int> cdll4 = std::move(cdll2); //using move constructor
std::cout << "Linked list 4: " << cdll4;
}
Посмотреть этот код на Github.
Ссылка:
Введение в алгоритмы
Руководство по проектированию алгоритмов
Простые структуры данных и алгоритмы
Вам также может понравиться
Переместить все нечетные числа после четных в односвязном списке
Объединить два отсортированных связанных списка (на месте)
Разделить односвязный круговой связанный список
Обратить связанный список
Поиск длины цикла в связанном списке
Односвязный список
Следите за мной в Facebook и Twitter
Первоначально опубликовано на https://programmercave0.github.io 2 февраля 2018 г.