实现双向循环链表

一:什么是双向循环链表

二:实现双向循环链表

补充:实现双向循环链表的销毁,打印

一:什么是双向循环链表

我问问你什么是链表呢?链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。它的特征无非是物理结构非连续,非顺序,逻辑顺序是通过指针链接的,也就意味着可以任意位置插入删除数据。

它由一个个节点构成,每个节点存放数据和地址

如下是最简单的单链表,大家可以结合图片理解

我们今天实现的双向循环链表拥有单链表的特征,只不过多了头节点,循环,是不是看起来复杂些,它的用处大的很呢!嘿嘿。

二:接下来让我们来会一会这个双向循环链表

我们知道双向循环链表头节点,循环,也就是有哨兵位,前继指针,后继指针,同时他们是定义双向链表节点的结构三元素。

有了三元素坐镇,可以定义双向循环链表啦。

定义一个双向循环链表,我们先创建哨兵位,哨兵位即为头节点,它并不存储有效数据,只起到占位置的作用,然后在将所需数据接到头节点之后。

哨兵位也有注意的点,哨兵位是双向循环链表起始位置,如果没有哨兵位,这个链表是不存在的;哨兵位可以单独存在,所以双向循环链表不存在为空情况。

在初始化双向循环链表时,链表为空什么都没有,当然得放进哨兵位来占位置咯,它不存储有效数据哦

                                                                                                 

我们在申请空间存放节点时要判断一下节点是否为空,为啥要判断节点为空呢?因为如果节点为空,那么他连哨兵位都没有了,就不构成哨兵位。

定义好双向循环链表,那就开始增删查改的工作吧!

二:

a.尾插

首先明确,尾插发生变动的地方,一个是哨兵位,一个是尾节点,一个是新节点。先插新节点,因为尾插入新节点对链表没啥影响,newnode前趋指针指向尾,后趋指针指向哨兵位,将为节点后趋指针指向newnode,phead前趋指针指向newnode,这样完成了尾插

这是代码

这是尾插的调试,通过调试来判断代码逻辑正确性

b.头插

将节点插入到第一个有效节点之前。而不是哨兵位节点之前,如果插到哨兵位之前,交换了newnode的位置,指针的指向并没有发生改变,到头来还是尾插。

头插

让代码运行一下吧

链表打印:

尾删:

顾名思义是删除尾节点,我们不可以直接删除尾节点,而是要先保存尾节点,然后在删除尾节点。具体步骤如下:

让尾插代码跑起来吧

头删

头删,删除的是有效节点,即哨兵位之后的节点,下图中,要删的节点是d1

怎么删呢,不能直接删d1,因为head->next指向d1,d2指向d1,在删除之前,先保存d1,然后删

双向链表的查找

因为双向链表的哨兵位节点不存放有效数据,所以从哨兵位的下一个节点开始查找数据,当指针指向所需要的数据,表明查找成功,否则失败

让代码运行一下吧

g.1在pos前插入数据

pos是有效节点,它不可能是哨兵位。在pos后插入数据所用的方试类似于头插,头插那一套在这里同样适用。

g.2    pos后插入数据

原理跟pos前插入数据一样,只不过换了位置

删除pos节点

不能直接删掉pos,要先保留pos前一个节点以及后一个节点,然后前一个节点指向后一个节点,后一个节点指向前一个节点

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
双向循环链表是一种常用的数据结构之一,可以在链表中进行快速的插入、删除和查找操作。以下是一种简单的C语言实现双向循环链表的代码: ``` #include <stdio.h> #include <stdlib.h> struct node { int data; struct node *prev; struct node *next; }; struct node *createNode(int data) { struct node *newNode = (struct node*)malloc(sizeof(struct node)); newNode->data = data; newNode->prev = NULL; newNode->next = NULL; return newNode; } void insertAtBegin(struct node **head, int data) { struct node *newNode = createNode(data); if (*head == NULL) { *head = newNode; (*head)->next = *head; (*head)->prev = *head; } else { struct node *last = (*head)->prev; newNode->next = *head; newNode->prev = last; last->next = newNode; (*head)->prev = newNode; *head = newNode; } } void insertAtEnd(struct node **head, int data) { struct node *newNode = createNode(data); if (*head == NULL) { *head = newNode; (*head)->next = *head; (*head)->prev = *head; } else { struct node *last = (*head)->prev; newNode->prev = last; newNode->next = *head; last->next = newNode; (*head)->prev = newNode; } } void deleteNode(struct node **head, int data) { if (*head == NULL) { printf("List is empty.\n"); return; } struct node *cur = *head; struct node *prev = NULL; while (cur->data != data && cur->next != *head) { prev = cur; cur = cur->next; } if (cur->data != data) { printf("%d not found in list.\n", data); return; } if (cur == *head && cur->next == *head) { *head = NULL; free(cur); return; } if (cur == *head) { *head = cur->next; } struct node *next = cur->next; prev->next = next; next->prev = prev; free(cur); } void displayList(struct node *head) { if (head == NULL) { printf("List is empty.\n"); return; } printf("List: "); struct node *cur = head; do { printf("%d ", cur->data); cur = cur->next; } while (cur != head); printf("\n"); } int main() { struct node *head = NULL; insertAtBegin(&head, 10); insertAtBegin(&head, 20); insertAtEnd(&head, 30); insertAtEnd(&head, 40); displayList(head); deleteNode(&head, 20); deleteNode(&head, 50); displayList(head); return 0; } ``` 在这个例子中,我们使用了一个结构体来存储每个节点的数据和指向前一个和后一个节点的指针。我们还定义了一些操作函数,如创建新节点、在链表头部和尾部插入新节点、删除节点和显示链表等。在主函数中,我们创建了一个双向循环链表并进行了一些操作,最后输出链表的内容。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值