一、释意
1.双链表
单链表中只有一个指向后继的指针 从前一个结点找到后一个结点很容易,但是从后面的结点想去找前一个结点就只能从来依次遍历了,所以双链表就是单链表的进化版。多设一个指针域指向前一个结点 这样拿到一个结点找其前驱和后继时间复杂度都是O(1)了。如图:
2.循环单链表
循环单链表与单链表的唯一区别就是循环单链表的最后一个结点不是NULL,而是指向头结点的(表中无指针域为空的结点),判断循环单链表的判空条件不是头结点的next指针是否为空,而是它是否等于头指针(next域又指向自己)图例:
判空图例:
循环单链表的插入删除算法与单链表几乎一样,但是单链表只能从表头结点开始往后顺序遍历整个单链表,而循环链表可以从表中任何一个结点开始遍历整个链表 所以对于循环单链表可以只设一个尾指针 让操作效率更高,因为若设头指针则在对表尾插入元素是时间复杂度为O(n),表头是O(1),但是在有尾指针的情况下 表头插入和表尾插入都是O(1);
3.循环双链表
循环双链表又是循环单链表的升级版,最后一个结点的next指针不为NULL,而是指向头结点。如果所示:
同上,对于循环双链表,当循环双链表为空表时,其头结点的prior域和next域都指向头结点
二、基本操作
1.双链表的定义
#include<stdio.h>
#include<stdlib.h>
typedef struct DNode{
int data;
struct DNode *prior; //这里是指针 定义一个指针类型区域 prior就指向相同数据类型的节点
struct DNode *next;
}DNode,*DLinkList;
双链表在单链表的基础上多加了一个prior指针,指向其前驱。因此双链表的按值查找和按位查找和单链表是相同的,但是插入和删除与单链表不同 因为插入删除还要兼顾到prior指针(要保证插入和删除后prior指针依然指向前驱)
2.双链表初始化(带头结点)
//双链表初始化 (带头节点)
bool InitDLinkList(DLinkList &L){
L=(DNode*)malloc(sizeof(DNode)); //分配一个头节点
if(L==NULL){
return false; //内存不足分配失败
}
L->prior=NULL; //头节点的prior永远指向空
L->next=NULL; //头节点之后还没有节点
return true;
}
初始化后头结点的前驱和后继都指向NULL
3.判空操作
//判空操作
bool Empty(DLinkList L){
if(L->next==NULL){
return true;
}
return false;
}
直接判断头结点的next指针是否为空即可
4.双链表插入
//插入操作 在p节点之后插入s节点 (没有讨论p就是尾结点的情况->直接插入到尾部 然后令新节点的next为空即可)
bool InsertNextNode(DNode *p,DNode *s){
s->prior=p;
s->next->prior=s;
s->next=p->next;
p->next=s;
}
指定结点进行后插 图例:只需修改指针即可 但是注意其中语句交换顺序!!
上面的代码不是唯一的,但也不可以任意排序 思考:第一步和第二步是不是必须在第四步之前?否则*p的后继节点的指针就会丢掉,插入失败。建议做题时画图 切不可死记
5.双链表删除
//删除操作 删除p节点之后的节点
bool DeleteNextDNode(DNode *p){
if(p==NULL){
return false;
}
DNode *q=(DNode*)malloc(sizeof(DNode)); //用来保存被释放的结点
q=p->next;
if(q==NULL || q->next==NULL){
return false;
}
q->next->prior=p;
p->next=q->next;
free(q);
}
三、完整代码
#include<stdio.h>
#include<stdlib.h>
typedef struct DNode{
int data;
struct DNode *prior; //这里是指针 定义一个指针类型区域 prior就指向相同数据类型的节点
struct DNode *next;
}DNode,*DLinkList;
//双链表初始化 (带头节点)
bool InitDLinkList(DLinkList &L){
L=(DNode*)malloc(sizeof(DNode)); //分配一个头节点
if(L==NULL){
return false; //内存不足分配失败
}
L->prior=NULL; //头节点的prior永远指向空
L->next=NULL; //头节点之后还没有节点
return true;
}
//判空操作
bool Empty(DLinkList L){
if(L->next==NULL){
return true;
}
return false;
}
//插入操作 在p节点之后插入s节点 (没有讨论p就是尾结点的情况->直接插入到尾部 然后令新节点的next为空即可)
bool InsertNextNode(DNode *p,DNode *s){
s->prior=p;
s->next->prior=s;
s->next=p->next;
p->next=s;
}
//删除操作 删除p节点之后的节点
bool DeleteNextDNode(DNode *p){
if(p==NULL){
return false;
}
DNode *q=(DNode*)malloc(sizeof(DNode)); //用来保存被释放的结点
q=p->next;
if(q==NULL || q->next==NULL){
return false;
}
q->next->prior=p;
p->next=q->next;
free(q);
}
int main(){
}