C语言——双向链表的使用

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 >
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值