C语言---双向链表


前言

这可能是最全的双向链表的讲解,一共整整10中常用的方法,有代码说明,更有图画展示,话不多说,大家都过来看。
前面几篇我们学习到了顺序表、单链表数据结构,下来我们学习新的数据结构—双向链表


1.双向链表

在这里插入图片描述

注意:这⾥的“带头”跟前面我们说的“头节点”是两个概念,实际前⾯的在单链表阶段称呼不严谨,但是为了同学们更好的理解就直接称为单链表的头节点。
带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里"放哨的”“哨兵位”存在的意义:
遍历循环链表避免死循环


2.双向链表的结构体定义

注:本次是在clion软件进行代码的编写和测试的
在这里插入图片描述

一个节点分为三个部分,分别是指向上一个和下一个的节点的指针,及其数据。

typedef int LTDataType;
typedef struct ListNode
{
    LTDataType data;
    struct ListNode *next;
    struct ListNode*prev;
}LTNode;

3.相关代码的实现

记住:一定要首尾相连

3.1.打印链表

打印的结束标志是遍历一圈即可。(pcur!=phead)
记住头指针是不用读取的

//打印链表
void LTPrint(LTNode* phead)
{
    LTNode* pcur=phead->next;
    while(pcur!=phead)
    {
        printf("%d->",pcur->data);
        pcur=pcur->next;
    }
    printf("\n");
}

3.2.创建节点

LTNode *LTByNode(LTDataType x)
{
    LTNode *newnode= (LTNode*)malloc(sizeof (LTNode));
    if(newnode==NULL)
    {
        perror("LTByNode()::malloc()");
        exit(-1);
    }
    //首尾相连
    newnode->next=newnode->prev=newnode;
    newnode->data=x;
    return newnode;
}

3.3.初始化链表

就是创建一个头结点。

LTNode* LTInit()
{
    LTNode* phead = LTByNode(-1);
    return phead;
}

3.4.尾插

在这里插入图片描述

void LTPushBack(LTNode* phead, LTDataType x)
{
    //首先判断是否是空链表
    assert(phead);
    LTNode *newnode= LTByNode(x);
    //phead pheea->next newnode
    newnode->prev=phead->prev;
    newnode->next=phead;

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

}

3.5.头插

在这里插入图片描述

oid LTPushFront(LTNode* phead, LTDataType x)
{
    assert(phead);
    LTNode *newnode= LTByNode(x);
    //phead phead->next newnode
    newnode->next=phead->next;
    newnode->prev=phead;

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

}

3.6.尾删

在这里插入图片描述

void LTPopBack(LTNode* phead)
{
    //链表必须有效且链表不能为空(只有一个哨兵位)
    assert(phead && phead->next != phead);
    //先将要删除的节点保存起来
    LTNode* del=phead->prev;

    //phead del del->prev
    del->prev->next=phead;
    phead->prev=del->prev;

    free(del);
    del=NULL;
}

3.6.头删

在这里插入图片描述

void LTPopFront(LTNode* phead)
{
    assert(phead&&phead->next);
    LTNode *del=phead->next;


    del->next->prev=phead;
    phead->next=del->next;
    free(del);
    del=NULL;
}

3.7.销毁链表

void LTDestroy(LTNode* phead)
{
    assert(phead);

    LTNode* pcur = phead->next;
    while (pcur != phead)
    {
        LTNode* next = pcur->next;
        free(pcur);
        pcur = next;
    }
    free(phead);
    phead = NULL;
}

3.8.查找节点

思路和打印链表一样,就是遍历一次整个链表。

LTNode *LTFind(LTNode* phead,LTDataType x)
{
    LTNode *pcur=phead->next;
    while(pcur!=phead)
    {
        if(pcur->data==x)
            return pcur;
        pcur=pcur->next;
    }
    return NULL;
    
}

3.9.在指定位置之后插入数据

在这里插入图片描述

void LTInsert(LTNode* pos, LTDataType x)
{
    assert(pos);
    LTNode *newnode= LTByNode(x);
    //pos pos->next newnode
    newnode->next=pos->next;
    newnode->prev=pos;

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

3.10.在指定位置的节点

在这里插入图片描述

void LTErase(LTNode* pos)
{
    assert(pos);
    //pos->prev pos pos->next
    pos->prev->next=pos->next;
    pos->next->prev=pos->next;
    free(pos);
    pos=NULL;
}

4.参考代码

List.h文件

//
// Created by Lenovo on 2024/4/22.
//

#ifndef C_CLION_LIST_H
#define C_CLION_LIST_H

#endif //C_CLION_LIST_H

#include <stdio.h>
#include <stdlib.h>
#include<assert.h>
#include <stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
    LTDataType data;
    struct ListNode *next;
    struct ListNode*prev;
}LTNode;
//初始化链表
LTNode* LTInit();

