链表是一种常见的数据结构,它由一系列的节点组成,每个节点包含数据部分和指向列表中下一个节点的next指针。链表是一种动态数据结构,它可以有效地插入和删除节点,而不需要改变列表中剩余的元素。
常见的链表主要有以下几种类型:
-
单向链表:这是最简单的链表形式,每个节点包含两个部分:一部分存储数据元素,另一部分存储指向列表中下一个节点的指针(next)。链表的最后一个节点指向null(或称为None、nil),以此表示链表结束。
-
双向链表:双向链表中的每个节点除了包含数据部分和指向下一个节点的指针外,还包含一个指向前一个节点的指针(pre)。这使得双向链表比起单向链表在向后遍历方面更加灵活。
-
循环链表:无论是单向循环链表还是双向循环链表,其特点都是链表的最后一个节点指向链表的第一个节点,形成一个环。
链表的基本操作包括:
- 插入(Insert):在链表的特定位置添加一个新的节点。
- 删除(Erase):从链表中移除一个节点。
- 查找(Search):遍历链表以查找包含特定数据的节点。
- 修改(renew):在查找包含特定数据的节点后,对该节点数据进行更新
- 高效的插入和删除操作,时间复杂度为O(1)(在已知节点迭代器的情况下),这是因为插入和删除操作只涉及到节点指针的修改,不需要移动元素。
- 动态数据结构,大小可以根据需要增加或减少。
链表的缺点
- 相对于数组,链表需要更多的内存来存储节点之间的链接信息。
- 由于链表不是连续存储的,所以在链表中随机访问元素的效率较低,需要从头节点开始遍历。
代码示例,仅展示增删,无查改,改只是在查中衍生一步修改数据而已,查在删中就可体现,不赘述,有需要可自行添加。
#pragma once
#include<iostream>
using namespace std;
template<class V>
struct list_node
{
list_node(V& obj)
:
_next(nullptr),
_data(obj)
{
}
struct list_node* _next;
V _data;
};
template<class V>
class list
{
typedef struct list_node<V> Node;
public:
bool push_back(V& obj)//以尾插为例
{
if (root == nullptr)
{
root = new Node(obj);
root->_next = root;
return true;
}
Node* cur = root;
while (cur->_next != root)
{
cur = cur->_next;
}
cur->_next = new Node(obj);
cur->_next->_next = root;
return true;
}
bool pop_back()//以尾删为例
{
if (root->_next == root)
{
delete root;
root = nullptr;
return true;
}
Node* cur = root;
Node* parent = cur;
while (cur->_next != root)
{
parent = cur;
cur = cur->_next;
}
parent->_next = cur->_next;
delete cur;
return true;
}
void find()
{
Node* cur = root;
while (true)
{
cout << cur->_data << endl;
cur = cur->_next;
if (cur == root)break;
}
}
private:
Node* root = nullptr;
};