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

本文介绍了双向带头循环链表的基础结构,包括其节点定义、创建、销毁、打印、插入、删除等基本操作。这种数据结构在需要高效前后遍历和双向插入删除的场景中表现出优势,但也因其额外的存储需求和操作复杂性存在劣势。
摘要由CSDN通过智能技术生成
  1. 双向带头循环链表的基础结构

双向带头循环链表(Doubly Circular Linked List)是一种数据结构,它与普通链表类似,但是每个节点除了有指向下一个节点的指针外,还有指向前一个节点的指针,而且它的头节点指向尾节点,尾节点也指向头节点,形成了一个循环。因此,我们能得到它的基础结构为

typedef int LTDataType;
typedef struct ListNode
{
    LTDataType data;
    struct ListNode* next;
    struct ListNode* prev;
}ListNode;
  1. 双向带头循环链表的数据操作

①创建返回链表的头结点

//创建一个新节点
ListNode* BuyListNode(LTDataType x)
{
    ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
    if (newnode == NULL)
    {
        printf("malloc fail");
        return NULL;
    }

    newnode->data = x;
    newnode->next = NULL;
    newnode->prev = NULL;

    return newnode;
}

②创建一个新节点

// 创建返回链表的头结点.
ListNode* ListCreate()
{
    ListNode* head = BuyListNode(-1);

    head->next = head;
    head->prev = head->next;

    return head;
}

③双向链表销毁

// 双向链表销毁
void ListDestory(ListNode* pHead)
{
    ListNode* tail = pHead->next;
    while (tail != pHead)
    {
        ListNode* tmp = tail->next;
        free(tail);
        tail = tmp;
    }

    free(tail);
    tail = NULL;
}

④双向链表打印

// 双向链表打印
void ListPrint(ListNode* pHead)
{
    ListNode* cur = pHead->next;

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

⑤双向链表尾插

void ListPushBack(ListNode* pHead, LTDataType x)
{
    ListNode* newnode = BuyListNode(x);
    ListNode* tail = pHead->prev;

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

⑥判断双向链表是否为空

//判断双向链表是否为空
bool LTEmpty(ListNode* phead)
{
    return phead->next != phead;
}

⑦双向链表尾删

// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
    assert(pHead);
    assert(LTEmpty(pHead));

    ListNode* tail = pHead->prev;

    tail->prev->next = pHead;
    pHead->prev = tail->prev;
    free(tail);
    tail = NULL;
}

⑧双向链表头插

// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
    ListNode* newnode = BuyListNode(x);

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

⑨双向链表头删

// 双向链表头删
void ListPopFront(ListNode* pHead)
{
    assert(pHead);
    assert(LTEmpty(pHead));

    ListNode* tmp = pHead->next;

    pHead->next->next->prev = pHead;
    pHead->next = pHead->next->next;
    free(tmp);
    tmp = NULL;
}

⑩双向链表查找

// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
    ListNode* pos = pHead->next;

    while (pos != pHead)
    {
        if (pos->data == x)
        {
            return pos;
        }
        pos = pos->next;
    }

    return NULL;
}

⑪双向链表在pos的前面进行插入

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
    ListNode* prev = pos->prev;
    ListNode* newnode = BuyListNode(x);
    
    newnode->next = pos;
    newnode->prev = prev;
    prev->next = newnode;
    pos->prev = newnode;
}

⑫双向链表删除pos位置的节点

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
    ListNode* tmp = pos;

    pos->prev->next = pos->next;
    pos->next->prev = pos->prev;
}
  1. 双向带头循环链表的优势与劣势

①优势

  1. 双向带头循环链表能够高效地实现数据的前后遍历,可以在链表中任何位置快速地插入或删除节点,而不需要遍历整个链表。

  1. 它允许双向遍历,这使得某些算法和数据操作(例如,快速删除和反转链表)更加容易和高效。

  1. 对于需要使用首尾相接的数据结构的场景(例如,实现循环队列),双向带头循环链表是一种自然的选择。

②劣势

  1. 它需要更多的存储空间来存储前驱指针,这可能会占用额外的内存空间。

  1. 由于需要同时维护前驱和后继指针,操作的实现可能会更加复杂,代码的可读性和可维护性也可能会受到影响。

  1. 在某些场景下,双向带头循环链表可能并不是最优的数据结构选择,例如,当数据具有固定的大小和顺序时,数组可能是更好的选择。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值