参考书:王道考研数据结构
(此贴为博主学习408的笔记,因博主也是学习者,个人总结如有错误欢迎指正。如有侵权请告知,马上删除致歉)
一:双链表含义
双链表是在单链表的基础上增加了一个指向前驱结点的prior指针。以此解决了单链表只可以找到后继而无法找到前驱结点的痛点问题,因为单链表只有一个指向后继的next指针,使得单链表只能从头到尾依次顺序的向后遍历。
双链表的按值查找和按位查找与单链表相同,但是增加与删除操作上与单链表不同,不仅要修改后继指针,也要修改其prior前驱指针,在此过程中应注意不可断链!!
二:双链表的插入操作演示
核心操作:
①s->next = p->next;
②p->next->prior = s;
③s->prior = p;
④p->next = s;
三:双链表删除
核心操作:
1.p->next = q->next;
2.q->next->prior = p;
四:其余操作与单链表操作基本大同小异。
#include <stdio.h>
#include <stdlib.h>
/**********************************************
制作人:祝星。
项目名称:数据结构-双链表的基本操作(C语言实现)
完成时间:2022年7月0日
完成内容:双链表的创建,修改,增加,删除,销毁。
具体内容,尾插法,按位查找,按位删除,求表长,遍历输出,销毁双链表
运行环境:win10
程序环境:VC++
文件语言:C语言
文件类型:.cpp
注:1.VC++的.cpp环境,&为取地址。
2.LNode *等价于LinkList,前者强调这是结点,后者强调这是链表,合适的地方使用合适的名字,代码可读性更高。
注意:剩余操作与单链表一致,无非是多一步操作,在next指针指向后,修改prior指针,并且保证不断链。
建议读者在学习链表等数据结构时画图理解更直观。
***********************************************/
#define Max 10
/**定义一个具有前驱指针和后继指针的双链表的结构体**/
typedef struct DNode{
int data; //数据域
struct DNode *prior,*next; //前驱指针和后继指针
}DNode,*DLinkList; //前者强调这是结点,后者强调这是链表
/**
初始化一个双链表,其前驱指针和后继指针指向NULL
除非连接为循环链表,否则prior指针永远指向NULL
**/
DLinkList InitDLinkList(DLinkList &L){
int x; //输入data值
L = (DLinkList)malloc(sizeof(DNode)); //申请链表的头结点
DNode *s,*r = L;//初始化两个结点指针指向头结点
if(L==NULL) //没有申请到内存空间则返回false
return false;
L->prior=NULL;
L->next=NULL;
printf("输入(9999结束)");
scanf("%d",&x);//输入插入的值,输入9999结束
while(x!=9999){
s = (DNode*)malloc(sizeof(DNode));//动态申请一个存放插入值的结点
s->data=x;//将输入的值赋值给新申请结点
r->next=s;//将指向头结点的r的next指针指向s
s->prior=r;//将新节点的前驱指针指向上一个
r=s;//将r的指针指向s新节点
printf("输入(9999结束)");
scanf("%d",&x);
}
r->next=NULL;//r的next指针指向NULL
return L;
}
/**
双链表的插入操作
**/
bool InsertNextDNode(DNode *p,DNode *s){
if(p==NULL || s==NULL)
return false;
s->next=p->next;
if(p->next !=NULL)
p->next->prior = s;
s->prior = p;
p->next = s;
return true;
}
/*******************************
函数名:按位查找
参数:L,i
返回值:返回结点指针值
*******************************/
DNode *GetElem(DLinkList L,int i){
int j=1;//因为有头结点,所以从1开始遍历
if(i<1){//判断查找的位置是否合法
printf("数值非法");
}
DNode *p = L->next;//初始化结点指针
while(p!=NULL&&j<i){
p=p->next;
j++;
}
return p;
}
/*******************************
函数名:按位删除结点
参数:L,i
返回值:返回结点指针值
*******************************/
DLinkList DeleteNode(DLinkList &L,int i){
DNode *p;
p=GetElem(L,i);//调用按位查找函数
if(p==NULL)
return false;
if(p->next!=NULL){
p->prior->next = p->next;//p的前驱的后继指针指向p的后继
p->next->prior=p->prior;//p的后继的前驱指针指向p的前驱
free(p); //释放
}
return L;
/*
第二种方法,和单链表一样找到P的前驱结点,也就是i-1,此方法是王道书中的删除核心操作
DNode *p,*q;
p=GetElem(L,i-1);//调用按位查找函数
if(p==NULL)
return false;
q=p->next;
if(q!=NULL){
p->next = q->next;
q->next->prior = p;
free(p); //释放
}
return L;
*/
}
/*指定结点的后插*/
DLinkList InsertNextNode(DLinkList &L,DNode *p,int e){
if(p==NULL)
return false;
DNode *s = (DNode*)malloc(sizeof(DNode));//申请不到内存空间则返回false
if(s==NULL)
return false;
s->data=e;
s->next=p->next;
p->next->prior = s;
s->prior = p;
p->next=s;
return L;
}
/*指位序后插入*/
DLinkList InsertNNode(DLinkList &L,int i,int e)
{
if(i<1)
return false;
DNode *s;
int j=0;
s=L;
while(s!=NULL&&j<i)
{
s=s->next;
j++;
}
if(s==NULL)
return false;
return InsertNextNode(L,s,e);
}
/*求表长*/
int Length(DLinkList L)
{
int len=0;
DNode *p=L;
while (p->next!=NULL)
{
p=p->next;
len++;
}
return len;
}
/*遍历输出*/
void Show(DLinkList L){
printf("当前单链表的所有元素:【head】<=>");
DNode *p;
p =L->next;
while(p!=NULL){
printf("[%d]<=>",p->data);
p=p->next;
}
printf("NULL");
printf("\n");
printf("当前表长为%d\n",Length(L));
printf("-----------------------------------------------------------\n");
}
/***
彻底销毁链表各个节点,并将销毁后的链表指向null
*/
void DestroyList(DLinkList &L)
{
DNode *p ,*q;//初始化两个指针,p用来free释放,q用来在p释放后将p指针指向下一个
p= L->next;//删除带头链表头结点不删除
q=p;
while(p!=NULL){
q=p->next; //提前将q指向下一个
free(p); //释放p
p = q; //将p指向q所指向的下一个
}
L->next = NULL; //头结点指向NULL
printf("\n销毁成功\n");
}
int main(){
DLinkList(l);
l = InitDLinkList(l);
Show(l);
DeleteNode(l,3);
printf("删除第三个位置的元素后:");
Show(l);
printf("在第二个位置后插入99:");
InsertNNode(l,2,99);
Show(l);
return 0;
}