C语言单链表功能实现(创建、增添、删除、修改)

可以直接通过代码和代码注释的结合来学习并理解链表的基本逻辑,比单纯的看文字理解代码更有乐趣,不过笔者建议在看代码之前可以先去本链接下的视频学习下链表会事半功倍哦~

视频来源B站大佬:@孤烟creater发布的视频

【动画演示】链表详解及其底层机制 C语言_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1S64y1W7u5/?spm_id_from=333.337.search-card.all.click&vd_source=d16e4170ec5d95766145b717597ec72f

#define  _CRT_SECURE_NO_WARNINGS 

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/****************************************单链表********************************/



typedef struct List_Node			//定义一个链表属性的结构体(节点)
{
	int list_val;					//存储链表内节点的值(数据域)
	struct List_Node* next_node;	//名为next_node的指针指向该节点的下一个节点的地址(指针域)
}List_Node_t;						//链表属性结构体的别名


/*创建一个有n和节点的单链表*/
List_Node_t* Single_Creat_List(List_Node_t* head,int n)
{
	//创建头节点
	//head = (List_Node_t*)malloc(sizeof(List_Node_t));//为头节点申请内存空间
	head->list_val = 1000;		//头节点值初始化为0
	head->next_node = NULL; //头节点指向下一个节点的地址为空(不指向)

	//开始添加节点构成一个单链表
	List_Node_t* current = head;          //设置当前正在操作的节点为头节点,从头节点开始
	for (int i = 0; i < n; i++)
	{
		List_Node_t* new_node = (List_Node_t*)malloc(sizeof(List_Node_t));//创造一个新的节点(还没有进入链表中),为节点申请内存空间
		new_node->list_val = i * 20;	//为新节点的数据域赋值
		new_node->next_node = NULL;		//指针置空,因为还没有成为链表中的一员
		current->next_node = new_node;  //将当前正在进行操作的节点的指针指向下一个节点的地址(比如现在正在操作的是头节点,那我就把头节点的指针域赋上它正要指向的节点的地址的值(如:它指向的那个节点的地址是:0x0025)
		current = current->next_node;   //让当前链表的节点的地址后移一个到它的下一节点去(就是说:我让指向当前的正在操作的节点的地址的指针*current等于当前的节点的指针域中的指针变量所指向的下一个节点的地址),这样,我就可以开始对头节点之后的节点进行操作的,
		//也就是说当前节点原本是头节点,经过这个之后,当前节点就变成了头节点的下一个节点了
	}
	return head;
}

/*遍历并打印每个节点的值*/
void Ergodic_List(List_Node_t* head)
{
	int j=0;
	List_Node_t* current = head;          //设置当前正在操作的节点为头节点,从头节点开始
	//遍历链表并输出节点值
	printf("开始输出节点\n");
	current = head;//从头节点开始
	while (current->next_node != NULL)//判断是否遍历结束
	{
		printf("第%d个节点的值为:%d\n", j++, current->list_val);
		current = current->next_node;
	}
}

/*释放整个链表内存空间*/
void Free_List(List_Node_t* head)
{
	//释放内存,因为这只是个测试代码,创建成功后打印出来就不会再用了,所以需要把真个链表内存都释放了
	List_Node_t* current = head;                             //设置当前正在操作的节点为头节点,从头节点开始
	while (current->next_node != NULL)//判断是否遍历完成
	{
		List_Node_t* temp = current;//创建一个中间变量来传递指针,这样就能保证current的指针能一直传递下去,相当于让*current作为一个遍历的工具
		current = current->next_node;//移向下一个节点
		free(temp);//释放已经遍历过的节点指针
	}

}


