数据结构—带头双向循环链表

一、带头双向循环链表的定义和结构

1、定义

带头双向循环链表,有一个数据域两个指针域。一个是前驱指针,指向其前一个节点;一个是后继指针,指向其后一个节点。

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

2、结构

带头双向循环链表:在所有的链表当中 结构最复杂,一般用在 单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多 优势,实现反而简单了。

二、带头双向循环链表接口的实现

1、创建文件

 test.c(主函数、测试链表各个接口)

List.c(带头双向循环链表接口函数的实现)

List.h(带头双向循环链表的类型定义、接口函数声明、引用的头文件)

2、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 * BuyLTNode(LTDataType x);
//双链表初始化
LTNode *LTInit();
//双链表打印
void LTPrint(LTNode* phead);
//双链表判空
bool LTEmpty(LTNode* phead);
//双链表尾插
void LTPushBack(LTNode* phead,LTDataType x);
//双链表头插
void LTPushFront(LTNode* phead,LTDataType x);
//双链表尾删
void LTPopBack(LTNode* phead,LTDataType x);
//双链表头删
void LTPopFront(LTNode* phead,LTDataType x);
//双链表查找
LTNode *LTFind(LTNode* phead,LTDataType x);
//双链表在pos位之前插入
void LTInsert(LTNode* pos,LTDataType x);
//双链表在pos位删除结点
void LTErase(LTNode* pos);
//双链表销毁
void LTDestroy(LTNode* phead);
// 获取双向链表的元素个数
size_t ListSize(LTNode* phead);

 三、在List.c上实现各个接口函数

1、动态申请一个新结点

LTNode * BuyLTNode(LTDataType x)
{
    LTNode* newnode=(LTNode *)malloc(sizeof(LTNode));
    if(newnode==NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    newnode->data=x;
    newnode->next=NULL;
    newnode->prev=NULL;
    return newnode;
}

2、初始化双向链表

LTNode *LTInit()
{
    LTNode *phead= BuyLTNode(0);//动态申请一个头结点
    phead->prev=phead;//前指针指向自己
    phead->next=phead;//后指针也指向自己
    phead->data=0;//哨兵卫不需要存值
    return phead;
}

初始化带头双向循环链表,首先动态申请一个头结点头结点的前驱和后继指针都指向自己,形成一个循环

3、双向链表的销毁

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

    LTNode *cur=phead->next;
    while(cur!=phead)
    {
        LTNode *new=cur->next;//记录cur的直接后继结点
        free(cur);
        cur=new;
    }
    free(phead);
}

 4、双向链表的打印

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

    printf("guard<==>");//guard也就是哨兵卫
    LTNode *cur=phead->next;//记录第一个结点
    while(cur!=phead)
    {
        printf("%d<==>",cur->data);
        cur=cur->next;
    }
    printf("\n");
}

5、双向链表尾插

void LTPushBack(LTNode* phead,LTDataType x)
{
    assert(phead);

    //LTInsert(phead,x);

    LTNode *newnode= BuyLTNode(x);//动态申请一个新的结点
    LTNode *tail=phead->prev;//记录尾结点

    tail->next=newnode;//尾结点的next指针指向新的结点
    newnode->prev=tail;//新的结点的prev指针指向尾结点

    newnode->next=phead;//新结点的next指针指向头结点
    phead->prev=newnode;//头结点的prev指针指向新结点
}

6、双向链表尾删

void LTPopBack(LTNode* phead,LTDataType x)
{
    assert(phead);
    assert(!LTEmpty(phead));//只剩下头结点时 链表为空,不能再继续删除

    //LTErase(pjead->prev);

    LTNode *tail=phead->prev;//记录尾结点
    LTNode *tailprev=tail->prev;//记录尾结点的前一个

    free(tail);//释放尾结点
    tailprev->next=phead;//尾结点的前一个的next指针指向头结点
    phead->prev=tailprev;//头结点的prev指针指向尾结点的前一个结点
}

7、双向链表的头插

