【初阶数据结构与算法】第五篇:双链表

 

⭐️本篇博客我要给大家分享一下算法中的双链表。希望对大家有所帮助。
⭐️ 博主码云gitee链接:码云主页

💙系列文章💙

【初阶数据结构与算法】第一篇:算法中的时间复杂度和空间复杂度

【初阶数据结构与算法】第二篇:顺序表

【初阶数据结构与算法】第三篇:单链表

 【初阶数据结构与算法】第四篇:链表面试题详解


目录

前言

🌏二、代码实现

        🍯1.双链表定义

        🍯2.双链表初始化两种形式

        🍯3.双链表申请空间

        🍯4.双链表尾插

        🍯5.双链表尾删

        🍯6.双链表打印

        🍯7.双链表头插

        🍯8.双链表头删

        🍯9.双链表查找

        🍯10.中间插入及头插和尾插优化

        🍯11.中间删除及头删和尾删优化

        🍯12.双链表销毁

🌏总结


前言


 🌏一、双链表概念

🍤第一个结点属于多开辟的空间,为了方便头插头删,后续结点为真正的链表内容。

内容:

        🍤一个指向前面结点的指针

        🍤一个存储数据的空间

        🍤一个指向后面结点的指针

🌏二、代码实现

        🍯1.双链表定义

🍤与链表不同的是,双链表结构更复杂,但其实实现起来比单链表简单,并且还有一个哨兵

//此双链表带哨兵位
typedef int typedefData;
typedef struct Double_linked_list {
    typedefData Data;               //数据
    struct Double_linked_list* next;//下一个指针
    struct Double_linked_list* prev;//上一个指针
}Double_linked_list;

        🍯2.双链表初始化两种形式

🍤初始化时,我们要将哨兵位地址传过去,因为我们要修改哨兵位的值,将哨兵位值修改为0,两个指针都指向他自己

//初始化不带返回值
void InitializeNotReturn_Double_linked_list(Double_linked_list** pphead) {
    assert(pphead);

    *pphead = Application_Double_linked_list(0);
    (*pphead)->next = *pphead;
    (*pphead)->prev = *pphead;
}

 🍤这里不用传送哨兵位的值,因为直接在,函数中创建一个哨兵,并且将值设为0,两个指针指向他自己,最后将这个地址返回

//初始化带返回值
Double_linked_list* InitializeReturn_Double_linked_list(void) {

    Double_linked_list* phead = Application_Double_linked_list(0);
    phead->next = phead;
    phead->prev = phead;

    return phead;
}

        🍯3.双链表申请空间

🍤新的空间设为我们需要的值,将两个指针都置为空,返回新空间的地址

//申请一个空间
Double_linked_list* Application_Double_linked_list(typedefData x) {

    Double_linked_list* newnode = (Double_linked_list*)malloc(sizeof(Double_linked_list));
    if (newnode == NULL) {
        printf("fail malloc:%s\n", strerror(errno));
        exit(-1);
    }
    newnode->next = NULL;
    newnode->prev = NULL;
    newnode->Data = x;

    return newnode;
}

        🍯4.双链表尾插

🍤1.创建新结点存储数据

🍤2.原尾结点与新结点进行相互链接

🍤3.新结点与头结点相互链接

//尾插
void PushBack_Double_linked_list(Double_linked_list* phead, typedefData x) {
    assert(phead);

    Double_linked_list* newnode = Application_Double_linked_list(x);
    Double_linked_list* tail = phead->prev;

    tail->next = newnode;
    newnode->prev = tail;

    phead->prev = newnode;
    newnode->next = phead;
}

        🍯5.双链表尾删

🍤1.找到尾结点和倒数第二个结点

🍤2.释放尾结点

🍤3.头结点和倒数第二个结点链接

void PopBack_Double_linked_list(Double_linked_list* phead) {
    assert(phead);

    Double_linked_list* tail = phead->prev;
    Double_linked_list* tailprev = tail->prev;

    phead->prev = tailprev;
    tailprev->next = phead;

    free(tail);
    tail = NULL;
}

        🍯6.双链表打印

🍤用一个cur节点指针来走,走到head的位置就停下来

void Print_Double_linked_list(Double_linked_list* phead) {
    assert(phead);

    Double_linked_list* cur = phead->next;
    printf("Head ");
    while (cur != phead) {
        printf("<-> %d ", cur->Data);
        cur = cur->next;
    }
    printf("<-> Head\n");
}

        🍯7.双链表头插

