在上一篇文章中,我们认识了顺序表,但是在许多情况中,顺序表在处理一些事件时还存在许多问题,比如:
1.头插、头删或者在中部的插入或删除需要移动大量的元素,时间复杂度过高。
2.增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。
3.增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为50,满了以后增容到 200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了45个数据空间。
为了解决这些问题,我们提出了如下结构,链表。
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
结构:列表中存在数据域与指针域,数据域用于存放该地区的值,指针域用于存放指向的下一个目标的地址。
typedef int SLTDataType;
//single list
typedef struct SListNode {
//数据域与指针域
SLTDataType data;
struct SListNode* next;
}SLTNode;
上面就是常见的单链表的结构:
1.链表在逻辑上连续,在物理上不连续
2.每一个新的区域都是动态申请出来的,申请出的区域可以连续也可以不连续
1.单向和双向链表
2.带头和不带头
3.循环或非循环
虽然链表的分类有很多,但在实际情况中,我们并不是都用的,比较常用的链表就是无头单向链表与带头双向循环链表。
无头单向链表
带头双向循环链表
以下,我们就来实现一下这两个链表
1)结构定义:单向不带头链表分为指针域和数据域。其中指针域存放下一个位置的地址,数据域存放当前位置的值。
typedef int SLTDataType;
//single list
typedef struct SListNode {
//数据域与指针域
SLTDataType data;
struct SListNode* next;
}SLTNode;
2)尾插
//尾插
void SListPushBack(SLTNode** pphead,SLTDataType x)
{
SLTNode* newNode = CreatNode(x);
//指针未指向任何位置,表明链表中还没有值
if (*pphead == NULL) {
*pphead = newNode;
}
else {
//新建一个临时节点,用于寻找最后一个节点
SLTNode* cur = *pphead;
//找最后一个节点(尾节点指向NULL位置)
while (cur->next != NULL) {
cur = cur->next;
}
//尾节点指针域存放新节点地址
cur->next = newNode;
}
}
3)头插
//头插
void SListPushFront(SLTNode** pphead, SLTDataType x) {
SLTNode* newNode = CreatNode(x);
//让新节点指针域指向当前首节点
newNode->next = *pphead;
//新插入的节点变为了首节点
*pphead = newNode;
}
4)尾删
//尾删
void SListPopBack(SLTNode** pphead) {
//表里面没有值
assert(*pphead);
//表中只有一个值
if ((*pphead)->next == NULL) {
free(*pphead);
*pphead = NULL;
}
//表中有一个或者一个以上的值
else {
SLTNode* cur = *pphead;
SLTNode* prev = NULL;
//找到尾节点和前一个节点
while (cur->next != NULL) {
prev = cur;
cur = cur->next;
}
free(cur);
cur = NULL;
prev->next = NULL;
}
}
5)头删
//头删
void SListPopFront(SLTNode** pphead) {
assert(*pphead);
//建立一个临时节点存储当前首节点指针域指向的地址,即第二个节点的地址
SLTNode* cur = (*pphead)->next;
//释放当前首节点的值
free(*pphead);
*pphead = NULL;
//为首节点赋上第二个节点的值
*pphead = cur;
}
6)在位置前插入
//在pos前插入
void SListInsert(SLTNode** pphead,SLTNode* pos,SLTDataType x) {
//pos位置就是首结点,就转换为头插
if (pos == *pphead) {
SListPushFront(pphead,x);
}//pos位置是其他的节点
else {
SLTNode* newNode = CreatNode(x);
SLTNode* prev = *pphead;
while (prev->next != pos) {
prev = prev->next;
}
//将前一个的指针域指向插入的值的地址:newNode
prev->next = newNode;
//将新的值指针域指向pos位置
newNode->next = pos;
}
}
7)在位置后插入
//在pos后插入
void SListInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x) {
//建立一个新的节点
SLTNode* newNode = CreatNode(x);
//建立一个临时变量存储pos后面的节点
SLTNode* after = pos->next;
//让pos指向新节点
pos->next = newNode;
//让新节点指向刚刚pos后面的节点
newNode->next = after;
}
8)删除位置的值
//删除pos位置的值
void SListErase(SLTNode** pphead,SLTNode* pos) {
//pos位置与首节点重合,转换为头删
if (pos == *pphead) {
SListPopFront(pphead);
}
else {
SLTNode* prev = *pphead;
//找pos的前一个位置
while (prev->next != pos) {
prev = prev->next;
}
//将前一个值指向pos的后一个值
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
9)删除位置后的值
//删除pos后面的值
void SListEraseAfter(SLTNode** pphead,SLTNode* pos) {
//建立一个临时变量存储pos后两位的位置
SLTNode* after = pos->next->next;
free(pos->next);
pos->next = NULL;
//让pos指向刚刚后两位的位置
pos->next = after;
}
10)按值查找
//按值查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x) {
SLTNode* cur = phead;
while (cur) {
if (cur->data == x) {
return cur;
}
cur = cur->next;
}
return NULL;
}
11)修改
//修改
void SListModify(SLTNode** pphead, SLTNode* pos,SLTDataType x) {
pos->data = x;
}
12)保存
//保存
void SListSave(SLTNode* pphead) {
FILE* pf = fopen("SListNode.txt","wb");
if (pphead == NULL) {
fclose(pf);
pf = NULL;
}
else {
SLTNode* cur = pphead;
while (cur != NULL) {
fwrite(cur,sizeof(SLTDataType),1,pf);
cur = cur->next;
}
}
}
13)打印
//打印
void SListPrint(SLTNode* phead) {
SLTNode* cur = phead;
if (cur == NULL) {
//如果链表中无元素,则cur == NULL,不进入循环
printf("NULL\n");
}
else {
//一直遍历到最后一个位置:尾节点指向的NULL位置
while (cur != NULL) {
//打印数据
printf("%d ", cur->data);
//根据指针跳转到下一个位置
cur = cur->next;
}
printf("\n");
}
}
14)清空
//清空
void SListClear(SLTNode** pphead) {
assert(*pphead);
//清空链表,链表之后还要使用,所以我们只将首位置置为NULL,不释放
//这里从第二个位置开始释放
SLTNode* cur = (*pphead)->next;
SLTNode* after = NULL;
while (cur != NULL) {
//先记录下一个节点的位置
after = cur->next;
//释放当前节点
free(cur);
cur = NULL;
cur = after;
}
//清空链表,链表之后还要使用,所以我们只将首位置置为NULL,不释放
*pphead = NULL;
}
15)销毁
//销毁
void SListDestroy(SLTNode** pphead) {
assert(*pphead);
//销毁链表,链表之后不能使用了,所以将首位置也一并释放
SLTNode* cur = *pphead;
SLTNode* after = NULL;
while (cur != NULL) {
//先记录下一个节点的位置
after = cur->next;
//释放当前节点
free(cur);
cur = NULL;
cur = after;
}
}
1)SListNode.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDataType;
//single list
typedef struct SListNode {
//数据域与指针域
SLTDataType data;
struct SListNode* next;
}SLTNode;
//打印
void SListPrint(SLTNode* phead);
//头插
void SListPushFront(SLTNode** pphead, SLTDataType x);
//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);
//头删
void SListPopFront(SLTNode** pphead);
//尾删
void SListPopBack(SLTNode** pphead);
//按值查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
//在pos前插入
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在pos后插入
void SListInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos);
//删除pos后面的值
void SListEraseAfter(SLTNode** pphead, SLTNode* pos);
//修改
void SListModify(SLTNode **pphead, SLTNode* pos, SLTDataType x);
//保存
void SListSave(SLTNode* pphead);
//清空
void SListClear(SLTNode** pphead);
//销毁
void SListDestroy(SLTNode** pphead);
2)SListNode.c
#define _CRT_SECURE_NO_WARNINGS
#include"SListNode.h"
//打印
void SListPrint(SLTNode* phead) {
SLTNode* cur = phead;
if (cur == NULL) {
//如果链表中无元素,则cur == NULL,不进入循环
printf("NULL\n");
}
else {
//一直遍历到最后一个位置:尾节点指向的NULL位置
while (cur != NULL) {
//打印数据
printf("%d ", cur->data);
//根据指针跳转到下一个位置
cur = cur->next;
}
printf("\n");
}
}
//建立一个新的,可以长久储存的节点
SLTNode* CreatNode(SLTDataType x) {
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL) {
perror("malloc");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
//尾插
void SListPushBack(SLTNode** pphead,SLTDataType x)
{
SLTNode* newNode = CreatNode(x);
//指针未指向任何位置,表明链表中还没有值
if (*pphead == NULL) {
*pphead = newNode;
}
else {
//新建一个临时节点,用于寻找最后一个节点
SLTNode* cur = *pphead;
//找最后一个节点(尾节点指向NULL位置)
while (cur->next != NULL) {
cur = cur->next;
}
//尾节点指针域存放新节点地址
cur->next = newNode;
}
}
//头插
void SListPushFront(SLTNode** pphead, SLTDataType x) {
SLTNode* newNode = CreatNode(x);
//让新节点指针域指向当前首节点
newNode->next = *pphead;
//新插入的节点变为了首节点
*pphead = newNode;
}
//头删
void SListPopFront(SLTNode** pphead) {
assert(*pphead);
//建立一个临时节点存储当前首节点指针域指向的地址,即第二个节点的地址
SLTNode* cur = (*pphead)->next;
//释放当前首节点的值
free(*pphead);
*pphead = NULL;
//为首节点赋上第二个节点的值
*pphead = cur;
}
//尾删
void SListPopBack(SLTNode** pphead) {
//表里面没有值
assert(*pphead);
//表中只有一个值
if ((*pphead)->next == NULL) {
free(*pphead);
*pphead = NULL;
}
//表中有一个或者一个以上的值
else {
SLTNode* cur = *pphead;
SLTNode* prev = NULL;
//找到尾节点和前一个节点
while (cur->next != NULL) {
prev = cur;
cur = cur->next;
}
free(cur);
cur = NULL;
prev->next = NULL;
}
}
//按值查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x) {
SLTNode* cur = phead;
while (cur) {
if (cur->data == x) {
return cur;
}
cur = cur->next;
}
return NULL;
}
//在pos前插入
void SListInsert(SLTNode** pphead,SLTNode* pos,SLTDataType x) {
//pos位置就是首结点,就转换为头插
if (pos == *pphead) {
SListPushFront(pphead,x);
}//pos位置是其他的节点
else {
SLTNode* newNode = CreatNode(x);
SLTNode* prev = *pphead;
while (prev->next != pos) {
prev = prev->next;
}
//将前一个的指针域指向插入的值的地址:newNode
prev->next = newNode;
//将新的值指针域指向pos位置
newNode->next = pos;
}
}
//在pos后插入
void SListInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x) {
//建立一个新的节点
SLTNode* newNode = CreatNode(x);
//建立一个临时变量存储pos后面的节点
SLTNode* after = pos->next;
//让pos指向新节点
pos->next = newNode;
//让新节点指向刚刚pos后面的节点
newNode->next = after;
}
//删除pos位置的值
void SListErase(SLTNode** pphead,SLTNode* pos) {
//pos位置与首节点重合,转换为头删
if (pos == *pphead) {
SListPopFront(pphead);
}
else {
SLTNode* prev = *pphead;
//找pos的前一个位置
while (prev->next != pos) {
prev = prev->next;
}
//将前一个值指向pos的后一个值
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
//删除pos后面的值
void SListEraseAfter(SLTNode** pphead,SLTNode* pos) {
//建立一个临时变量存储pos后两位的位置
SLTNode* after = pos->next->next;
free(pos->next);
pos->next = NULL;
//让pos指向刚刚后两位的位置
pos->next = after;
}
//修改
void SListModify(SLTNode** pphead, SLTNode* pos,SLTDataType x) {
pos->data = x;
}
//保存
void SListSave(SLTNode* pphead) {
FILE* pf = fopen("SListNode.txt","wb");
if (pphead == NULL) {
fclose(pf);
pf = NULL;
}
else {
SLTNode* cur = pphead;
while (cur != NULL) {
fwrite(cur,sizeof(SLTDataType),1,pf);
cur = cur->next;
}
}
}
//清空
void SListClear(SLTNode** pphead) {
assert(*pphead);
//清空链表,链表之后还要使用,所以我们只将首位置置为NULL,不释放
//这里从第二个位置开始释放
SLTNode* cur = (*pphead)->next;
SLTNode* after = NULL;
while (cur != NULL) {
//先记录下一个节点的位置
after = cur->next;
//释放当前节点
free(cur);
cur = NULL;
cur = after;
}
//清空链表,链表之后还要使用,所以我们只将首位置置为NULL,不释放
*pphead = NULL;
}
//销毁
void SListDestroy(SLTNode** pphead) {
assert(*pphead);
//销毁链表,链表之后不能使用了,所以将首位置也一并释放
SLTNode* cur = *pphead;
SLTNode* after = NULL;
while (cur != NULL) {
//先记录下一个节点的位置
after = cur->next;
//释放当前节点
free(cur);
cur = NULL;
cur = after;
}
}
3)Test.c
#define _CRT_SECURE_NO_WARNINGS
#include"SListNode.h"
void menu() {
printf("*******************************\n");
printf("***1、头插 2、尾插 ***\n");
printf("***3、头删 4、尾删 ***\n");
printf("***5、打印 6、按值查找 ***\n");
printf("***7、前插 8、后插 ***\n");
printf("***9、删除 10、后删 ***\n");
printf("***11、修改 12、保存 ***\n");
printf("***13、清空 14、销毁 ***\n");
printf("***-1、退出 ***\n");
printf("*******************************\n");
}
enum {
PushFront = 1,
PushBack,
PopFront,
PopBack,
Print,
FindByValue,
Insert,
InsertAfter,
Erase,
EraseAfter,
Modify,
Save,
Clear,
Destroy,
Exit = -1
};
int main() {
SLTNode* s = NULL;
SLTDataType x;
SLTNode* pos;
int input = 0;
do {
menu();
printf("请输入你想进行的操作:");
scanf("%d", &input);
switch (input) {
case PushFront:
printf("请输入你要插入的数据,以-1结束\n");
do {
scanf("%d", &x);
if (x != -1)
{
SListPushFront(&s,x);
}
} while (x != -1);
break;
case PushBack:
printf("请输入你要插入的数据,以-1结束\n");
do {
scanf("%d", &x);
if (x != -1)
{
SListPushBack(&s,x);
}
} while (x != -1);
break;
case PopFront:
SListPopFront(&s);
break;
case PopBack:
SListPopBack(&s);
break;
case Print:
SListPrint(s);
break;
case FindByValue:
printf("请输入你想要查找的值:");
scanf("%d", &x);
pos = SListFind(s,x);
if (pos == NULL) {
printf("链表中没有这个值\n");
}
else {
printf("找到了\n");
}
break;
case Insert:
printf("请输入你想要在哪个值前插入:");
scanf("%d", &x);
pos = SListFind(s,x);
printf("请输入你想要插入的值:");
scanf("%d", &x);
if (pos == NULL) {
printf("链表中没有这个值,请检查你输入的值是否正确\n");
}
else {
SListInsert(&s, pos, x);
}
break;
case InsertAfter:
printf("请输入你想要在哪个值后插入:");
scanf("%d", &x);
pos = SListFind(s, x);
printf("请输入你想要插入的值:");
scanf("%d", &x);
if (pos == NULL) {
printf("链表中没有这个值,请检查你输入的值是否正确\n");
}
else {
SListInsertAfter(&s, pos, x);
}
break;
case Erase:
printf("请输入你想要删除的值:");
scanf("%d", &x);
pos = SListFind(s, x);
if (pos == NULL) {
printf("链表中没有这个值,请检查你输入的值是否正确\n");
}
else {
SListErase(&s, pos);
}
break;
case EraseAfter:
printf("请输入你想要删除哪个值之后的值:");
scanf("%d", &x);
pos = SListFind(s, x);
if (pos == NULL) {
printf("链表中没有这个值,请检查你输入的值是否正确\n");
}
else if (pos->next == NULL) {
printf("这个值后已经没有值了,无法进行删除,请检查你输入的值是否正确\n");
}
else {
SListEraseAfter(&s, pos);
}
break;
case Modify:
printf("请输入你想要修改的值:");
scanf("%d", &x);
pos = SListFind(s, x);
printf("请输入修改后的值:");
scanf("%d", &x);
if (pos == NULL) {
printf("链表中没有这个值,请检查你输入的值是否正确\n");
}
else {
SListModify(&s, pos, x);
}
break;
case Save:
SListSave(s);
break;
case Clear:
SListClear(&s);
break;
case Destroy:
SListDestroy(&s);
break;
case Exit:
break;
default:
printf("输入值错误,请重新输入\n");
}
} while (input != Exit);
return 0;
}
1)结构定义:带头双向循环链表分为数据域、前指针域和后指针域。其中前指针域存放前一个位置的地址,后指针域存放后一个位置的地址。
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType val;
}ListNode;
2)初始化头结点:创建一个头节点。前后指针域都指向自己,数据域不做处理。
//初始化创建头结点
ListNode* ListInit()
{
ListNode* phead =ListCreate(0);
//前指针域
phead->next = phead;
//后指针域
phead->prev = phead;
return phead;
}
3)插入
//创建新节点
ListNode* ListCreate(LTDataType x)
{
//动态申请内存
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
if (newNode == NULL) {
perror("malloc");
}
//赋值
newNode->val = x;
return newNode;
}
//按位置插入
void ListInsert(ListNode* pos, LTDataType x){
assert(pos);
//建立新节点
ListNode* newNode = ListCreate(x);
//临时节点存储插入位置的前一个位置地址
ListNode* prev = pos->prev;
//将新节点后指针域存储插入位置地址
newNode->next = pos;
//将插入位置前指针域存储新节点位置
pos->prev = newNode;
//插入位置前一个位置的后指针域存储新节点位置
prev->next = newNode;
//将新节点前指针域存储插入位置前一个位置地址
newNode->prev = prev;
}
4)删除
//按位置删除
void ListErase(ListNode* pos) {
assert(pos);
//创建临时节点存储插入位置前后节点地址
ListNode* prev = pos->prev;
ListNode* next = pos->next;
//将前节点的后指针指向后节点
prev->next = next;
//将后节点的前指针指向前节点
next->prev = prev;
free(pos);
pos = NULL;
}
5)头插
// 头插
void ListPushFront(ListNode* pHead, LTDataType x) {
assert(pHead);
ListInsert(pHead->next,x);
}
6)尾插
// 尾插
void ListPushBack(ListNode* pHead, LTDataType x) {
assert(pHead);
ListInsert(pHead,x);
}
7)头删
// 头删
void ListPopFront(ListNode* pHead) {
assert(pHead);
ListErase(pHead->next);
}
8)尾删
// 尾删
void ListPopBack(ListNode* pHead) {
assert(pHead);
ListErase(pHead->prev);
}
9)查找
//查找
ListNode* ListFind(ListNode* pHead, LTDataType x){
assert(pHead);
//新建临时节点作为首元素节点
ListNode* tail = pHead->next;
while (tail != pHead) {
if (tail->val == x) {
return tail;
}
tail = tail->next;
}
return NULL;
}
10)打印
//打印
void ListPrint(ListNode* pHead) {
assert(pHead);
if (pHead->next == pHead) {
printf("表中无元素\n");
return;
}
ListNode* tail = pHead->next;
while (tail != pHead) {
printf("%d ",tail->val);
tail = tail->next;
}
printf("\n");
}
11)清空
//清空
void ListClear(ListNode* pHead) {
assert(pHead);
ListNode* tail = pHead->next;
//依次对各个空间进行释放
while (tail != pHead) {
ListNode* next = tail->next;
free(tail);
tail = NULL;
tail = next;
}
//修改头结点前后指针域
pHead->next = tail;
pHead->prev = tail;
}
12)销毁
//销毁
void ListDestory(ListNode* pHead) {
assert(pHead);
ListNode* tail = pHead->next;
//依次对各个空间进行释放
while (tail != pHead) {
ListNode* next = tail->next;
free(tail);
tail = NULL;
tail = next;
}
//释放头结点
free(pHead);
pHead = NULL;
}
1)ListNode.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType val;
}ListNode;
// 创建返回链表的头结点.
ListNode* ListInit();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
//清空
void ListClear(ListNode* pHead);
//打印
void ListPrint(ListNode* pHead);
2)ListNode.c
#define _CRT_SECURE_NO_WARNINGS
#include"ListNode.h"
//创建新节点
ListNode* ListCreate(LTDataType x)
{
//动态申请内存
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
if (newNode == NULL) {
perror("malloc");
}
//赋值
newNode->val = x;
return newNode;
}
//初始化创建头结点
ListNode* ListInit()
{
ListNode* phead =ListCreate(0);
//前指针域
phead->next = phead;
//后指针域
phead->prev = phead;
return phead;
}
//查找
ListNode* ListFind(ListNode* pHead, LTDataType x){
assert(pHead);
//新建临时节点作为首元素节点
ListNode* tail = pHead->next;
while (tail != pHead) {
if (tail->val == x) {
return tail;
}
tail = tail->next;
}
return NULL;
}
//按位置插入
void ListInsert(ListNode* pos, LTDataType x){
assert(pos);
//建立新节点
ListNode* newNode = ListCreate(x);
//临时节点存储插入位置的前一个位置地址
ListNode* prev = pos->prev;
//将新节点后指针域存储插入位置地址
newNode->next = pos;
//将插入位置前指针域存储新节点位置
pos->prev = newNode;
//插入位置前一个位置的后指针域存储新节点位置
prev->next = newNode;
//将新节点前指针域存储插入位置前一个位置地址
newNode->prev = prev;
}
//按位置删除
void ListErase(ListNode* pos) {
assert(pos);
//创建临时节点存储插入位置前后节点地址
ListNode* prev = pos->prev;
ListNode* next = pos->next;
//将前节点的后指针指向后节点
prev->next = next;
//将后节点的前指针指向前节点
next->prev = prev;
free(pos);
pos = NULL;
}
//打印
void ListPrint(ListNode* pHead) {
assert(pHead);
if (pHead->next == pHead) {
printf("表中无元素\n");
return;
}
ListNode* tail = pHead->next;
while (tail != pHead) {
printf("%d ",tail->val);
tail = tail->next;
}
printf("\n");
}
//清空
void ListClear(ListNode* pHead) {
assert(pHead);
ListNode* tail = pHead->next;
//依次对各个空间进行释放
while (tail != pHead) {
ListNode* next = tail->next;
free(tail);
tail = NULL;
tail = next;
}
//修改头结点前后指针域
pHead->next = tail;
pHead->prev = tail;
}
//销毁
void ListDestory(ListNode* pHead) {
assert(pHead);
ListNode* tail = pHead->next;
//依次对各个空间进行释放
while (tail != pHead) {
ListNode* next = tail->next;
free(tail);
tail = NULL;
tail = next;
}
//释放头结点
free(pHead);
pHead = NULL;
}
// 尾插
void ListPushBack(ListNode* pHead, LTDataType x) {
assert(pHead);
ListInsert(pHead,x);
}
// 尾删
void ListPopBack(ListNode* pHead) {
assert(pHead);
ListErase(pHead->prev);
}
// 头插
void ListPushFront(ListNode* pHead, LTDataType x) {
assert(pHead);
ListInsert(pHead->next,x);
}
// 头删
void ListPopFront(ListNode* pHead) {
assert(pHead);
ListErase(pHead->next);
}
3)Test.c
#define _CRT_SECURE_NO_WARNINGS
#include"ListNode.h"
void TestList1()
{
ListNode* plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushBack(plist, 4);
ListPrint(plist);
ListPushFront(plist, 0);
ListPushFront(plist, -1);
ListPrint(plist);
ListPopFront(plist);
ListPopFront(plist);
ListPopFront(plist);
ListPrint(plist);
ListPopBack(plist);
ListPrint(plist);
ListNode* pos = ListFind(plist, 3);
if (pos)
{
// 查找,附带着修改的作用
pos->val *= 10;
printf("找到了,并且节点的值乘以10\n");
}
else
{
printf("没有找到\n");
}
ListPrint(plist);
ListInsert(pos, 300);
ListPrint(plist);
ListErase(pos);
ListPrint(plist);
ListClear(plist);
ListPrint(plist);
ListDestory(plist);
}
int main() {
TestList1();
return 0;
}