先导
在写双链表之前,先复习一下线性表和链表的概念和特点。
线性表:由多个同一数据类型组成的线性结构,数据间具有一对一的关系。
链表:链式存储的线性表,表中的元素物理地址不连续,逻辑指针连续,逻辑上具有一一对应的关系。
一、双链表的构成
双链表中的元素(结点)由前向指针域、后向指针域和数据域组成,通过前向指针域和后向指针域将各个元素(结点)链接在一起,从而实现链表的双向链接。
链表中的数据域分为头结点数据域和普通结点数据域,二者之间的区别是头结点数据域不存储任何数据只放链表的长度,普通结点数据域存储数据。
链表中的头结点除了起到标识作用外,不在起任何作用。也就是说头结点只是为了告诉你这是一个链表,不参与对数据的增添删改。对于前面所说的记录链表长度,其实是可以不要的,只是人为的想让它记录一下。若还不能理解,可以将其看做火车的司机车厢,在火车上司机车厢只负责将整条列车开往目的,其他一概不过问。
前向指针域:存储前一个结点的首地址
后向指针域:存储后一个结点的首地址
数据域:存储数据(普通结点)和链表的长度(头结点)
二、双链表的链接示意图
链表中元素的物理地址不是连续的,需要使用指针将各个元素串联起来,进而实现逻辑上的连续,双链表也不例外。双链表中的pri和next都是指针,分别存放前一个结点的首地址和后一个结点的首地址,因此在链接的过程中实际上指向结点的首地址(如上图所示)。链接结点首地址相当于链上整个结点,故在链接的时候pri和next可以直接指向结点,但不能忘记址本质是指向结点的首地。另外,在链接时,头结点的pri和最后一个结点的next需要指向NULL。
三、双链表结构体的定义
typedef int datatype;
typedef struct node
{
union
{
datatype data;//普通结点数据域
int len;//头结点数据域
};
struct node* pri;//前向指针域
struct node* next;//后向指针域
}node,*node_p;
四、双链表的创建
头结点的初始化需要在堆区申请空间,申请完毕后需要返回其首地址,告诉计算机这片地址是头结点。因此,函数的数据类型是指针类型。头结点的前向指针域和后向指针域的初始化不能省略。
/*---------------双链表的创建---------------*/
node_p create_double_link_list(void)
{
node_p Head_node=(node_p)malloc(sizeof(node));
if(Head_node==NULL)
{
printf("空间申请失败\n");
return NULL;
}
Head_node->len=0;//头结点数据域的初始化
Head_node->pri=NULL;//头结点前向指针域的初始化
Head_node->next=NULL;//头结点后向指针域的初始化
return Head_node;
}
五、双链表新节点的创建
新结点的初始化需要在堆区申请空间,申请完毕后需要返回其首地址,告诉计算机这片地址是新结点。因此,函数的数据类型是指针类型。新节点的前向指针域和后向指针域的初始化可以省略。
/*------------双链表新节点的创建------------*/
node_p create_new_node_double_link_list(datatype newdata)
{
node_p New_node=(node_p)malloc(sizeof(node));
if(New_node==NULL)
{
printf("空间申请失败\n");
return NULL;
}
New_node->data=newdata;//普通结点数据域的初始化
New_node->pri=NULL;//普通结点前向指针域的初始化
New_node->next=NULL;//普通结点后向指针域的初始化
return New_node;
}
六、双链表的判空
双链表的判空,即判断双链表是不是只有头结点。当双链表只有头结点时,其pri和next指针域都指向NULL,故将其作为空的条件进行判断。判断完毕后,需要返回一个数值告诉我们是否为空,这里需要用到三目运算符“?:”。
/*---------------双链表的判空---------------*/
int empty_double_link_list(node_p Head_node)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return -1;// 这种情况的返回值要往负数写,不要往正数写,正数返回值大多是需要用到的
}
return Head_node->pri==NULL&&Head_node->next==NULL?1:0;
}
七、双链表的输出
双链表的输出和单链表一样,将结点依次后移,逐个输出即可。
/*---------------双链表的输出---------------*/
void show_double_link_list(node_p Head_node)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return;
}
if(empty_double_link_list(Head_node))
{
printf("双链表已空,无法输出\n");
return;
}
node_p Show_node=Head_node->next;
while(Show_node!=NULL)
{
printf("%d-->",Show_node->data);
Show_node=Show_node->next;
}
printf("NULL\n");//人为的加上NULL,是为了让输出看起来更加完整
}
八、双链表的头插
双链表的头插有两种情况:第一个结点存在和第一个结点不存在
第一个结点存在(如下图所示):
第一步,将新结点的next指针域指向原链表第一个结点的首地址。
第二步,将原链表第一个结点的pri指针域指向新结点的首地址。
第三步,将头结点的next指针域指向新结点的首地址。
第四部,将新结点的pri指针域指向头结点的首地址。
注意:第二步和第三步不能交换位置,若交换会导致新结点的pri指针域连上它自己,从而导致双链表不会链接成功。
第一个结点不存在 (如下图所示):
第一步,将新结点的next指针域指向NULL。
第二步,将头结点的next指针域指向新结点的首地址。
第三步,将新结点的pri指针域指向头结点的首地址。
注意:第二种情况的头插与第一情况的头插相比,少了第一种情况的第二步。故在写程序时,只需添加一个if判断即可。
/*---------------双链表的头插---------------*/
void insert_head_double_link_list(node_p Head_node,datatype newdata)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return;
}
node_p New_node=create_new_node_double_link_list(newdata);
New_node->next=Head_node->next;
if(Head_node->next!=NULL)
{
Head_node->next->pri=New_node;
}
Head_node->next=New_node;
New_node->pri=Head_node;
Head_node->len++;
}
九、双链表的头删
/*---------------双链表的头删---------------*/
void delete_head_double_link_list(node_p Head_node)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return;
}
if(empty_double_link_list(Head_node))
{
printf("双链表已空,无法删除\n");
return;
}
node_p Delete_node=Head_node->next;
Head_node->next=Delete_node->next;
if(Delete_node->next!=NULL)
{
Delete_node->next->pri=Head_node;
}
Delete_node->next=NULL;
Delete_node->pri=NULL;
free(Delete_node);
Head_node->len--;
}
十、双链表的尾插
/*---------------双链表的尾插---------------*/
void insert_tail_double_link_list(node_p Head_node,datatype newdata)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return;
}
node_p Tail_node=Head_node;
while(Tail_node->next!=NULL)
{
Tail_node=Tail_node->next;
}
node_p New_node=create_new_node_double_link_list(newdata);
New_node->next=NULL;
Tail_node->next=New_node;
New_node->pri=Tail_node;
Head_node->len++;
}
十一、双链表的尾删
/*---------------双链表的尾删---------------*/
void delete_tail_double_link_list(node_p Head_node)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return;
}
if(empty_double_link_list(Head_node))
{
printf("双链表已空,无法删除\n");
return;
}
node_p Penultimate_node=Head_node;
while(Penultimate_node->next->next!=NULL)
{
Penultimate_node=Penultimate_node->next;
}
node_p Delete_node=Penultimate_node->next;
Penultimate_node->next=NULL;
Delete_node->pri=NULL;
free(Delete_node);
Head_node->len--;
}
十二、双链表的按位置插入
/*------------双链表的按位置插入------------*/
void insert_pos_double_link_list(node_p Head_node,int pos,datatype newdata)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return;
}
if(pos<1||pos>Head_node->len+1)
{
printf("超出双链表范围,无法插入\n");
return;
}
int i;
node_p New_node=create_new_node_double_link_list(newdata);
node_p Pos_node=Head_node;
for(i=1;i<pos;i++)
{
Pos_node=Pos_node->next;
}
New_node->next=Pos_node->next;
if(Pos_node->next!=NULL)
{
Pos_node->next->pri=New_node;
}
Pos_node->next=New_node;
New_node->pri=Pos_node;
Head_node->len++;
}
十三、双链表的按位置删除
/*------------双链表的按位置删除------------*/
void delete_pos_double_link_list(node_p Head_node,int pos)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return;
}
if(empty_double_link_list(Head_node))
{
printf("双链表已空,无法输出\n");
return;
}
if(pos<1||pos>Head_node->len)
{
printf("超出双链表范围,无法删除\n");
return;
}
int i;
node_p Pos_node=Head_node;
for(i=1;i<pos;i++)
{
Pos_node=Pos_node->next;
}
node_p Delete_node=Pos_node->next;
Pos_node->next=Delete_node->next;
if(Delete_node->next!=NULL)
{
Delete_node->next->pri=Pos_node;
}
Delete_node->next=NULL;
Delete_node->pri=NULL;
free(Delete_node);
Head_node->len--;
}
十四、双链表的按值查找
/*-------------双链表的按值查找-------------*/
void search_data_double_link_list(node_p Head_node,datatype data)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return;
}
if(empty_double_link_list(Head_node))
{
printf("双链表已空,无法按值查找\n");
return;
}
int arr[Head_node->len];
int i,j=0;
node_p Next_node=Head_node->next;
for(i=1;i<=Head_node->len;i++)
{
if(Next_node->data!=data)
{
Next_node=Next_node->next;
}
else
{
arr[j]=i;
Next_node=Next_node->next;
j++;
}
}
if(j!=0)
{
printf("该值对应的结点是\n");
for(i=0;i<j;i++)
{
printf("第%d个结点\t",arr[i]);
}
printf("\n");
return;
}
printf("该值不在此双循环链表中\n");
}
十五、双链表的按位置查找
/*------------双链表的按位置查找------------*/
datatype search_pos_double_link_list(node_p Head_node,int pos)
{
if(Head_node==NULL)
{
printf("参数为空,请检查\n");
return -1;
}
if(empty_double_link_list(Head_node))
{
printf("双链表已空,无法按位置查找\n");
return -2;
}
if(pos<1||pos>Head_node->len)
{
printf("超出双链表范围,无法按位置查找\n");
return -3;
}
int i;
node_p Pos_node=Head_node->next;
for(i=1;i<pos;i++)
{
Pos_node=Pos_node->next;
}
return Pos_node->data;
}