用c语言实现的一个简单的双链表操作程序,包括初始化、后插插入、删除数据、遍历链表、用户输入输出数据可视化交互以及释放链表内存等功能。

一、代码解析

1. 节点结构、双链表结构定义

 

a. Node结构体

  • 每个节点包含一个数据域(data)和两个指针域(pervnext)。

  • perv指向当前节点的前一个节点,next指向当前节点的后一个节点。

  • 这种双向指针结构使得双链表可以同时向前和向后遍历。

b .DoubleLinkedList结构体:

  • head:用于快速访问链表的开头。

  • tail:用于快速访问链表的末尾。

  • 通过维护head和tail指针,可以快速访问链表头尾,并且很大程度上简化了插入、删除等操作。

2.初始化

 

a.函数定义

DoubleLinkedList* list 是指向双链表结构体的指针, 返回类型为void。

b. 函数逻辑功能

①检查传递的指针为空指针,防止程序崩溃,并且将错误信息返回,提示初始化失败。

②初始化链表,将链表的headtail指针都设置为NULL,表示链表为空,即初始化状态没有节点。

③将初始化成功信息打印输出。

 3.后插插入操作

a. 函数定义

参数:

list:指向双链表结构体的指针。

data:用户输入的数据。 

b. 函数逻辑功能

①分配内存

使用malloc函数向内存申请了大小为结构体Node的一块空间,并且在返回类型void*强制转换成Node*,赋值给新的结构体指针变量newNode。

②检查内存分配是否成功

用fi()语句判断,newNode是否被赋予NULL,如果newNodeNULL,说明内存分配失败,打印错误信息并返回。

 ③初始化新节点
  • 将新节点的data(用户输入的数据)设置为传入的data参数。

  • 初始化pervnext指针为NULL,因为新节点暂时没有前驱和后继。可理解为第一个节点即最后一个节点。

④插入新节点
  • 如果链表为空(headNULL),将新节点同时设置为头节点和尾节点。

  • 如果链表不为空:        👉 当前尾节点的next指针指向新节点。
                                         👉 新节点的perv指针指向当前尾节点。
                                         👉 更新tail指针为新节点。

图形解释:切记语句顺序不可颠倒!!! 

 

⑤打印成功信息 

打印一条信息,提示用户数据已成功插入到链表尾部。

4.删除操作 

a. 函数定义

功能:👉 从双链表中删除包含特定数据的节点。

  • 参数

    • list:指向双链表结构体的指针。

    • data:要删除的节点数据。

 b. 函数逻辑功能

①检查此前链表是否为空

很有可能用户并没有输入插入数据,链表为空,程序执行空语句是切记不运行的。如果链表为空(headNULL),打印错误信息并返回。

②遍历链表查找目标节点

从头节点开始,利用while循环,只要链表不为NULL就遍历链表直到找到包含data的节点。如果遍历完整个链表都没有找到目标节点,打印删除失败的信息。

③删除节点

根据目标节点的位置(头节点、尾节点或中间节点),执行不同的删除操作:

条件1:❀删除唯一节点

如果节点的前驱指针和后驱指针同样都是指向NULL,则说经既是头节点又是尾节点(即链表只有一个节点),随后将headtail都设为NULL。即完成删除操作。

条件2:❀❀ 删除头结点

如果节点的前驱节点指向为NULL,则说明是头节点,随后更新head将其设置为当前节点a的下一个节点b,并将新头节点的perv指针设为NULL

条件3:❀❀❀删除尾节点

 

如果节点的后驱指针指向为NULL,则说明是尾节点,更新tail将其设置为当前节点b的前驱节点a,并将新尾节点的next指针设为NULL

图示:即上图删除next。

条件4:❀❀❀❀删除中间节点

 

如果节点是中间节点,调整前驱节点的next指针和后继节点的perv指针,绕过当前节点。

 

④释放内存并打印成功信息

 

如果遍历完整个链表都没有找到目标节点,打印删除失败的信息。

 

5. 释放链表内存

a.函数定义

  • 功能:释放双链表占用的内存。

  • 参数

    • list:指向双链表结构体的指针。
       

b.函数逻辑功能

