一、双向链表的定义及基本操作
单链表中的结点只有一个指向其后继的指针,使得单链表要访问某个结点的前驱结点时,只能从头开始遍历,而双向链表的话 可以直接访问结点的后继结点和前驱结点。
双链表的结点中有两个指针prior和next,分别指向前驱结点和后继结点。
- 定义双向链表
//定义双链表
typedef struct DoubleLNode{
Elem data;//数据域
struct DoubleLNode *prior;//前驱指针
struct DoubleLNode *next;//后继指针
}DLN;
- 初始化双向链表
//初始化链表
void InitLNode(DLN *&L){
L=(DLN *)malloc(sizeof(DLN));//带头节点的链表
L->prior=NULL;
L->next=NULL;
}
- 创建双向链表
创建链表:头插法和尾插法
//尾插法
void CreateLNodeW(DLN *&L,Elem a[],int n){
DLN *s,*p=L;
for(int i=0;i<n;i++){
s=(DLN *)malloc(sizeof(DLN));
s->data=a[i];
if(p->next==NULL){//头结点的后继结点为空的时侯
s->next = L;// s的后继结点为头结点H
s->prior = L;//s的前驱结点为头结点H
L->next = s;//头结点H的后继结点为s
L->prior=s;//头结点H的前驱结点为s
}
else{//在最后插入
//最后一个结点为p->prior(头结点的前驱)
p->prior->next=s; //
s->prior=p->prior;
s->next=p;
p->prior=s;
}
}
}
//头插法
void CreateLNodeH(DLN *&L,Elem a[],int n){
DLN *s;
for(int i=0;i<n;i++){
s=(DLN *)malloc(sizeof(DLN));
s->data=a[i];
if(L->next == NULL){//头结点的后继结点为空的时侯
s->next = L;//s的后继结点为头结点H
s->prior = L;//s的前驱结点为头结点H
L->next = s;//头结点H的后继结点为s
L->prior=s;//头结点H的前驱结点为s
}else{
s->next =L->next;
L->next->prior = s;
s->prior = L;
L->next = s;
}
}
}
- 判空(判断双向链表是否为空)
//判空
bool EmpyLNod(DLN *L){
if(L->next==NULL || L->prior==NULL) return 1;
return 0;
}
- 插入操作
插入操作分为:结点之前和结点之后插入。
结点之后插入:
//在p结点之后插入s结点
bool InsertDNodeH(DLN *&L,Elem a,Elem b){// a是要插入的前一个结点,b 是我们想插入的值 。插入在 p之后
DLN *s,*p=L->next;
s=(DLN *)malloc(sizeof(DLN));
while(p->next!=L){//先找到a值的 结点,才好插入b值
if(p->data==a){
break;//此时p指向所插入结点的前一个结点
}
p=p->next;
}
if(p->next==L) return 0;//表示删除元素不在链表当中,返回 0 (false)
//break 之后:此时p指向所删除的结点
s->data=b; // s结点 为插入的结点
s->next=p->next; // s后继结点 为 p->next(p的后继结点)。
p->next->prior=s; //p的后继结点 的前驱为 s 结点
s->prior=p; //s结点的前驱为p结点
p->next=s; //p的后继结点为s
return 1;
}
结点之前插入:
//在p结点之前插入s结点
bool InsertDNodeQ(DLN *&L,Elem a,Elem b){// a是要插入的前一个结点,b 是我们想插入结点的值 。插入在 p之前
DLN *s,*p=L->next;
s=(DLN *)malloc(sizeof(DLN));
while(p->next!=L){ //先找到a值的 结点,才好插入b值
if(p->data==a){
break; //此时p指向所插入结点的后一个结点
}
p=p->next;
}
if(p->next==L) return 0; //表示删除元素不在链表当中,返回 0 (false)
//break 之后:此时p指向所插入结点的前一个结点
s->data=b; // s结点 为插入的结点
p->prior->next=s; //p->prior(p结点的前驱结点) 的后继结点为 s 结点
s->prior=p->prior; //s的前驱为 p->prior
s->next=p; // s后继结点 为 p结点
p->prior=s;// p的前驱为s
return 1;
}
- 删除操作
删除成功返回true,删除失败返回flase。
//删除p结点
bool DelDNode(DLN *&L,Elem a){// a 是我们想删除的值
DLN *p=L->next;
if(L->next==NULL || L->prior==NULL) return 0;
while(p->next!=L){
if(p->data==a){
break;//此时p指向所删除的结点
}
p=p->next;
}
if(p->next==L) return 0;//表示删除元素不在链表当中,返回 0 (false)
//break 之后:此时p指向所删除的结点
p->prior->next=p->next;
free(p);
return 1;
}
- 输出链表所有元素
//输出链表所有的元素
void DispLNode(DLN *L){//输出链表所有的元素
DLN *p=L->next;
while(p->next!=L){
printf("%c ",p->data);
p=p->next;
}
printf("%c ",p->data);
printf("\n");
}
全部代码(包含主函数main() ):
```c
#include<stdio.h>
#include<malloc.h>
typedef char Elem;
//定义双链表
typedef struct DoubleLNode{
Elem data;//数据域
struct DoubleLNode *prior;//前驱指针
struct DoubleLNode *next;//后继指针
}DLN;
//初始化链表
void InitLNode(DLN *&L){
L=(DLN *)malloc(sizeof(DLN));//带头节点的链表
L->prior=NULL;
L->next=NULL;
}
//创建链表
void CreateLNodeW(DLN *&L,Elem a[],int n){//尾插法
DLN *s,*p=L;
for(int i=0;i<n;i++){
s=(DLN *)malloc(sizeof(DLN));
s->data=a[i];
if(p->next==NULL){//头结点的后继结点为空的时侯
s->next = L;// s的后继结点为头结点H
s->prior = L;//s的前驱结点为头结点H
L->next = s;//头结点H的后继结点为s
L->prior=s;//头结点H的前驱结点为s
}
else{//在最后插入
//最后一个结点为p->prior(头结点的前驱)
p->prior->next=s; //
s->prior=p->prior;
s->next=p;
p->prior=s;
}
}
}
void CreateLNodeH(DLN *&L,Elem a[],int n){//头插法
DLN *s;
for(int i=0;i<n;i++){
s=(DLN *)malloc(sizeof(DLN));
s->data=a[i];
if(L->next == NULL){//头结点的后继结点为空的时侯
s->next = L;//s的后继结点为头结点H
s->prior = L;//s的前驱结点为头结点H
L->next = s;//头结点H的后继结点为s
L->prior=s;//头结点H的前驱结点为s
}else{
s->next =L->next;
L->next->prior = s;
s->prior = L;
L->next = s;
}
}
}
//判空
bool EmpyLNod(DLN *L){
if(L->next==NULL || L->prior==NULL) return 1;
return 0;
}
//输出链表所有的元素
void DispLNode(DLN *L){//输出链表所有的元素
DLN *p=L->next;
while(p->next!=L){
printf("%c ",p->data);
p=p->next;
}
printf("%c ",p->data);
printf("\n");
}
//删除p结点
bool DelDNode(DLN *&L,Elem a){// a 是我们想删除的值
DLN *p=L->next;
if(L->next==NULL || L->prior==NULL) return 0;
while(p->next!=L){
if(p->data==a){
break;//此时p指向所删除的结点
}
p=p->next;
}
if(p->next==L) return 0;//表示删除元素不在链表当中,返回 0 (false)
//break 之后:此时p指向所删除的结点
p->prior->next=p->next;
free(p);
return 1;
}
//在p结点之后插入s结点
bool InsertDNodeH(DLN *&L,Elem a,Elem b){// a是要插入的前一个结点,b 是我们想插入的值 。插入在 p之后
DLN *s,*p=L->next;
s=(DLN *)malloc(sizeof(DLN));
while(p->next!=L){//先找到a值的 结点,才好插入b值
if(p->data==a){
break;//此时p指向所插入结点的前一个结点
}
p=p->next;
}
if(p->next==L) return 0;//表示删除元素不在链表当中,返回 0 (false)
//break 之后:此时p指向所删除的结点
s->data=b; // s结点 为插入的结点
s->next=p->next; // s后继结点 为 p->next(p的后继结点)。
p->next->prior=s; //p的后继结点 的前驱为 s 结点
s->prior=p; //s结点的前驱为p结点
p->next=s; //p的后继结点为s
return 1;
}
//在p结点之前插入s结点
bool InsertDNodeQ(DLN *&L,Elem a,Elem b){// a是要插入的前一个结点,b 是我们想插入结点的值 。插入在 p之前
DLN *s,*p=L->next;
s=(DLN *)malloc(sizeof(DLN));
while(p->next!=L){ //先找到a值的 结点,才好插入b值
if(p->data==a){
break; //此时p指向所插入结点的后一个结点
}
p=p->next;
}
if(p->next==L) return 0; //表示删除元素不在链表当中,返回 0 (false)
//break 之后:此时p指向所插入结点的前一个结点
s->data=b; // s结点 为插入的结点
p->prior->next=s; //p->prior(p结点的前驱结点) 的后继结点为 s 结点
s->prior=p->prior; //s的前驱为 p->prior
s->next=p; // s后继结点 为 p结点
p->prior=s;// p的前驱为s
return 1;
}
int main(){
DLN *L;
InitLNode(L);
Elem a[]={'a','b','c','d'};
CreateLNodeW(L,a,4); DispLNode(L);
DelDNode(L,'a'); DispLNode(L);
printf("输出1 表示插入成功 输出0 表示插入失败:%d\n",InsertDNodeH(L,'b','e')); DispLNode(L);
printf("输出1 表示插入成功 输出0 表示插入失败:%d\n",InsertDNodeH(L,'b','f')); DispLNode(L);
return 0;
}
二、总结反思:
双链表与单链表在某些操作上大同小异