数据结构链表完整实现(负完整代码)

发布时间:2024年01月13日


前言


引入

在上一篇文章中,我们认识了顺序表,但是在许多情况中,顺序表在处理一些事件时还存在许多问题,比如:

1.头插、头删或者在中部的插入或删除需要移动大量的元素,时间复杂度过高。

2.增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗。

3.增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为50,满了以后增容到 200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了45个数据空间。

为了解决这些问题,我们提出了如下结构,链表。

1、链表定义及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

结构:列表中存在数据域与指针域,数据域用于存放该地区的值,指针域用于存放指向的下一个目标的地址。

typedef int SLTDataType;

//single list
typedef struct SListNode {
	//数据域与指针域
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

在这里插入图片描述

上面就是常见的单链表的结构:

1.链表在逻辑上连续,在物理上不连续

2.每一个新的区域都是动态申请出来的,申请出的区域可以连续也可以不连续

链表的分类

1.单向和双向链表

在这里插入图片描述

2.带头和不带头

在这里插入图片描述

3.循环或非循环

在这里插入图片描述

虽然链表的分类有很多,但在实际情况中,我们并不是都用的,比较常用的链表就是无头单向链表与带头双向循环链表。

无头单向链表

在这里插入图片描述

带头双向循环链表

在这里插入图片描述

以下,我们就来实现一下这两个链表

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;
}

4、带头双向循环链表实现

实现

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;
}
文章来源:https://blog.csdn.net/wjfwonderful/article/details/135577073
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。