🍤1.创建新结点存储数据

🍤2.新结点与头结点后的第一个结点链接

🍤3.第三步,新结点与头结点链接

void PushFront_Double_linked_list(Double_linked_list* phead, typedefData x) {
    assert(phead);

    Double_linked_list* newnode = Application_Double_linked_list(x);
    Double_linked_list* cur = phead->next;
    Double_linked_list* curnext = cur->next;

    cur->next = newnode;
    newnode->prev = cur;

    curnext->prev = newnode;
    newnode->next = curnext;
}

        🍯8.双链表头删

🍤1.头结点与头删结点后的结点链接

🍤2.释放头删结点

void PopFront_Double_linked_list(Double_linked_list* phead) {
    assert(phead);

    Double_linked_list* cur = phead->next;
    Double_linked_list* curnext = cur->next;

    phead->next = curnext;
    curnext->prev = phead;

    free(cur);
    cur = NULL;
}

        🍯9.双链表查找

🍤直接遍历

Double_linked_list* Find_Double_linked_list(Double_linked_list* phead, typedefData x) {
    assert(phead);

    Double_linked_list* cur = phead->next;
    while (cur != phead) {
        if (cur->Data == x) {
            return cur;
        }
        cur = cur->next;
    }
    return NULL;
}

        🍯10.中间插入及头插和尾插优化

                🍍中间插入

🍤任意位置前插入是在给定的pos结点之后插入

void Insert_Double_linked_list(Double_linked_list* pos, typedefData x) {
    assert(pos);

    Double_linked_list* newnode = Application_Double_linked_list(x);
    Double_linked_list* next = pos->next;

    newnode->next = next;
    next->prev = newnode;

    pos->next = newnode;
    newnode->prev = pos;
}

                🍍头插优化

🍤直接传头的下一个节点

void PushFront_Double_linked_list_other(Double_linked_list* pos, typedefData x) {
    assert(pos);
    //直接下一个节点插入就好了
    Insert_Double_linked_list(pos->next, x);
}

                🍍尾插优化

🍤直接传头的上一个节点

void PushBack_Double_linked_list_other(Double_linked_list* pos, typedefData x) {
    assert(pos);
    //直接在哨兵位之前插入即可,循环链表
    Insert_Double_linked_list(pos->prev, x);
}

        🍯11.中间删除及头删和尾删优化

                🍍中间删除

🍤判断是不是只有头了,只有头则不要删除

void Erase_Double_linked_list(Double_linked_list* phead, Double_linked_list* pos) {
    assert(phead && pos);
    //防止头节点为空
    assert(phead->next != phead);

    Double_linked_list* pospre = pos->prev;
    Double_linked_list* posnext = pos->next;

    pospre->next = posnext;
    posnext->prev = pospre;

    free(pos);
    pos = NULL;
}

                🍍头删优化

🍤同样判断是不是头,如果是头则不要删除

void PopFront_Double_linked_list_other(Double_linked_list* phead) {
    assert(phead);
    //防止头节点为空
    assert(phead->next != phead);
    Erase_Double_linked_list(phead, phead->next);
}

                🍍尾删优化

🍤同样判断是不是头,如果是头则不要删除

void PopBack_Double_linked_list_other(Double_linked_list* phead) {
    assert(phead);
    //防止头节点为空
    assert(phead->next != phead);
    Erase_Double_linked_list(phead, phead->prev);
}

        🍯12.双链表销毁

🍤轮流释放节点,最后释放头

void Destroy_Double_linked_list(Double_linked_list* phead) {
    assert(phead);

    Double_linked_list* cur = phead->next;
    Double_linked_list* curnext = cur->next;
    while (cur != phead) {
        free(cur);
        cur = curnext;
        curnext = curnext->next;
    }
    free(phead);
    phead = NULL;
}


🌏总结

链表和顺序表的对比

不同点顺序表链表
存储空间上物理上连续逻辑上连续
随机访问支持不支持
任意位置插入删除要移动元素,O(N)只要改变指针指向
插入数据要考虑扩容,会带来一定的空间消耗没有容量这个概念,可以按需申请和释放
缓存利用率
应用场景元素高效存储+频繁访问任意位置插入和删除频繁

评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

penguin_bark

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值