/*将要添加的元素添加到指定的链表处*/
List_Node_t* List_Node_add(List_Node_t* head, int val,int pos)
{
	int i = 0;
	List_Node_t* new_node = (List_Node_t*)malloc(sizeof(List_Node_t));//创建一个新节点
	new_node -> list_val = val;                                       //为新节点数据域赋值
	new_node -> next_node= NULL;									  //将新节点指针域置空
	if (head == NULL)		                                          //如果链表的头节点不存在,即链表为空										  
	{
		head = new_node;                                              //让新建的节点成为该链表的头节点
	}
	else				  											  //如果是添加到链表中间
	{
		List_Node_t* prev = NULL;                                      //用于存放要插入的节点的地址的指针      
		List_Node_t* cur  = head;                                      //开始从头节点遍历
		while (cur != NULL&&i<pos)                                     //遍历到尾节点或者到达要插入的节点处时,就不再进行遍历
		{
			prev = cur;												  //获取要插入的节点的属性(前一个)	
			i++;                                                      //遍历每个节点的序号
			cur = cur->next_node;                                     //向后移动一个节点的位置(后一个)
		}//这个while的目的是为了得到两个连续的节点的地址
		if (prev == NULL)                                             //如果是插入到头节点之前的话,比如pos小于
		{
			new_node->next_node = head;								  //新节点的的指针域就会指向原头节点的地址成为新的头节点
			head = new_node;                                          //新的节点正式变为了头节点,而头节点没被释放同时还是被新的头节点指针域指向,所以.老的头节点变为了第二个节点
		}
		else                                                          //插入到链表中间
		{
			prev->next_node = new_node;                               //让前一个节点指向新节点
			new_node->next_node = cur;                                //让新节点指向后一个节点
		}
	}
	return head;
}


/*删除指定的元素及其所在的节点(常用):会删除该链表上的所有的数据域为val的节点*/
List_Node_t* List_Node_remove_data(List_Node_t* head, int val)
{
	bool val_flag = true;
	while (val_flag)
	{
		if (head == NULL)                                               //如果链表不存在
		{
			return head;                                                     //直接退出函数
		}
		if (head->list_val == val)                                      //如果要删除的是头节点
		{
			List_Node_t* temp = head;
			head = head->next_node;                                     //将头节点之后的那个节点改为现在的链表的头节点,目的是如果直接删除头节点,而在这之后还有相同的值的话,会继续下去,防止卡死
			temp->next_node = NULL;                                     //头节点指针域直接指向NULL,和链表相断开(非必要)
			free(temp);
			return head;
		}
		List_Node_t* current = head;                                      //从头节点开始遍历,当前节点为头节点,一般定义了当前节点之后都会选择从头节点开始,即current=head
		while (current->next_node != NULL && current->next_node->list_val != val)//找到要想删除的节点的前一个节点(这个while里面是对要删除的那个节点的判断判断)
		{
			current = current->next_node;                               //一直后移遍历
			if (current->next_node == NULL)
			{
				val_flag = false;
			}
		}
		if (val_flag == false)
		{
			break;
		}
		List_Node_t* del_node = current->next_node;                     //此时的current就是要删除的那个节点的前一个节点,这里的操作是保存要删除的节点的地址,为了让这个节点在离开链表后能被做其他的利用
		current->next_node = current->next_node->next_node;             //这里的操作是直接让已经被删除了的节点的前一个节点的指针域直接指向那个已经被删除了的节点的后一个节点的地址,由此让它们连接起来
		delete  del_node;                                               //彻底删除这个节点
		del_node = NULL;
	}
	return head;
}


/*删除指定的位置及其所在的节点(不常用)*/
List_Node_t* List_Node_remove_pos(List_Node_t* head, int pos)
{
	int i = 0;
	if (head == NULL)                                         //如果链表不存在
	{
		return head;
	}
	if (pos == 0)                                             //如果要删除头节点
	{
		List_Node_t* temp = head;                             //将头节点的地址暂存,因为下面会对头节点进行操作    
		head = head->next_node;                               //新的头节点设置为被头节点指向的那个节点,即第二个节点
		temp->next_node = NULL;                               //将老头节点指向NULL,和链表断开
		free(temp);                                           //释放老头节点内存空间
		return head;
	}
	List_Node_t* current=head;                               //当前节点为头节点开始遍历
	List_Node_t* perv   =NULL;                               //当前节点的上一个节点
	while (current->next_node != NULL && i < pos)            //如果当前节点为尾节点且找到了要删除的节点(这样做的目的可以防止在不知道链表长度的时候给出的删除的节点的范围超出)
	{
		perv = current;                                      //当前节点的前一个节点
		i++;                                                 //当前节点的序号
		current = current->next_node;                        //当前节点
	};
	perv->next_node = current->next_node;                    //被删除节点的上一个节点指向被删除节点的下一个节点
	current->next_node = NULL;                               //被删除的节点置空
	delete current;                                          //彻底删除

	return head;
}

