赞
踩
带头双向循环链表,有一个数据域和两个指针域。一个是前驱指针,指向其前一个节点;一个是后继指针,指向其后一个节点。
- // 定义双向链表的节点
- typedef struct ListNode
- {
- LTDataType data; // 数据域
- struct ListNode* prev; // 前驱指针
- struct ListNode* next; // 后继指针
- }ListNode;
带头双向循环链表:在所有的链表当中 结构最复杂,一般用在 单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多 优势,实现反而简单了。
- test.c(主函数、测试顺序表各个接口功能)
- List.c(带头双向循环链表接口函数的实现)
- List.h(带头双向循环链表的类型定义、接口函数声明、引用的头文件)
- // List.h
- // 带头+双向+循环链表增删查改实现
- #pragma once
- #include <stdio.h>
- #include <stdlib.h>
- #include <assert.h>
-
- typedef int LTDataType;
-
- // 定义双向链表的节点
- typedef struct ListNode
- {
- LTDataType data; // 数据域
- struct ListNode* prev; // 前驱指针
- struct ListNode* next; // 后继指针
- }ListNode;
-
- // 动态申请一个新节点
- ListNode* BuyListNode(LTDataType x);
- // 创建返回链表的头结点
- ListNode* ListCreate();
- // 双向链表销毁
- void ListDestory(ListNode* plist);
- // 双向链表打印
- void ListPrint(ListNode* plist);
- // 双向链表尾插
- void ListPushBack(ListNode* plist, LTDataType x);
- // 双向链表尾删
- void ListPopBack(ListNode* plist);
- // 双向链表头插
- void ListPushFront(ListNode* plist, LTDataType x);
- // 双向链表头删
- void ListPopFront(ListNode* plist);
- // 双向链表查找
- ListNode* ListFind(ListNode* plist, LTDataType x);
- // 双向链表在pos的前面进行插入
- void ListInsert(ListNode* pos, LTDataType x);
- // 双向链表删除pos位置的节点
- void ListErase(ListNode* pos);
- // 双向链表的判空
- bool ListEmpty(ListNode* phead);
- // 获取双向链表的元素个数
- size_t ListSize(ListNode* phead);
- // 动态申请一个新节点
- ListNode* BuyListNode(LTDataType x)
- {
- ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
- newnode->data = x;
- newnode->prev = NULL;
- newnode->next = NULL;
- return newnode;
- }
- // 创建返回链表的头结点
- ListNode* ListCreate()
- {
- ListNode* phead = (ListNode*)malloc(sizeof(ListNode)); // 哨兵位头结点
- phead->next = phead;
- phead->prev = phead;
- return phead;
- }
也可以用下面这个函数(道理一样):
- // 初始化链表
- void ListInit(ListNode** pphead)
- {
- *pphead = BuyListNode(-1); // 动态申请一个头节点
- (*pphead)->prev = *pphead; // 前驱指针指向自己
- (*pphead)->next = *pphead; // 后继指针指向自己
- }
头指针初始指向 NULL,初始化链表时,需要改变头指针的指向,使其指向头节点,所以这里需要传二级指针。
初始化带头双向循环链表,首先动态申请一个头结点,头结点的前驱和后继指针都指向自己,形成一个循环。
- // 双向链表的销毁
- void ListDestroy(ListNode** pphead)
- {
- assert(pphead);
- assert(*pphead);
-
- ListNode* cur = (*pphead)->next;
- while (cur != *pphead)
- {
- ListNode* next = cur->next; // 记录cur的直接后继节点
- free(cur);
- cur = next;
- }
- free(*pphead); // 释放头节点
- *pphead = NULL; // 置空头指针
- }
销毁链表,最后要将头指针 plist 置空,所以用了二级指针来接收。这里也可以用一级指针,但要在函数外面置空 plist 。
一级指针写法:
void ListDestroy(ListNode* phead) { assert(phead); ListNode* cur = phead->next; while (cur != phead) { ListNode* next = cur->next; free(cur); cur = next; } free(phead); phead = NULL; }
- // 打印双向链表
- void ListPrint(ListNode* phead)
- {
- assert(phead);
-
- ListNode* cur = phead->next; // 记录第一个节点
- printf("head <-> ");
- while (cur != phead)
- {
- printf("%d <-> ", cur->data);
- cur = cur->next;
- }
- printf("head\n");
- }
- // 双向链表尾插
- void ListPushBack(ListNode* phead, LTDataType x)
- {
- assert(phead); // 头指针不能为空
-
- /* ListNode* newnode = BuyListNode(x); // 动态申请一个节点
- ListNode* tail = phead->prev; // 记录尾节点
- tail->next = newnode; // 尾节点的后继指针指向新节点
- newnode->prev = tail; //2、新节点的前驱指针指向尾节点
- newnode->next = phead; // 新节点的后继指针指向头节点
- phead->prev = newnode; // 头节点的前驱指针指向新节点 */
-
- ListInsert(phead, x);
- }
- // 双向链表的尾删
- void ListPopBack(ListNode* phead)
- {
- assert(phead);
- assert(phead->next != phead); // 只剩头节点时 链表为空 不能再继续删除
-
- /* ListNode* tail = phead->prev; // 记录尾节点
- ListNode* tailPrev = tail->prev; // 记录尾节点的直接前驱
-
- tailPrev->next = phead; // 尾节点的前驱节点的next指针指向头节点
- phead->prev = tailPrev; // 头节点的prev指针指向尾节点的前驱节点
- free(tail); // 释放尾节点 */
-
- ListErase(pHead->prev);
- }
- // 双向链表的头插
- void ListPushFront(ListNode* phead, LTDataType x)
- {
- assert(phead);
-
- /* ListNode* newnode = BuyListNode(x); // 申请新节点
- ListNode* pheadNext = phead->next; // 记录第一个节点
-
- // 头节点和新节点建立链接
- phead->next = newnode;
- newnode->prev = phead;
- // 新节点和第一个节点建立链接
- newnode->next = pheadNext;
- pheadNext->prev = newnode; */
-
- ListInsert(phead->next, x);
- }
- // 双向链表的头删
- void ListPopFront(ListNode* phead)
- {
- assert(phead);
- assert(phead->next != phead); // 只剩头节点时 链表为空 不能再继续删除
-
- /* ListNode* pheadNext = phead->next; // 记录第一个节点
- // 头节点和第一个节点的后继节点建立链接
- phead->next = pheadNext->next;
- pheadNext->next->prev = phead;
- free(pheadNext); // 头删 */
-
- ListErase(phead->next);
- }
- // 在双向链表中查找元素,并返回该元素的地址
- ListNode* ListFind(ListNode* phead, LTDataType x)
- {
- assert(phead);
-
- ListNode* cur = phead->next;
- while (cur != phead)
- {
- if (cur->data == x)
- {
- return cur; //找到了 返回该元素的地址
- }
- cur = cur->next;
- }
- return NULL; //没找到 返回NULL
- }
- // 在指定pos位置之前插入元素
- void ListInsert(ListNode* pos, LTDataType x)
- {
- assert(pos);
-
- ListNode* newnode = BuyListNode(x); // 申请一个节点
- ListNode* posPrev = pos->prev; // 记录pos的直接前驱
-
- // pos的直接前驱和新节点建立链接
- posPrev->next = newnode;
- newnode->prev = posPrev;
-
- // 新节点和pos建立链接
- newnode->next = pos;
- pos->prev = newnode;
- }
实现了该函数后,可以尝试改进头插函数(pos相当于链表的第一个节点)和尾插函数(pos相当于链表的头节点),这样写起来更简便。
- // 删除指定pos位置的元素
- void ListErase(ListNode* pos)
- {
- assert(pos);
-
- ListNode* posPrev = pos->prev; // 记录pos的直接前驱
- ListNode* posNext = pos->next; // 记录pos的直接后继
-
- // pos的直接前驱和直接后继建立链接
- posPrev->next = posNext;
- posNext->prev = posPrev;
-
- free(pos); // 释放pos位置的元素
- //pos = NULL;
- }
实现了该函数后,可以尝试改进头删函数(pos相当于链表的第一个节点)和尾删函数(pos相当于链表的最后一个节点),这样写起来更简便。
- // 双向链表的判空
- bool ListEmpty(ListNode* phead)
- {
- assert(phead);
- return phead->next == phead; //为空 返回ture 否则返回false
- }
- // 获取双向链表的元素个数
- size_t ListSize(ListNode* phead)
- {
- assert(phead);
-
- size_t size = 0;
- ListNode* cur = phead->next; // 记录第一个节点
- while (cur != phead)
- {
- size++;
- cur = cur->next;
- }
- return size;
- }
- // List.c
- #include "List.h"
-
- // 动态申请一个新节点
- ListNode* BuyListNode(LTDataType x)
- {
- ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
- newnode->data = x;
- newnode->prev = NULL;
- newnode->next = NULL;
- return newnode;
- }
-
- // 创建返回链表的头结点
- ListNode* ListCreate()
- {
- ListNode* phead = (ListNode*)malloc(sizeof(ListNode)); // 哨兵位头结点
- phead->next = phead;
- phead->prev = phead;
- return phead;
- }
-
- // 双向链表的销毁
- void ListDestroy(ListNode** pphead)
- {
- assert(pphead);
- assert(*pphead);
-
- ListNode* cur = (*pphead)->next;
- while (cur != *pphead)
- {
- ListNode* next = cur->next; // 记录cur的直接后继节点
- free(cur);
- cur = next;
- }
- free(*pphead); // 释放头节点
- *pphead = NULL; // 置空头指针
- }
-
- // 打印双向链表
- void ListPrint(ListNode* phead)
- {
- assert(phead);
-
- ListNode* cur = phead->next; // 记录第一个节点
- printf("head <-> ");
- while (cur != phead)
- {
- printf("%d <-> ", cur->data);
- cur = cur->next;
- }
- printf("head\n");
- }
-
- // 双向链表尾插
- void ListPushBack(ListNode* phead, LTDataType x)
- {
- assert(phead); // 头指针不能为空
- ListInsert(phead, x);
- }
-
- // 双向链表的尾删
- void ListPopBack(ListNode* phead)
- {
- assert(phead);
- assert(phead->next != phead); // 只剩头节点时 链表为空 不能再继续删除
- ListErase(pHead->prev);
- }
-
- // 双向链表的头插
- void ListPushFront(ListNode* phead, LTDataType x)
- {
- assert(phead);
- ListInsert(phead->next, x);
- }
-
- // 双向链表的头删
- void ListPopFront(ListNode* phead)
- {
- assert(phead);
- assert(phead->next != phead); // 只剩头节点时 链表为空 不能再继续删除
- ListErase(phead->next);
- }
-
- // 在双向链表中查找元素,并返回该元素的地址
- ListNode* ListFind(ListNode* phead, LTDataType x)
- {
- assert(phead);
-
- ListNode* cur = phead->next;
- while (cur != phead)
- {
- if (cur->data == x)
- {
- return cur; //找到了 返回该元素的地址
- }
- cur = cur->next;
- }
- return NULL; //没找到 返回NULL
- }
-
- // 在指定pos位置之前插入元素
- void ListInsert(ListNode* pos, LTDataType x)
- {
- assert(pos);
-
- ListNode* newnode = BuyListNode(x); // 申请一个节点
- ListNode* posPrev = pos->prev; // 记录pos的直接前驱
-
- // pos的直接前驱和新节点建立链接
- posPrev->next = newnode;
- newnode->prev = posPrev;
-
- // 新节点和pos建立链接
- newnode->next = pos;
- pos->prev = newnode;
- }
-
- // 删除指定pos位置的元素
- void ListErase(ListNode* pos)
- {
- assert(pos);
-
- ListNode* posPrev = pos->prev; // 记录pos的直接前驱
- ListNode* posNext = pos->next; // 记录pos的直接后继
-
- // pos的直接前驱和直接后继建立链接
- posPrev->next = posNext;
- posNext->prev = posPrev;
-
- free(pos); // 释放pos位置的元素
- //pos = NULL;
- }
-
- // 双向链表的判空
- bool ListEmpty(ListNode* phead)
- {
- assert(phead);
- return phead->next == phead; //为空 返回ture 否则返回false
- }
-
- // 获取双向链表的元素个数
- size_t ListSize(ListNode* phead)
- {
- assert(phead);
-
- size_t size = 0;
- ListNode* cur = phead->next; // 记录第一个节点
- while (cur != phead)
- {
- size++;
- cur = cur->next;
- }
- return size;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。