C语言——双向链表的使用
虽然使用单链表能 100% 解决逻辑关系为 “一对一” 数据的存储问题,但在解决某些特殊问题时,单链表并不是效率最优的存储结构。比如说,如果算法中需要大量地找某指定结点的前趋结点,使用单链表无疑是灾难性的,因为单链表更适合 “从前往后” 找,而 “从后往前” 找并不是它的强项。
为了能够高效率解决类似的问题,本节来学习双向链表(简称双链表)
强烈推荐学习链接:http://c.biancheng.net/view/3343.html
双向链表的结构
指针域:用于指向当前节点的直接前驱节点;
数据域:用于存储数据元素。
指针域:用于指向当前节点的直接后继节点;
typedef struct _line{
struct line * prior; //指向直接前趋
int data;
struct line * next; //指向直接后继
}line;
下面是我在rt_threat中的实际应用代码
1、构建数据结构
typedef struct _message_bady
{
rt_uint32_t magic;
rt_uint16_t message_type;
rt_uint16_t reserved; //保留字段
} message_bady;
typedef struct _line
{
struct _line *prior; //指向直接前趋
message_bady data;
struct _line *next; //指向直接后继
} line;
2、链表的实现函数
/*创建几个节点,以便于后续操作*/
line *initLine(line *head)
{
head=(line*)malloc(sizeof(line));//创建链表第一个结点(首元结点)
head->prior=NULL;
head->next=NULL;
head->data.magic=1; head->data.message_type=1; head->data.reserved=1;
line *list=head;
for (int i=2; i<=5; i++)
{
//创建并初始化一个新结点
line *body=(line*)malloc(sizeof(line));
body->prior=NULL;
body->next=NULL;
body->data.magic=i;
body->data.message_type=i;
body->data.reserved=i;
list->next=body;//直接前趋结点的next指针指向新结点
body->prior=list;//新结点指向直接前趋结点
list=list->next;
}
return head;
}
/*在add的位置接入一个节点*/
line *insertLine(line *head,message_bady data,int add)
{
//新建数据域为data的结点
line *temp=(line*)malloc(sizeof(line));
temp->data.magic=data.magic;
temp->data.message_type=data.message_type;
temp->data.reserved=data.reserved;
temp->prior=NULL;
temp->next=NULL;
//插入到链表头,要特殊考虑
if (add==1) {
temp->prior=NULL;
temp->next=head;
head->prior=temp;
head=temp;
}else{
line *body=head;
//找到要插入位置的前一个结点
for (int i=1; i<add-1; i++) {
body=body->next;
}
//判断条件为真,说明插入位置为链表尾
if (body->next==NULL) {
body->next=temp;
temp->prior=body;
}else{
body->next->prior=temp;
temp->next=body->next;
body->next=temp;
temp->prior=body;
}
}
return head;
}
//删除结点的函数,data为要删除结点的数据域的值
line *delLine(line *head,message_bady data)
{
line *temp=head;
//遍历链表
while (temp) {
//判断当前结点中数据域和data是否相等,若相等,摘除该结点
if (temp->data.magic==data.magic)
{
//*******这个地方卡壳比较久的时间,是我自己接入的代码***********
if(temp->prior == NULL)//要考虑删除第一个节点的特殊情况
{
temp->next->prior = NULL;
head = head->next;
rt_free(temp);
return head;
}
if(temp->next == NULL)//要考虑删除最后一个节点的特殊情况
{
temp->prior->next = NULL;
rt_free(temp);
return head;
}
temp->prior->next=temp->next;
temp->next->prior=temp->prior;
rt_free(temp);
return head;
}
temp=temp->next;
}
rt_kprintf("line no data\r\n");
return head;
}
//head为原双链表,elem表示被查找元素
int selectElem(line *head,message_bady elem)
{
//新建一个指针temp,初始化为头指针 head
line *temp=head;
int i=1;
while (temp) {
if (temp->data.magic==elem.magic) {
return i;
}
i++;
temp=temp->next;
}
//程序执行至此处,表示查找失败
return -1;
}
//更新函数,其中,add 表示更改结点在双链表中的位置,newElem 为新数据的值
line *amendElem(line *p,int add,message_bady newElem)
{
line *temp=p;
//遍历到被删除结点
for (int i=1; i<add; i++) {
temp=temp->next;
}
temp->data=newElem;
return p;
}
//输出链表的功能函数
void display(line *head)
{
line * temp=head;
while (temp) {
if (temp->next==NULL) {
rt_kprintf("%d,%d,%d\n",temp->data.magic,temp->data.message_type,temp->data.reserved);
}else{
rt_kprintf("%d,%d,%d->",temp->data.magic,temp->data.message_type,temp->data.reserved);
}
temp=temp->next;
}
}
3、函数的引用实现功能
int count = 0;
message_bady mesage={10,20,30};
message_bady mesage1={7,7,7};
message_bady mesage2={8,8,8};
message_bady mesage3={9,9,9};
line *p = NULL;
p = initLine(p);
rt_kprintf("original\r\n");
display(p);//打印出目前的链表
rt_kprintf("add new\r\n");
p = insertLine(p,mesage,4);//在第四个位置加入一个节点
display(p);
p = insertLine(p,mesage1,1);//在第一个位置加入一个节点
display(p);
p = insertLine(p,mesage2,2);//在第2个位置加入一个节点
display(p);
rt_kprintf("del\r\n");
p = delLine(p,mesage);//删除节点message
display(p);
p = delLine(p,mesage2);//删除节点message
display(p);
count = selectElem(p,mesage);//查找到节点message所在的位置
rt_kprintf("count :%d\r\n",count); //打印位置检测不到返回-1
count = selectElem(p,mesage1);//查找到节点message1所在的位置
rt_kprintf("count :%d\r\n",count); //打印位置
p = amendElem(p,4,mesage3);//在第四个位置更新数据
display(p);
运行之后的调试信息如下
\ | /
- RT - Thread Operating System
/ | \ 4.0.2 build Dec 24 2020
2006 - 2019 Copyright by rt-thread team
original
1,1,1->2,2,2->3,3,3->4,4,4->5,5,5
add new
1,1,1->2,2,2->3,3,3->10,20,30->4,4,4->5,5,5
7,7,7->1,1,1->2,2,2->3,3,3->10,20,30->4,4,4->5,5,5
7,7,7->8,8,8->1,1,1->2,2,2->3,3,3->10,20,30->4,4,4->5,5,5
del
7,7,7->8,8,8->1,1,1->2,2,2->3,3,3->4,4,4->5,5,5
7,7,7->1,1,1->2,2,2->3,3,3->4,4,4->5,5,5
count :-1
count :1
7,7,7->1,1,1->2,2,2->9,9,9->4,4,4->5,5,5
msh >