Узлы в связанном списке связаны через указатели. Указатели представляют собой адрес места в памяти. Порядок в связанном списке определяется указателем в каждом узле. Узел в двухсвязном списке содержит элемент данных и указатель узла на следующий узел. В односвязном списке мы можем перемещаться только в одном направлении.
Первый узел связанного списка является головным, а последний узел — хвостовым. Если head равен NULL, то список пуст.
В C++ узел можно определить с помощью struct
, которые содержат элемент данных и указатель на следующий узел.
struct Node
{
T data;
Node * next;
Node(T val): data(val), next(nullptr){}
};
Node(T val): data(val), next(nullptr), prev(nullptr) {}
— это конструктор для struct Node
, который используется для инициализации data
, next
и prev
. T
означает, что это общая структура, и данные могут хранить значения всех типов данных.
Объявить голову: Node *head, *tail;
На приведенном выше рис. Узел, содержащий 5, является головным, а узел, содержащий 15, — хвостовым.
Три основные операции, поддерживаемые связным списком, — это поиск, вставка и удаление.
Searching
Функция поиска для двусвязного списка аналогична функции поиска для односвязного списка.
По теме: Односвязный список
В функции search
значение передается в качестве аргумента, и его узел возвращается, если он найден, в противном случае появляется сообщение "Нет такого элемента в списке" и возвращается nullptr
.
Функция начинает поиск от головы к последнему узлу, и переданное значение сопоставляется с элементом данных каждого узла.
Вот код для итеративного поиска.
struct Node *search(T n)
{ //returns node of the given value
Node *node = head;
while(node != nullptr)
{
if(node->data == n)
return node;
node = node->next;
}
std::cerr << "No such element in the list \n";
return nullptr;
}
Вставка
Мы можем вставить узел в начале или в конце связанного списка. Когда мы вставляем узел впереди, указатель следующего узла указывает на начало списка, а затем узел становится новым началом списка. Вставляемое значение передается в качестве аргумента, и создается новый узел, содержащий это значение.
void insertFront(T val)
{
Node *node = new Node(val);
Node *tmp = head;
if (head == nullptr)
{
head = node;
tail = node;
}
else
{
node->next = head;
head = node;
node->next->prev = node;
}
}
Когда мы вставляем узел в конце списка, узел добавляется после хвоста, а затем узел становится хвостом списка.
void insertBack(T val)
{
Node *node = new Node(val);
if(tail->next == nullptr)
{
tail->next = node;
tail = node;
}
}
Удаление
В функцию deleteNode
вводится значение, которое необходимо удалить. Функция ищет узел, содержащий значение, используя функцию search
, а затем узел удаляется.
Если искомый узел head
, тогда узел, следующий за головой, становится головным, а затем искомый узел удаляется. Узел удаляется, только если существует значение if (node != nullptr)
.
void deleteVal(T val)
{
Node* find = findVal(val);
Node *tmp = head;
if(tmp == find)
{
head = tmp->next;
}
else
{
while(find != nullptr)
{
if(tmp->next == find)
{
tmp->next = find->next;
find->next->prev = tmp;
delete find;
return;
}
tmp = tmp->next;
}
}
}
C++ реализация двусвязного списка
#include <iostream>
template<class T>
class DoublyLinkedList
{
struct Node
{
T data;
Node* next;
Node* prev;
Node(T val): data(val), next(nullptr), prev(nullptr) {}
};
Node *head, *tail;
public:
DoublyLinkedList(): head(nullptr), tail(nullptr) {}
~DoublyLinkedList()
{
Node *tmp = nullptr;
while (head)
{
tmp = head;
head = head->next;
delete tmp;
}
head = nullptr;
}
DoublyLinkedList(const DoublyLinkedList<T> & dll) = delete;
DoublyLinkedList& operator=(DoublyLinkedList const&) = delete;
void insertFront(T val)
{
Node *node = new Node(val);
Node *tmp = head;
if (head == nullptr)
{
head = node;
tail = node;
}
else
{
node->next = head;
head = node;
node->next->prev = node;
}
}
void insertBack(T val)
{
Node *node = new Node(val);
if(tail->next == nullptr)
{
tail->next = node;
tail = node;
}
}
void deleteVal(T val)
{
Node* find = findVal(val);
Node *tmp = head;
if(tmp == find)
{
head = tmp->next;
}
else
{
while(find != nullptr)
{
if(tmp->next == find)
{
tmp->next = find->next;
find->next->prev = tmp;
delete find;
return;
}
tmp = tmp->next;
}
}
}
template <class U>
friend std::ostream & operator<<(std::ostream & os, const DoublyLinkedList<U> & dll){
dll.display(os);
return os;
}
private:
Node *findVal(T n) //returns node of the given number
{
Node *node = head;
while(node != nullptr)
{
if(node->data == n)
return node;
node = node->next;
}
std::cerr << "No such element in the list \n";
return nullptr;
}
void display(std::ostream& out = std::cout) const
{
Node *node = head;
while(node != nullptr)
{
out << node->data << " ";
node = node->next;
}
}
};
int main(){
DoublyLinkedList<int> l1;
l1.insertFront(3);
l1.insertBack(5);
l1.insertBack(12);
l1.insertFront(6);
l1.insertBack(88);
std::cout<<l1<<"\n";
l1.deleteVal(11);
std::cout<<l1<<"\n";
return 0;
}
Посмотреть этот код на Github
Ссылка:
Введение в алгоритмы
Руководство по проектированию алгоритмов
Простые структуры данных и алгоритмы
Вам также может понравиться:
Переместить все нечетные числа после четных в односвязный список
Объединить два отсортированных связанных списка (на месте)
Разделить одноциклический связанный список
Двойной циклический связанный список
Обратить связанный список
Поиск длины цикла в связанном списке
Первоначально опубликовано на https://programmercave0.github.io 28 июля 2017 г.