目录
基础知识
链表也是一种线性表。链表的内存结构是不连续的内存空间,是将一组零散的内存块串联起来,从而进行数据存储的数据结构。
插入、删除数据效率O(1)级别(只需更改指针指向即可),随机访问效率O(n)级别(需要从链头至链尾进行遍历)。此外和数组相比,内存空间消耗更大,因为每个存储数据的节点都需要额外的空间存储指针。
常用链表:
单链表:每个节点只包含一个指针,即后继指针。首节点地址表示整条链表,尾节点的后继指针指向空地址null
循环链表:除了尾节点的后继指针指向首节点的地址外均与单链表一致。适用于存储有循环特点的数据,比如约瑟夫问题。
双向链表:节点除了存储数据外,还有两个指针分别指向前一个节点地址(前驱指针prev)和下一个节点地址(后继指针next)。首节点的前驱指针prev和尾节点的后继指针均指向空地址。虽然理论上链表的插入删除效率都为O(1),但某些情况下,单链表的插入删除效率为O(n),而双向链表可达O(1)。双向链表的按值查询速度一般来说也比单链表快一倍。但双向链表所耗储存空间比单链表更多。详见(https://time.geekbang.org/column/article/41013)
双向循环链表:除首节点的前驱指针指向尾节点,尾节点的后继指针指向首节点外与双向链表一致
缺点:
内存空间消耗更大,因为需要额外的空间存储指针信息。
对链表进行频繁的插入和删除操作,会导致频繁的内存申请和释放,容易造成内存碎片
链表代码的注意事项:
针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。如不想进行,可以利用哨兵结点写带头链表。
重要边界条件检查:如果链表为空时,代码是否能正常工作?如果链表只包含一个结点时,代码是否能正常工作?如果链表只包含两个结点时,代码是否能正常工作?代码逻辑在处理头结点和尾结点的时候,是否能正常工作?(https://time.geekbang.org/column/article/41149)
链表的基本操作——创建、遍历、插入、删除
1.单向链表的创建和遍历
(1)无头结点
#include<iostream>
using namespace std;
struct list { //定义链表的结构
int value;
list* next;
};
int main()
{
list* head = new list; //创建头指针
int n;
cout << "请输入将要储存的元素个数: ";
cin >> n;
cout << "请依次输入将要储存的元素:\n";
cin >> head->value;
list* p = head;
for (int i = 0;i < n - 1;i++)