/*修改指定的节点的元素*/
List_Node_t* List_Node_modfiy(List_Node_t* head, int val, int pos)
{
	int i = 0;
	if (head == NULL)
	{
		return head;
	}
	List_Node_t* current = head;
	while (current->next_node != NULL && i < pos)          //遍历直到尾节点或者要修改的节点结束    
	{
		i++;
		current = current->next_node;
	}
	current->list_val = val;                               //直接修改当前节点的值即可
	return head;
}

/*修改指定的所有元素为其他元素*/
List_Node_t* List_Data_modfiy(List_Node_t* head, int origin_val, int define_val)
{
	if (head == NULL)
	{
		return head;
	}
	List_Node_t* current = head;
	while (current->next_node != NULL)                                        //直接遍历整个链表直到尾节点结束
	{
		if (current->list_val == origin_val)                                  //替换所有要替换的元素
		{
			current->list_val = define_val;
		}
		current = current->next_node;
	}
	return head;
}



/*以函数的形式*/
int main(void)
{
	printf("Author:新照不萱\n");
	printf("Publication Date :2023-06-05\n");
	List_Node_t* list_head=(List_Node_t*)malloc(sizeof(List_Node_t));//为头节点分配内存
	printf("Creat a new linked_list:\n");
	List_Node_t* list_handled=Single_Creat_List(list_head,10);		//创建一个单链表
	Ergodic_List(list_handled);										//遍历并打印链表
	printf("Add a new Node:\n");
	list_handled=List_Node_add(list_handled,20,6);                  //在指定位置添加一个新节点
	Ergodic_List(list_handled);										//遍历并打印链表
	printf("Delect a  Node by data:\n");
	list_handled = List_Node_remove_data(list_handled, 1000);       //删除指定元素的节点
	Ergodic_List(list_handled);										//遍历并打印链表
	printf("Delect a  Node by posation:\n");
	list_handled=List_Node_remove_pos(list_handled, 3);             //删除指定位置的节点
	Ergodic_List(list_handled);										//遍历并打印链表
	printf("Modfiy a  Node by posation:\n");
	list_handled = List_Node_modfiy(list_handled,600,5);            //修改指定位置的节点
	Ergodic_List(list_handled);										//遍历并打印链表
	printf("Modfiy a  Node by data:\n");
	list_handled = List_Data_modfiy(list_handled,0,900);            //修改所有的元素为自定义元素
	Ergodic_List(list_handled);										//遍历并打印链表
	printf("Free Linklist\n");
	Free_List(list_handled);										//释放链表(没有这个的话,软件运行会直接卡死)
	printf("Free Finlish\n");
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
单链表是一种常见的数据结构,用于存储一系列元素,每个元素包含一个值和一个指向下一个元素的指针。下面是 C 语言实现单链表的初始化和创建代码示例: 1.初始化单链表 ```c #include <stdio.h> #include <stdlib.h> /*定义单链表节点结构体*/ typedef struct node{ int data; //数据域 struct node *next; //指向下一个节点的指针 }Node,*pNode; /*初始化单链表*/ pNode InitList(){ pNode head = (pNode)malloc(sizeof(Node)); //创建头节点 if(head == NULL){ printf("初始化失败,内存分配失败!\n"); exit(-1); } head->next = NULL; //初始化头节点的指针域为 NULL return head; } ``` 2.创建单链表 ```c /*创建单链表*/ pNode CreateList(pNode head){ pNode p = head; //定义一个指针 p 指向头节点 int num; //定义一个变量用于存储节点的值 printf("请输入节点的值,输入 -1 结束:\n"); while(1){ scanf("%d",&num); if(num == -1){ //输入 -1 时结束 break; } pNode new_node = (pNode)malloc(sizeof(Node)); //创建新节点 if(new_node == NULL){ printf("创建失败,内存分配失败!\n"); exit(-1); } new_node->data = num; //给新节点赋值 new_node->next = NULL; //初始化新节点的指针域为 NULL p->next = new_node; //将新节点添加到链表尾部 p = new_node; //指针 p 指向新节点,准备添加下一个节点 } return head; } ``` 以上代码实现单链表的初始化和创建。初始化函数 `InitList()` 用于创建头节点并初始化,`CreateList()` 用于创建链表并添加节点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值