①初始化指针

 

current 指针初始化为链表的头节点。
temp 指针用于临时保存当前节点,以便释放内存。

②遍历链表并释放内存

使用while循环遍历链表,直到currentNULL

在每次循环中:

current的值赋给临时创建的结构体指针空间temp,保存当前节点的引用。

current移动到下一个节点。

使用free释放temp指向的节点内存。

 清理链表头尾指针

将链表的headtail指针都设为NULL,表示链表已清空。

④最后打印释放成功信息。 

 

6.菜单函数

利用printf()函数打印简单的菜单功能即可。

 

7. main函数

①变量的定义

 

  • list:定义一个双链表结构体变量,用于存储链表。

  • choice:存储用户选择的菜单选项。

  • data:存储用户输入的数据。

  • initialized:标志变量,用于判断链表是否已初始化。

主循环

 

 

  • 使用while (1)创建一个无限循环,持续显示菜单并处理用户输入。

  • 调用Menu()函数显示操作菜单。

  • 使用scanf_s读取用户输入的菜单选项。

③使用switch语句调用相应的函数 

选项1:初始化链表

  • 调用initList函数初始化链表。

  • initialized标志设为1,表示链表已初始化。

 

选项2:后插插入数据

  • 检查链表是否已初始化,未初始化则提示用户。

  • 提示用户输入要插入的数据。

  • 调用insertAtEnd函数将数据插入链表尾部。

  • 调用traverseList函数遍历并打印链表内容。

 

选项3:删除数据

  • 检查链表是否已初始化,未初始化则提示用户。

  • 提示用户输入要删除的数据。

  • 调用deleteNode函数删除链表中的数据。

  • 调用traverseList函数遍历并打印链表内容。

 

选项4:遍历链表

  • 检查链表是否已初始化,未初始化则提示用户。

  • 调用traverseList函数遍历并打印链表内容。

 

选项5:退出程序

 

  • 如果链表已初始化,调用freeList函数释放链表内存。

  • 打印退出信息。

  • 使用exit(0)退出程序。

默认选项:无效选择

 

 

二、完整代码演示 

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
// 定义双链表--节点结构
typedef struct Node
{
	int data;                           // 节点存储的数据
	struct Node* perv;                  // 指向前驱节点的指针
	struct Node* next;                  // 指向后继节点的指针

}Node;


// 定义双链表---结构 
typedef struct
{
	Node* head;                         // 指向链表的头节点
	Node* tail;                         // 指向链表的尾节点
}DoubleLinkedList;




// 初始化双链表
void initList(DoubleLinkedList* list)
{
	if (list == NULL)                    // 检查是否为空指针
	{
		printf("初始化双链表失败\n");
		return;
	}
	list->head = NULL;                   // 初始化为空指针,即无节点
	list->tail = NULL;
	printf("双链表已初始化。\n");
}


// 后插插入
void insertAtEnd(DoubleLinkedList* list, int data)
{
	Node* newNode = (Node*)malloc(sizeof(Node));
	if (newNode == NULL)
	{
		printf("内存分配失败!\n");
		return;
	}

	newNode->data = data;
	newNode->perv = NULL;
	newNode->next = NULL;

	if (list->head == NULL)                      // 如果链表为空
	{
		list->head = newNode;                    // 新节点成为头节点
		list->tail = newNode;                    // 新节点成为尾节点
	}
	else                                         // 将新节点插入到链表尾部
	{
		list->tail->next = newNode;              // 当前尾节点的next指针指向新节点
		newNode->perv = list->tail;              // 新节点的perv指针指向当前尾节点
		list->tail = newNode;                    // 更新尾节点为新节点
	}
	printf("数据 %d 已插入到链表尾部。\n", data);

}


// 遍历链表
void traverseList(DoubleLinkedList* list)
{
	if (list->head == NULL)
	{
		printf("链表为空!\n");
		return;
	}

	Node* current = list->head;
	printf("链表内容:>");
	while (current != NULL)
	{
		printf("%d", current->data);
		current = current->next;
	}
	printf("\n");
}



