双链表的相关操作
都是平时自己的练习和想法,如果有一些错误请指出我会加以改正。
一个正在成长的爱好者,会继续加以努力
单链表:每一个节点由(数据域+指针域)可便利对节点进行查询。如果查询节点前一个节点的数据就会十分的不便利,因为单链表中每个节点的 指针是指向下一个节点的数据域。所以单链表对于数据的回查是不方便的
双链表:和单链表一样节点由(数据域+指针域)但是不同于单链表的地方在于双链表每个节点同时有指向下一个节点和上一个节点的指针,不论是查询一个节点的前驱和后驱元素都十分的便利
所以对于元素的检索双链表更具有优势
1>定义双链表
#include<stdio.h>
typedef struct DNode
{
int data;//存放每个节点的数据
int *prior, *next;//一个指向前驱节点的指针,一个指向后驱节点的指针
}DNode,*DLinkList;//对这个结构体重新命名为Dnode,并用DLinkList指向这个结构体
void main()
{
DLinkList L;//创建一个双链表
}
DNode,*DLinkLis和单链表一样使用了typedef对这个结构体重新命名为Dnode,并用DLinkList指向这个结构体(typedef的含义和作用我已在单链表帖子中已经说明)
两个指针*prior(adj:先前的; 较早的; 在前的; 优先的; 占先的; 较重要的; 在前面的;这样定义具有较高的可读性已经实用性,*next根据含义就可以知道是指向下一个节点的指针
在主函数main中声明了一个指向头节点的指针L
2>初始化双链表
带头节点初始化
在进行初始之前应该先为头节点进行分配空间,如何同时应该有一个判断是否分配成功
分析:在一开始双链表应该为空表,表中头节点只有后驱节点没有前驱节点,同样末尾节点只有后驱节点没有前驱节点
则由此可知:(L->prior = NULL;//头节点是没有前驱元素的 L->next = NULL;//初始时双链表为空表)
void InitDLinkList(DLinkList&L)//带头节点
{
L = (DNode*)malloc(sizeof(DNode));//为头节点分配空间
if (L == NULL)
{
printf("抱歉为空,分配失败!");
}
else {
L->prior = NULL;//头节点是没有前驱元素的
L->next = NULL;//初始时双链表为空表
printf("初始化成功!\n");
}
}
3>对双链表添加数数据
类似于单链表,对双链表的添加数据同样需要两个指针节点一个去保存 当前节点的数据,另外一个去指向一个节点的位置
初始状态下双链表的表头为空同时没有前驱元素
D->next = P->next;//定位到下一个节点
P->next =D;//D现在为第二个节点
D->prior = P;//第二个节点指向前驱节点
P = D;//D成为第一个节点
在给D分配空间之后就可以对D->data的数据域进行赋值,如何将D节点尾指针指向头节点的尾指针(头指针也就是P,前指针为空),之后可以将D作为头指针后的第一个节点如何将D的前指针指向头指针P。最后P=D,第一个节点的数据填充完毕
void CreatDLinkList(DLinkList &L)//双链表输入数据
{
DNode *D, *P;
L=(DNode*)malloc(sizeof(DNode));//为头节点分配空间
L->next = NULL;//初始状态下双链表为空表
P = L;//现在P就是头节点
P->prior = NULL;//头节点的前驱是为空的
int n;//输入数据的数量
printf("请输入双链表元素的数量:");
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
D = (DLinkList)malloc(sizeof(DNode));//为存储数据的指针分配空间
printf("data[%d]=",i);
scanf("%d",&D->data);//第一个节点的数据已经存储到D中
D->next = P->next;//定位到下一个节点
P->next =D;//D现在为第二个节点
D->prior = P;//第二个节点指向前驱节点
P = D;//D成为第一个节点
}
}
4>双链表长度的获取
主要是定义一个变量去统计数据数量
p=l->next定义一个指向头节点后第一个节点的指针,然后去遍历双链表非空就继续循环下去
void GetLength(DLinkList L)//获取双链表长度
{
DNode*P;//开辟一个节点
P = (DNode*)malloc(sizeof(DNode));//分配空间
P = L->next;
int length = 0;
while(P!=NULL)
{
length++;
P = P->next;
}
printf("双链表的长度为%d\n",length);
}
5>双链表的插入(按位)
尽管双链表每个节点拥有前后两个指针但是插入原来还是一样,不管是后插法还是前插法我们只需要首先找到第i-1(第i)个元素然后进行链接即可
和单链表不同双链表的插入要多几步,是关于前指针的链接
把思路理顺会很容易理解,建议用纸笔边画边写(可以类比单链表)
主要是要思考如果第i-1是尾节点,如果是尾节点则(P->next->prior = S)中P->next就会为NULL。这样就无法得到想要的结果
void InsertDLinkList(DLinkList &L, int i,int e)//插入有头节点
{
GetLength(L);
if (i < 1)
{
printf("插入位置不合法");
}
else {
DNode* P;//查询的数据的节点(遍历节点)
int number = 0;//记录位序
P = L;//P指向头节点,现在P就是头节点
while (P != NULL && number < i - 1)
{
P = P->next;//找到第i-1个节点位置
number++;
}
if(P==NULL)
{
printf("插入失败,插入前驱节点数据异常");
}
else {
DNode *S = (DNode*)malloc(sizeof(DNode));//保持插入数据的节点
S->data = e;//接收插入数据大S节点
S->next = P->next;//将插入元素的指针指向第i个节点
if (P->next!=NULL)//这里主要考虑的是最后一个节点
{
P->next->prior = S;//P->next代表第i个节点,P->next->prior代表第i个节点的前指针
}
S->prior = P;//再将S的前指针指向第i-1个节点
P->next = S;//最后将第i-1个节点的后指针指向插入节点
printf("插入数据%d到第%d个节点完成",e,i);
}
}
}
6>双链表的删除
我所写的是基本按位删除,所以看起来比较臃肿这样书写的目的也是便于去理解其中的逻辑关系(和单链表相同先找到第i-1个节点-->然后改变指针指向-->释放掉空间节点-->完成操作)
对于i-1节点和i节点的判断是有必要的:如果i-1已经是尾节点那么可以报错i不存在。同理如果p->next(等价于第i节点)为空时说明删除的节点就是尾节点,这样就可以直接修改i-1节点后指针为NULL即可然后释放掉i节点。最后的情况下i不是尾节点则按正常修改指针即可
void DeletDLinkList(DLinkList&L,int i)//删除节点i(按位)
{
if (i < 0)
printf("抱歉删除位置不合法!\n");
DNode* P;//查询的数据的节点(遍历节点)
int number = 0;//记录位序
P = L;//P指向头节点,现在P就是头节点
while (P != NULL && number < i - 1)
{
P = P->next;//找到第i-1个节点位置
number++;
}
if (P == NULL)
{
printf("数据为空,删除失败");//如果i-1节点已经位空,则i节点一定为空
}
else {
DNode *S = (DNode*)malloc(sizeof(DNode));//定义删除数据的节点
S = P->next;//S现在代表就是第i节点
if (S == NULL)//如果i-1是尾节点
{
printf("删除失败,删除节点位空");
}
else {
P->next = S->next;//将i-1节点的后指针指向i+1节点
if (S->next != NULL)//说明i不是最后一个节点
{
S->next->prior = P;
free(S);//释放掉节点s
}
printf("删除第%d个节点成功",i);
}
}
}
7>双链表的查找(按位/按值)
和单链表一样查找到的本质就是遍历
不管是for循环还是while循环主要其内部循环条件设置:假设前提条件已经定了有一个指针节点P同时指向了L->next
遍历:只要设置P!=null这样就可以遍历整个双链表
按位:按位和遍历的区别在于设置一个计数变量即可(int i=0-->i=查询节点的位置)即可
按值:只要同时结合按位和遍历即可,对当前节点进行逐一对比即可
void GetElem(DLinkList L, int i)//按位查找
{
if (i < 0)
printf("查询位置不合法");
DNode *P;//定义一个指针,代表每个节点的数据
P = L->next;//P指向头节点后的第一个节点
int number=1;//计数变量
while (P != NULL&&number<i)
{
number++;
P = P->next;//继续向下一个节点移动
}
printf("查询第%d个元素为%d",i,P->data);
}
按值查找
void GetELem_value(DLinkList L, int e)//按值查找
{
DNode *P;//定义一个指针,代表每个节点的数据
P = L->next;//P指向头节点后的第一个节点
int number = 1;//计数变量
while (P != NULL && P->data!=e)
{
number++;
P = P->next;//继续向下一个节点移动
}
if (P = NULL)//跳出循环说明双链表中不含该数据
{
printf("抱歉双链表中没有该数据\n");
}
printf("双链表中含有数据%d个位置为%d\n",e,number);
}
所以综上按位和按值实质上是一样的
8>整体代码
#include<stdio.h>
#include<stdlib.h>
typedef struct DNode
{
int data;//存放每个节点的数据
struct DNode *prior, *next;//一个指向前驱节点的指针,一个指向后驱节点的指针
}DNode,*DLinkList;//对这个结构体重新命名为Dnode,并用DLinkList指向这个结构体
void InitDLinkList(DLinkList&L)//带头节点
{
L = (DNode*)malloc(sizeof(DNode));//为头节点分配空间
if (L == NULL)
{
printf("抱歉为空,分配失败!");
}
else {
L->prior = NULL;//头节点是没有前驱元素的
L->next = NULL;//初始时双链表为空表
printf("初始化成功!\n");
}
}
void CreatDLinkList(DLinkList &L)//双链表输入数据
{
DNode *D, *P;
L=(DNode*)malloc(sizeof(DNode));//为头节点分配空间
L->next = NULL;//初始状态下双链表为空表
L->next = NULL;//头节点的前驱是为空的
P = L;//现在P就是头节点
int n;//输入数据的数量
printf("请输入双链表元素的数量:");
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
D = (DLinkList)malloc(sizeof(DNode));//为存储数据的指针分配空间
printf("data[%d]=",i);
scanf("%d",&D->data);//第一个节点的数据已经存储到D中
D->next = P->next;//定位到下一个节点
P->next =D;//D现在为第二个节点
D->prior = P;//第二个节点指向前驱节点
P = D;//D成为第一个节点
}
}
void GetLength(DLinkList L)//获取双链表长度
{
DNode*P;//开辟一个节点
P = (DNode*)malloc(sizeof(DNode));//分配空间
P = L->next;
int length = 0;
while(P!=NULL)
{
length++;
P = P->next;
}
printf("\n双链表的长度为%d\n",length);
}
void InsertDLinkList(DLinkList &L, int i,int e)//插入有头节点
{
GetLength(L);
if (i < 1)
{
printf("插入位置不合法");
}
else {
DNode* P;//查询的数据的节点(遍历节点)
int number = 0;//记录位序
P = L;//P指向头节点,现在P就是头节点
while (P != NULL && number < i - 1)
{
P = P->next;//找到第i-1个节点位置
number++;
}
if(P==NULL)
{
printf("插入失败,插入前驱节点数据异常");
}
else {
DNode *S = (DNode*)malloc(sizeof(DNode));//定义插入数据的节点
S->data = e;//接收插入数据大S节点
S->next = P->next;//将插入元素的指针指向第i个节点
if (P->next!=NULL)//这里主要考虑的是最后一个节点
{
P->next->prior = S;//P->next代表第i个节点,P->next->prior代表第i个节点的前指针
}
S->prior = P;//再将S的前指针指向第i-1个节点
P->next = S;//最后将第i-1个节点的后指针指向插入节点
printf("插入数据%d到第%d个节点完成",e,i);
}
}
}
void DeletDLinkList(DLinkList&L,int i)//删除节点i(按位)
{
if (i < 0)
printf("抱歉删除位置不合法!\n");
DNode* P;//查询的数据的节点(遍历节点)
int number = 0;//记录位序
P = L;//P指向头节点,现在P就是头节点
while (P != NULL && number < i - 1)
{
P = P->next;//找到第i-1个节点位置
number++;
}
if (P == NULL)
{
printf("数据为空,删除失败");//如果i-1节点已经位空,则i节点一定为空
}
else {
DNode *S = (DNode*)malloc(sizeof(DNode));//定义删除数据的节点
S = P->next;//S现在代表就是第i节点
if (S == NULL)//如果i-1是尾节点
{
printf("删除失败,删除节点位空");
}
else {
P->next = S->next;//将i-1节点的后指针指向i+1节点
if (S->next != NULL)//i不是最后一个节点
{
S->next->prior = P;
free(S);//释放掉节点s
}
printf("删除第%d个节点成功",i);
}
}
}
void GetElem(DLinkList L, int i)//按位查找
{
if (i < 0)
printf("查询位置不合法");
DNode *P;//定义一个指针,代表每个节点的数据
P = L->next;//P指向头节点后的第一个节点
int number=1;//计数变量
while (P != NULL&&number<i)
{
number++;
P = P->next;//继续向下一个节点移动
}
printf("查询第%d个元素为%d",i,P->data);
}
void GetELem_value(DLinkList L, int e)//按值查找
{
DNode *P;//定义一个指针,代表每个节点的数据
P = L->next;//P指向头节点后的第一个节点
int number = 1;//计数变量
while (P != NULL && P->data!=e)
{
number++;
P = P->next;//继续向下一个节点移动
}
if (P = NULL)//跳出循环说明双链表中不含该数据
{
printf("抱歉双链表中没有该数据\n");
}
printf("双链表中含有数据%d个位置为%d\n",e,number);
}
void ShowDLinkList(DLinkList L)//打印双链表
{
DNode *P;//定义一个指针,代表每个节点的数据
P = (DNode*)malloc(sizeof(DNode));//为节点分配空间
P = L->next;//P指向头节点后的第一个节点
int i=0;//记录位序
printf("打印双链表\n");
while (P!=NULL)
{
printf("data[%d]=%d\n",i,P->data);
i++;
P = P->next;//继续向下一个节点移动
}
}
void main()
{
DLinkList L;//创建一个双链表
InitDLinkList(L);//初始化
CreatDLinkList(L);//输入数据
InsertDLinkList(L,5,99);//插入元素按位
DeletDLinkList(L,5);//删除节点按位
GetLength(L);//获取双链表长度
GetElem(L,5);//按位查询
GetELem_value(L,45);//按值查找
ShowDLinkList(L);//打印双链表
}