C语言双向链表的实现

链表有很多分类,比如单向还是双向,循环还是不循环等等。根据这些分类,链表有8种分类。双向链表是其中最复杂的链表。学会了本篇文章的双向链表和昨天发的单向链表,链表的基本知识就学完了,我们也可以应用到实战去了。


(一)双向链表的定义和初始化

双向链表是指链表头尾相连,结点相互指向的链表。所以一个结点中需要保存两个指针,分别指向上一个结点和下一个结点。

//双向链表的定义

#define ListDataType int

typedef struct ListNode
{
    ListDataType data;
    struct ListNode* prev;
    struct ListNode* next;
}LtNode;

这里将双向链表的数据类型重命名为LtNode。里面保存了指向上一个结点的指针prev,和指向下一个结点的指针next。这里我们先写一个创建双向链表结点的函数。这里我们把哨兵岗位初始化为-1,这个哨兵岗位就是用来放哨的,占个位置,方便我们之后的操作。

//创建链表结点
LtNode* BuyNewNode(ListDataType val)
{
    LtNode* NewNode = (LtNode*)malloc(sizeof(LtNode));
    if (NewNode == NULL)
    {
        perror("malloc failed\n");
        exit(1);
    }
    NewNode->data = val;
    NewNode->prev = NewNode->next = NewNode;
    return NewNode;
}
//链表的初始化
void InitList(LtNode** pphead)
{
    //创建哨兵结点
    assert(pphead);
    *phead = BuyNewNode(-1);    
}

需要注意的是,在初始化头结点时,它的前后指针都得指向头结点本身。

(二)双向链表尾插,头插

//双向链表的尾插
void ListPushBack(LtNode* phead, ListDataType x)
{
    assert(phead);
    //我们可以通过头结点找到尾结点
    LtNode* newnode = BuyNewNode(x);
    
    //  phead   ->phead->prev  ->newnode
    newnode->next = phead;
    newnode->prev = phead->prev;
    
    phead->prev->next = newnode;
    phead->prev = newnode;
}
//双向链表的头插
void ListPushFront(LtNode* phead, ListDataType x)
{
    assert(phead);
    LtNode* newnode = BuyNewNode(x);
    
    //   phead -> newnode-> phead->next
    
    newnode->next = phead->next;
    newnode->prev = phead;
    
    phead->next->prev = newnode;
    phead->next = newnode;
    
   
}

(三)双向链表的尾删,头删,检查链表是否为空

//双向链表的尾删
void ListPopBack(LtNode* phead)
{
    assert(!ListEmpty(phead));
    
    LtNode* del = phead->prev;
    // phead   phead->prev->prev   phead->prev
    del->prev->next = phead;
    phead->prev = del->prev;

    free(del);
    del = NULL;
}
//检查链表是否为空
bool ListEmpty(LtNode* phead)
{
    assert(phead);
    return phead->next == phead;
}
//双向链表的头删
void ListPopFront(LtNode* phead)
{
     assert(phead);
     
     LtNode* del = phead->next;
     // phead  del  del->next    
     del->next->prv = phead;
     phead->next = del->next;
     
     free(del);
     del = NULL;
}
(四)双向链表的查找,指定位置插入,销毁
//查找某值
LtNode* ListFind(LtNode* phead. ListDataType x)
{
    assert(phead);
    LtNode* pcur = phead->next;
    while (pcur != phead)
    {    
        if (pcur->val == x)
        {
            return pcur;
        }
        pcur = pcur->next;
    }
    return NULL
}
//双向链表的指定位置插入
void ListInsert(LtNode* pos, ListDataType x)
{
    assert(phead);
    LtNode* pcur = phead->next;
    // pos   newnode pos->next
    LtNode* newnode = BuyNewNode(x);

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

    pos->next->prev = newnode;
    pos->next = newnode;
}
//双向链表的销毁
void ListDestroy(LtNode** pphead)
{
    assert(pphead && * pphead);
    LtNode* pcur = *pphead->next;;
    while (pcur != phead)
    {
        LtNode* ptem = pcur->next;
        free(pcur);
        pcur = ptem;
    }
    free(*pphead);
    *pphead = NULL;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值