双向链表
双向链表(Doubly Linked List)是一种常见的数据结构,它与单向链表相似,但每个节点有两个指针,一个指向前一个节点(前驱节点),一个指向后一个节点(后继节点)。
双向链表中的每个节点包含三个基本部分:数据域(存储节点的数据)、前驱指针和后继指针。头节点和尾节点是特殊的节点,它们分别代表双向链表的起始和结束。
特点
-
双向连接:每个节点都有两个指针,一个指向前驱节点,一个指向后继节点。这种双向连接的结构使得在链表中的节点之间可以进行双向遍历。
-
首尾节点:双向链表通常有头节点和尾节点,它们分别代表链表的起始和结束。头节点的前驱指针为空,尾节点的后继指针为空。
-
插入和删除的灵活性:相较于单向链表,双向链表可以更方便地在任意位置插入和删除节点。通过调整前驱和后继指针的指向,可以高效地完成这些操作,而无需遍历整个链表。
-
前向和后向遍历:由于每个节点都有前驱和后继指针,双向链表可以从头节点或尾节点开始,沿着不同的方向遍历整个链表。这种灵活性在某些情况下能提高效率。
-
多占用空间:相对于单向链表,双向链表需要额外存储每个节点的前驱指针,因此会占用更多的内存空间。
-
维护复杂性:因为每个节点有两个指针,所以在插入、删除或修改节点时,需要同时更新相关节点的前驱和后继指针,这增加了链表的维护复杂性。
实现
数据结构:
节点:
template<typename T>
class Node {
public:
T val; // 数据域
Node *pre; // 前驱节点指针
Node *next; // 后继节点指针
~Node() {
}
};
链表属性:
private:
Node<T> *head; // 头指针
Node<T> *tail; // 尾指针
int siz = 0; // 元素个数
头尾指针使用同一个节点 ,不保存数据
结构图
实现功能
1、void add(T t); // 从尾部添加元素
2、void add(int index, T t); //在索引index处添加元素t
3、add_first(T t); // 从头部添加元素
4、bool contains(T t) const // 判断元素t是否在list中
5、T get(int index); // 获取指定位置的元素
6、T get_first() ;//获取第一个元素
7、T get_last(); // 获取最后一个元素
8、int index_of(T t); 获取指定位置的索引
9、 void clear();清空list
C++代码
头文件
/**
* 双向链表实现list
*/
#include <iostream>
#include <bits/functexcept.h>
#ifndef LIST_LINK_LIST_H
#define LIST_LINK_LIST_H
template<typename T>
class Node {
public:
T val;
Node *pre;
Node *next;
~Node() {
}
};
template<typename T>
class LinkList {
public:
// 从尾部添加元素
void add(T t);
// 在索引index处添加元素t
void add(int index, T t);
// 从头部添加元素
void add_first(T t);
// 清空list
void clear() {
Node<T> *t_h = head->next;
while (t_h != tail) {
Node<T> * t = t_h;
t_h = t_h->next;
delete t;
}
siz = 0;
head->pre = tail->next = nullptr;
};
// 判断元素t是否在list中
bool contains(T t) const {
Node<T> *t_h = head->next;
while (t_h != tail) {
if (t_h->val == t) return true;
t_h = t_h->next;
}
return false;
};
T get(int index) {
if (index < 0 || index > siz - 1) {
std::__throw_out_of_range_fmt("LinkList<T>::get(int index) index out of range");
}
Node<T> *ind_node;
if (index < (siz >> 1)) {
ind_node = head->next;
for (int i = 0; i < index; i++) {
ind_node = ind_node->next;
}
} else {
ind_node = tail->pre;
for (int i = siz - 1; i > index; i--) {
ind_node = ind_node->pre;
}
}
return ind_node->val;
};
T get_first() const { return head->next->val; }
T get_last() const { return tail->pre->val; }
int index_of(T t);
int size() const { return siz; }
// 打印
void print() {
if(!siz) return;
Node<T> *t = head->next;
while (t != tail) {
std::cout << t->val << " ";
t = t->next;
}
std::cout << std::endl;
}
LinkList() {
head = tail = new Node<T>();
head->pre = tail->next = nullptr;
}
~LinkList() {
Node<T> *t_h = head;
while (t_h != tail) {
t_h = t_h->next;
Node<T> *t = t_h;
delete t;
}
}
private:
Node<T> *head; // 头指针
Node<T> *tail; // 尾指针
int siz = 0; // 元素个数
};
#endif //LIST_LINK_LIST_H
其他实现
末尾添加元素
template<typename T>
void LinkList<T>::add(T t) {
Node<T> *node = new Node<T>();
node->val = t;
if (!siz) {
head->next = node;
tail->pre = node;
node->next = tail;
node->pre = head;
} else {
tail->pre->next = node;
node->pre = tail->pre;
tail->pre = node;
node->next = tail;
}
siz++;
}
指定位置添加元素
template<typename T>
void LinkList<T>::add(int index, T t) {
Node<T> *node = new Node<T>();
node->val = t;
Node<T> *ind_node;
if (index < 0) {
add_first(t);
} else if (index >= siz) {
add(t);
} else {
if (index < (siz >> 1)) {
ind_node = head->next;
for (int i = 0; i < index; i++) {
ind_node = ind_node->next;
}
} else {
ind_node = tail->next;
for (int i = siz - 1; i > index; i--) {
ind_node = ind_node->pre;
}
}
}
node->pre = ind_node->pre;
ind_node->pre->next = node;
ind_node->pre = node;
node->next = ind_node;
siz++;
}
头部添加元素
template<typename T>
void LinkList<T>::add_first(T t) {
Node<T> *node = new Node<T>();
node->val = t;
if (!siz) {
head->next = node;
tail->pre = node;
node->pre = head;
node->next = tail;
} else {
head->next->pre = node;
node->next = head->next;
node->pre = head;
head->next = node;
}
siz++;
}
获取指定位置的元素
template<typename T>
int LinkList<T>::index_of(T t) {
int i = 0;
Node<T> *t_h = head->next;
for (i; i < siz; i++) {
if (t_h->val == t) return i;
t_h = t_h->next;
}
return -1;
}
运行测试
#include "link_list.h"
int main() {
LinkList<int> linkList = LinkList<int>();
linkList.add_first(1);
linkList.add(2);
linkList.add_first(0);
linkList.add(0,10);
linkList.print();
std::cout << linkList.index_of(1) << std::endl;
std::cout<< linkList.get(2)<<std::endl;
std::cout << linkList.contains(2) << std::endl;
linkList.clear();
std::cout << "----------" << std::endl;
linkList.print();
linkList.add(1);
linkList.add(2);
linkList.add(3);
linkList.print();
}
运行结果
10 0 1 2
2
1
1
----------
1 2 3