原文链接:https://www.acwing.com/blog/content/432/
单向链表
链表是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
使用链表结构可以克服数组需要先知道数据大小的缺点,链表结构可以充分利用内存空间。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
静态链表
int head, e[N], ne[N], idx;
// 初始化
void init()
{
head = -1;
idx = 0;
}
// 在链表头插入一个数a
void insert(int a)
{
e[idx] = a, ne[idx] = head, head = idx ++ ;
}
//向表中k位置插图x
void add(int k,int x){
n[idx]=x;
ne[idx]=ne[k];
ne[k]=idx++;
}
// 将k删除,需要保证头结点存在
void remove(int k)
{
ne[k]=ne[ne[k]];
}
动态链表
下面展示一些 内联代码片
。
typedef int ElemType;
struct LNode {
ElemType data;
struct LNode *next;
} LNode, *LinkList;
//初始化
void init(LinkList *L) {
*L = (LinkList)malloc(sizeof(struct LNode));
if (!*L) //存储分配失败
exit(-1);
(*L)->next = NULL;
}
//获取元素个数(长度)
int Length(LinkList L) {
int i = 0;
LinkList p = L->next; // p指向第一个结点
while (p) { // 没到表尾
i++;
p = p->next;
}
return i;
}
//查找
int Find(LinkList L, int i, ElemType *e) {
int j = 1; // j为检索
LinkList p = L->next; // p指向第一个结点
while (p && j < i) { // 顺指针向后查找,直到p指向第i个元素或p为空
p = p->next;
j++;
}
if (!p || j > i) // 第i个元素不存在
return 0;
*e = p->data; // 取第i个元素
return e;
}
bool Insert(LinkList L, int i, ElemType e) {
// 表L中第i个位置之前插入元素e
int j = 0;
LinkList p = L, s;
while (p && j < i - 1) { // 寻找第i-1个结点
p = p->next;
j++;
}
if (!p || j > i - 1) // i小于1或者大于表长
return false;
s = (LinkList)malloc(sizeof(struct LNode)); // 动态地分配内存空间,生成新结点
s->data = e; // 插入L中
s->next = p->next;
p->next = s;
return true;
}
bool Delete(LinkList L, int i, ElemType *e) {
int j = 0;
LinkList p = L, q;
while (p->next && j < i - 1) { // 寻找第i个结点,并令p指向其前驱结点
p = p->next;
j++;
}
if (!p->next || j > i - 1) // 删除位置不合理
return flase;
q = p->next; // 删除并释放结点
p->next = q->next;
*e = q->data;
free(q);
return true;
}
双向链表
有了单向链表的基础,我们就可以很好的去理解双向链表,下面我们来看一下:
它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。他弥补了单向链表的缺点,一般我们都构造双向循环链表。
动态模板
struct DuLNode {
ElemType data;
struct DuLNode *prior, *next;
} DuLNode, *DuLinkList;
//初始化
void init(DuLinkList *L) {
*L = (DuLinkList)malloc(sizeof(DuLNode));
(*L)->next = (*L)->prior = *L;
}
//判断是否为空
bool is_Empty(DuLinkList L) {
if (L->next == L && L->prior == L)
return true;
else
return false;
}
//获取长度
intLength(DuLinkList L) {
int i = 0;
DuLinkList p = L->next; // p指向第一个结点
while (p != L) { // p没到表头
i++;
p = p->next;
}
return i;
}
//查找
int Find(DuLinkList L, int i, ElemType *e) {
int j = 1; // j为索引值
DuLinkList p = L->next; // p指向第一个结点
while (p != L && j < i) { // 顺指针向后查找,直到p指向第i个元素或p指向头结点
p = p->next;
j++;
}
if (p == L || j > i) // 第i个元素不存在
return 0;
*e = p->data; // 取第i个元素
return e;
}
// L中第i个位置之前插入元素e,i的合法值为1<=i<=l.length+1,否则无法在第(length+1)个结点之前插入元素
boll Insert(DuLinkList L, int i, ElemType e) {
DuLinkList p, s;
if (i < 1 || i > ListLength(L) + 1) // i值不合法
return ERROR;
p = GetElemP(L, i - 1); // 在L中确定第i个元素前驱的位置指针p
if (!p) // p=NULL,即第i个元素的前驱不存在(设头结点为第1个元素的前驱)
return false;
s = (DuLinkList)malloc(sizeof(DuLNode));
if (!s)
return OVERFLOW;
s->data = e;
s->prior = p; // 在第i-1个元素之后插入
s->next = p->next;
p->next->prior = s;
p->next = s;
return true;
}
// 表L的第i个元素,i的合法值为1<=i<=length
bool Delete(DuLinkList L, int i, ElemType *e) {
DuLinkList p;
if (i < 1) // i值不合法
return true;
p = GetElemP(L, i); // 在L中确定第i个元素的位置指针p
if (!p) // p = NULL,即第i个元素不存在
return ERROR;
*e = p->data;
p->prior->next = p->next; // 此处并没有考虑链表头,链表尾
p->next->prior = p->prior;
free(p);//释放空间
return false;
}
静态模板
// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;
// 初始化
void init()
{
//0是左端点,1是右端点
r[0] = 1, l[1] = 0;
idx = 2;
}
// 在节点a的右边插入一个数x
void insert(int a, int x)
{
e[idx] = x;
l[idx] = a, r[idx] = r[a];
l[r[a]] = idx, r[a] = idx ++ ;
}
// 删除节点a
void remove(int a)
{
l[r[a]] = l[a];
r[l[a]] = r[a];
}
``