文章目录
带头双向循环链表
双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。
首先创建三个文件
test.c主函数,测试我们链表的功能
ListNode.c,编写链表的函数
ListNode.h,编写函数的头文件
提示:以下是本篇文章正文内容,下面案例可供参考
(1)创建一个带头双向循环链表的结构体
typedef int LTDataType;
//创建链表的结构体
typedef struct ListNode
{
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
}LTNode;
(2)动态开辟节点
//动态开辟单链表的节点
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->prev = NULL;
newnode->next = NULL;
return newnode;
}
(3)链表的初始化
初始化带头的双向循环链表,我们要将next指向自己(头),prev也指向自己(头)。
//链表的初始化
LTNode* ListInit()
{
LTNode* head = BuyListNode(-1);
head->prev = head;
head->next = head;
return head;
}
(4)链表的尾插
//链表的尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
创建一个节点
//LTNode* newnode = BuyListNode(x);
//phead->prev->next = newnode;
//newnode->prev = phead->prev;
//newnode->next = phead;
//phead->prev = newnode;
//复用
ListPosInsert(phead, x);
}
(5)链表的尾删
//链表的尾删
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
//LTNode* tail = phead->prev; //找旧尾
//LTNode* tailprev = tail->prev; //找新尾
链接新的尾
//tailprev->next = phead;
//phead->prev = tailprev;
//
释放以前的尾
//free(tail);
ListPosErase(phead->prev);
}
(6)链表的头插
//链表的头插-是指在头节点的下一个插入
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
创建一个节点
//LTNode* newnode = BuyListNode(x);
保存好下一个节点
//LTNode* first = phead->next;
//phead->next = newnode;
//newnode->prev = phead;
//newnode->next = first;
//first->prev = newnode;
ListPosInsert(phead->next, x);
}
(7)链表的头删
//链表的头删-是指在头节点的下一个插入
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead); //防止只有一个头指针
//LTNode* first = phead->next;
//LTNode* second = first->next;
//phead->next = second;
//second->prev = phead;
//free(first);
ListPosErase(phead->next);
}
(8)链表的查找
//链表的查找
LTNode* ListFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
(9)在pos位置插入
比如有1 2 3 ,在2位置插入意思是,将2位置以及后面的数往后挪,新的数字占用2的位置
//链表的pos位置插入
void ListPosInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* prev = pos->prev;
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
(10)删除pos处
//链表的pos位置删除
void ListPosErase(LTNode* pos)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
(11)判断链表是否为空
//判断链表是否为空
bool ListEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
(12)计算链表的大小
//计算链表大小
size_t ListSize(LTNode* phead)
{
size_t size = 0;
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
size++;
cur = cur->next;
}
return size;
}
(13)销毁链表
//链表的销毁
void ListDeatroy(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
(14)链表的打印
//链表的打印
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
全部源码
ListNode.h
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int LTDataType;
//创建链表的结构体
typedef struct ListNode
{
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
}LTNode;
//动态开辟单链表的节点
LTNode* BuyListNode(LTDataType x);
//链表的初始化
LTNode* ListInit();
//链表的尾插
void ListPushBack(LTNode* phead, LTDataType x);
//链表的尾删
void ListPopBack(LTNode* phead);
//链表的头插
void ListPushFront(LTNode* phead, LTDataType x);
//链表的头删-是指在头节点的下一个插入
void ListPopFront(LTNode* phead);
//链表的打印
void ListPrint(LTNode* phead);
//链表的销毁
void ListDeatroy(LTNode* phead);
//链表的查找
LTNode* ListFind(LTNode* phead, LTDataType x);
//链表的pos位置插入
void ListPosInsert(LTNode* pos, LTDataType x);
//链表的pos位置删除
void ListPosErase(LTNode* pos);
//判断链表是否为空
bool ListEmpty(LTNode* phead);
//计算链表大小
size_t ListSize(LTNode* phead);
ListNode.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Dlist.h"
//动态开辟单链表的节点
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->prev = NULL;
newnode->next = NULL;
return newnode;
}
//链表的初始化
LTNode* ListInit()
{
LTNode* head = BuyListNode(-1);
head->prev = head;
head->next = head;
return head;
}
//链表的尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
创建一个节点
//LTNode* newnode = BuyListNode(x);
//phead->prev->next = newnode;
//newnode->prev = phead->prev;
//newnode->next = phead;
//phead->prev = newnode;
//复用
ListPosInsert(phead, x);
}
//链表的尾删
void ListPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
//LTNode* tail = phead->prev; //找旧尾
//LTNode* tailprev = tail->prev; //找新尾
链接新的尾
//tailprev->next = phead;
//phead->prev = tailprev;
//
释放以前的尾
//free(tail);
ListPosErase(phead->prev);
}
//链表的头插-是指在头节点的下一个插入
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
创建一个节点
//LTNode* newnode = BuyListNode(x);
保存好下一个节点
//LTNode* first = phead->next;
//phead->next = newnode;
//newnode->prev = phead;
//newnode->next = first;
//first->prev = newnode;
ListPosInsert(phead->next, x);
}
//链表的头删-是指在头节点的下一个插入
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead); //防止只有一个头指针
//LTNode* first = phead->next;
//LTNode* second = first->next;
//phead->next = second;
//second->prev = phead;
//free(first);
ListPosErase(phead->next);
}
//链表的打印
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
//链表的销毁
void ListDeatroy(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
//链表的查找
LTNode* ListFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
//链表的pos位置插入
void ListPosInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyListNode(x);
LTNode* prev = pos->prev;
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
//链表的pos位置删除
void ListPosErase(LTNode* pos)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
//判断链表是否为空
bool ListEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
//计算链表大小
size_t ListSize(LTNode* phead)
{
size_t size = 0;
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
size++;
cur = cur->next;
}
return size;
}
Test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Dlist.h"
//演示尾插尾删
void test1()
{
LTNode* plist = ListInit();
//尾插
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPrint(plist);
//尾删
ListPopBack(plist);
ListPopBack(plist);
//打印
ListPrint(plist);
ListDeatroy(plist);
}
//演示头插头删
void test2()
{
LTNode* plist = ListInit();
//头插
ListPushFront(plist, 1);
ListPushFront(plist, 2);
ListPushFront(plist, 3);
ListPrint(plist);
//头删
ListPopFront(plist);
ListPopFront(plist);
//打印
ListPrint(plist);
ListDeatroy(plist);
}
//演示查找元素和修改
void test3()
{
LTNode* plist = ListInit();
//尾插
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
printf("size=%d\n", ListSize(plist));
//打印
ListPrint(plist);
LTNode* pos = ListFind(plist, 5);
if (pos != NULL)
{
//ListPosInsert(pos, 50);
ListPosErase(pos);
ListPrint(plist);
}
else
{
printf("没有这个数\n");
}
ListDeatroy(plist);
}
int main()
{
test3();
return 0;
}