// 删除节点
void deleteNode(DoubleLinkedList* list, int data)
{
	if (list->head == NULL)   // 如果链表为空(head为NULL),打印错误信息并返回                                   
	{
		printf("链表为空,无法删除!\n");
		return;
	}
	Node* current = list->head;  // 将头节点赋值给current
	while (current != NULL)      // 从头节点开始,遍历链表直到找到包含data的节点
	{                            // 如果遍历完整个链表都没有找到目标节点,打印删除失败的信息
		if (current->data == data)     // 将用户输入的数据在current链表中
		{		
			if (current->perv == NULL && current->next == NULL)  // 如果节点既是头节点又是尾节点(即链表只有一个节点),将head和tail都设为NULL
			{				
				list->head = NULL;
				list->tail = NULL;
			}
			else if (current->perv == NULL) // 如果节点是头节点,更新head为当前节点的下一个节点,并将新头节点的perv指针设为NULL
			{							
				list->head = current->next;
				list->head->perv = NULL;
			}
			else if (current->next == NULL)	// 如果节点是尾节点,更新tail为当前节点的前驱节点,并将新尾节点的next指针设为NULL
			{			
				list->tail = current->perv;
				list->tail->next = NULL;
			}
			else                            // 如果节点是中间节点,调整前驱节点的next指针和后继节点的perv指针,绕过当前节点
			{		
				current->perv->next = current->next;
				current->next->perv = current->perv;
			}
			free(current);                  // 释放被删除节点的内存
			printf("数据 %d 已从链表中删除。\n", data);
			return;
		}
		current = current->next;
	}
	printf("未找到数据 %d,删除失败!\n", data);
}



// 释放链表内存
void freeList(DoubleLinkedList* list)
{
	Node* current = list->head;          // 指针初始化为链表的头节点。
	Node* temp;                          // temp指针用于临时保存当前节点,以便释放内存

	while (current != NULL)
	{
		temp = current;                  // current的值赋给temp,保存当前节点的引用
		current = current->next;         // 将current移动到下一个节点
		free(temp);                      // 释放空间
	}

	list->head = NULL;                   // 设为NULL,表示链表已空
	list->tail = NULL;
	printf("链表已释放\n");

}


void Menu()
{
	printf("---------------------------------------------------------\n");
	printf("---------1.初始化双链表-------------2.后插插入数据-------\n");
	printf("---------3.删除数据-----------------4.遍历数表-----------\n");
	printf("---------5.退出程序--------------------------------------\n");
	printf("---------------------------------------------------------\n");
	printf("请输入你的选择:>");

}

int main()
{	
	DoubleLinkedList list;
	int choice = 0;
	int data = 0;
	int initialized = 0;

	printf("欢迎使用双链表操作程序!\n");

	while (1)
	{
		Menu();
		scanf_s("%d", &choice);

		switch (choice)
		{
		case 1:
			initList(&list);
			initialized = 1;
			break;
		
		case 2:
			if (!initialized)                       // 检查链表是否已初始化,未初始化则提示用户
			{
				printf("请先初始化链表!\n");
				break;
			}
			printf("请输入要插入的数据: >");
			scanf_s("%d", &data);
			insertAtEnd(&list, data);               // 调用insertAtEnd函数将数据插入链表尾部            
			traverseList(&list);                    // 调用traverseList函数遍历并打印链表内容
			break;

		case 3:
			if (!initialized)
			{
				printf("请先初始化链表!\n");
				break;
			}
			printf("请输入要删除的数据: ");
			scanf_s("%d", &data);
			deleteNode(&list, data);                // 调用deleteNode函数删除链表中的数据
			traverseList(&list);
			break;

		case 4:
			if (!initialized)
			{
				printf("请先初始化链表!\n");
				break;
			}
			traverseList(&list);
			break;

		case 5:
			if (initialized)                        // 如果链表已初始化,调用freeList函数释放链表内存
			{
				freeList(&list);
			}
			printf("感谢使用双链表操作程序!再见!\n");
			exit(0);

		default:
			printf("无效的选择,请重新输入!\n");
		}
	}
	return 0;
}

这段代码提供了一个完整的双链表操作示例,涵盖了基本的链表操作,适用于学习和理解双链表的实现和使用。❀❀❀

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值