//LTNode* LTInit();
void LTDestroy(LTNode* phead);
void LTPrint(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);
//在pos位置之后插⼊数据
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
LTNode *LTFind(LTNode* phead,LTDataType x);

List.c文件

//
// Created by Lenovo on 2024/4/22.
//
#include "List.h"


//创建节点
LTNode *LTByNode(LTDataType x)
{
    LTNode *newnode= (LTNode*)malloc(sizeof (LTNode));
    if(newnode==NULL)
    {
        perror("LTByNode()::malloc()");
        exit(-1);
    }
    //首尾相连
    newnode->next=newnode->prev=newnode;
    newnode->data=x;
    return newnode;
}
//初始化链表
LTNode* LTInit()
{
    LTNode* phead = LTByNode(-1);
    return phead;
}
void LTDestroy(LTNode* phead)
{
    assert(phead);

    LTNode* pcur = phead->next;
    while (pcur != phead)
    {
        LTNode* next = pcur->next;
        free(pcur);
        pcur = next;
    }
    free(phead);
    phead = NULL;
}
//打印链表
void LTPrint(LTNode* phead)
{
    LTNode* pcur=phead->next;
    while(pcur!=phead)
    {
        printf("%d->",pcur->data);
        pcur=pcur->next;
    }
    printf("\n");
}
bool LTEmpty(LTNode* phead);
//尾\头 插\删
void LTPushBack(LTNode* phead, LTDataType x)
{
    //首先判断是否是空链表
    assert(phead);
    LTNode *newnode= LTByNode(x);
    //phead pheea->next newnode
    newnode->prev=phead->prev;
    newnode->next=phead;

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

}

void LTPopBack(LTNode* phead)
{
    //链表必须有效且链表不能为空(只有一个哨兵位)
    assert(phead && phead->next != phead);
    //先将要删除的节点保存起来
    LTNode* del=phead->prev;

    //phead del del->prev
    del->prev->next=phead;
    phead->prev=del->prev;

    free(del);
    del=NULL;
}

void LTPushFront(LTNode* phead, LTDataType x)
{
    assert(phead);
    LTNode *newnode= LTByNode(x);
    //phead phead->next newnode
    newnode->next=phead->next;
    newnode->prev=phead;

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

}

void LTPopFront(LTNode* phead)
{
    assert(phead&&phead->next);
    LTNode *del=phead->next;


    del->next->prev=phead;
    phead->next=del->next;
    free(del);
    del=NULL;
}

//在pos位置之后插⼊数据
void LTInsert(LTNode* pos, LTDataType x)
{
    assert(pos);
    LTNode *newnode= LTByNode(x);
    //pos pos->next newnode
    newnode->next=pos->next;
    newnode->prev=pos;

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

void LTErase(LTNode* pos)
{
    assert(pos);
    //pos->prev pos pos->next
    pos->prev->next=pos->next;
    pos->next->prev=pos->next;
    free(pos);
    pos=NULL;
}

LTNode *LTFind(LTNode* phead,LTDataType x)
{
    LTNode *pcur=phead->next;
    while(pcur!=phead)
    {
        if(pcur->data==x)
            return pcur;
        pcur=pcur->next;
    }
    return NULL;
}

main文件

//
// Created by Lenovo on 2024/4/22.
//
#include "List.h"
void test()
{
    LTNode *plist=LTInit();;
    LTPushBack(plist,1);
    LTPushBack(plist,2);
    LTPushBack(plist,3);
    LTPushBack(plist,4);
    LTPrint(plist);
    LTNode *find= LTFind(plist,3);
    LTInsert(find,8);
    LTPrint(plist);
    LTErase(find);
    LTPrint(plist);
//    LTPopFront(plist);
//    LTPrint(plist);
//    LTPopFront(plist);
//    LTPrint(plist);
    LTDestroy(plist);
//    LTPushFront(plist,6);
//    LTPrint(plist);
//    LTPushFront(plist,7);
//    LTPrint(plist);
}

int main()
{
    test();
    return 0;
}

  • 20
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值