void LTPushFront(LTNode* phead,LTDataType x)
{
    assert(phead);
    //LTInsert(phead->next,x);

    LTNode *newnode= BuyLTNode(x);//申请新结点
    LTNode *first=phead->next;//记录第一个结点

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

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

8、双向链表的头删

void LTPopFront(LTNode* phead,LTDataType x)
{
    assert(phead);
    assert(!LTEmpty(phead));

    //LTErase(phead->next);

    LTNode *first=phead->next;
    LTNode *second=first->next;

    phead->next=second;
    second->prev=phead;
    free(first);
}

 

9、查找双向链表中的元素

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

 10、在指定pos位置之前插入元素

void LTInsert(LTNode* pos,LTDataType x)
{
    assert(pos);

    LTNode *posprev=pos->prev;
    LTNode *newnode= BuyLTNode(x);

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


}

实现了该函数后,可以尝试改进头插函数(pos相当于链表的第一个节点)和尾插函数(pos相当于链表的头节点),这样写起来更简便

11、删除指定pos位置的元素

void LTErase(LTNode* pos)
{
    assert(pos);

    LTNode *posprev=pos->prev;
    LTNode *posnext=pos->next;

    posprev->next=posnext;
    posnext->prev=posprev;
    free(pos);

}

实现了该函数后,可以尝试改进函数(pos相当于链表的第一个节点)和尾删函数(pos相当于链表的最后一个节点),这样写起来更简便 

 

12、双向链表的判空

bool LTEmpty(LTNode* phead)
{
    assert(phead);
    return phead==phead->next;
}

 13、获取双向链表的元素个数

size_t ListSize(LTNode* phead)
{
    assert(phead);
    
    size_t size = 0;
    LTNode * cur=phead->next;
    while(cur!=phead)
    {
        size++;
        cur=cur->next;
    }
    return size;
}

四、代码整合

//List.c

#include "List.h"

LTNode * BuyLTNode(LTDataType x)
{
    LTNode* newnode=(LTNode *)malloc(sizeof(LTNode));
    if(newnode==NULL)
    {
        perror("malloc fail");
        return NULL;
    }
    newnode->data=x;
    newnode->next=NULL;
    newnode->prev=NULL;
    return newnode;
}

LTNode *LTInit()
{
    LTNode *phead= BuyLTNode(0);
    phead->prev=phead;
    phead->next=phead;
    phead->data=0;
    return phead;
}
void LTPrint(LTNode* phead)
{
    assert(phead);

    printf("guard<==>");
    LTNode *cur=phead->next;
    while(cur!=phead)
    {
        printf("%d<==>",cur->data);
        cur=cur->next;
    }
    printf("\n");
}

bool LTEmpty(LTNode* phead)
{
    assert(phead);
    return phead==phead->next;
}

void LTPushBack(LTNode* phead,LTDataType x)
{
    assert(phead);

    //LTInsert(phead,x);

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

    LTNode *newnode= BuyLTNode(x);
    LTNode *first=phead->next;

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

void LTPopBack(LTNode* phead,LTDataType x)
{
    assert(phead);
    assert(!LTEmpty(phead));

    //LTErase(pjead->prev);

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

    free(tail);
    tailprev->next=phead;
    phead->prev=tailprev;
}
void LTPopFront(LTNode* phead,LTDataType x)
{
    assert(phead);
    assert(!LTEmpty(phead));

    //LTErase(phead->next);

    LTNode *first=phead->next;
    LTNode *second=first->next;

    phead->next=second;
    second->prev=phead;
    free(first);
}

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

void LTInsert(LTNode* pos,LTDataType x)
{
    assert(pos);

    LTNode *posprev=pos->prev;
    LTNode *newnode= BuyLTNode(x);

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


}
void LTErase(LTNode* pos)
{
    assert(pos);

    LTNode *posprev=pos->prev;
    LTNode *posnext=pos->next;

    posprev->next=posnext;
    posnext->prev=posprev;
    free(pos);

}

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

    LTNode *cur=phead->next;
    while(cur!=phead)
    {
        LTNode *new=cur->next;
        free(cur);
        cur=new;
    }
    free(phead);
}

size_t ListSize(LTNode* phead)
{
    assert(phead);

    size_t size = 0;
    LTNode * cur=phead->next;
    while(cur!=phead)
    {
        size++;
        cur=cur->next;
    }
    return size;
}

  • 15
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值