初识链表(无头单向非循环链表的增删查改)

目录

链表相对于顺序表的优点是 操作数据时不需要大规模地移动原有的数据

1. 中间/头部的插入删除,时间复杂度为O(N)

 SListNode.h(头文件的引用,结构体的定义)

  SListNode.c(各个函数的定义)

test.c(测试各个函数的功能)

打印函数

插入数据时开辟空间的函数

一、增(头插 尾插 指定位置前或后插入)

1、头插

2.头删

3、尾插

4、尾删

 二、查找数据

三、删除指定数据与删除指定数据前或后端的数据

1.删除指定数据

2.删除指定数据后边的数据

3.删除指定元素前面的那个元素

四、指定数据前或后插入数据

1.指定数据后插入新数据

2.指定数据前插入数据

五、改


链表相对于顺序表的优点是 操作数据时不需要大规模地移动原有的数据

1. 中间/头部的插入删除,时间复杂度为O(N)

2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
3. 增容一般是呈 2 倍的增长,势必会有一定的空间浪费。例如当前容量为 100 ,满了以后增容到 200 ,我们 再继续插入了5 个数据,后面没有数据插入了,那么就浪费了 95 个数据空间。
如何解决以上问题呢?下面给出了链表的结构来看看。
链表概念:链表是一种 物理存储结构上非连续 、非顺序的存储结构,数据元素的 逻辑顺序 是通过链表中的 指针链 次序实现的
本文介绍的就是链表中的最简单的无头单向非循环链表  后期在探讨其他类型的
无头单向非循环链表: 结构简单 ,一般不会单独用来存数据。实际中更多是作为 其他数据结构的子结 ,如哈希桶、图的邻接表等等。另外这种结构在 笔试面试 中出现很多。

先大致看一下整体的代码 好利于下面的阅读和理解

 SListNode.h(头文件的引用,结构体的定义)

  SListNode.c(各个函数的定义)

test.c(测试各个函数的功能)

打印函数

void SListPrint(SLTNode* plist)
{
	while (plist != NULL)//只要结点不是空就打印其对应的值 链表尾是空   
	{
		printf("%d->", plist->data);
		plist = plist->next;
	}
	printf("NULL\n");
}

插入数据时开辟空间的函数

SLTNode* BuySList(val)
{
	SLTNode* node= (SLTNode*)malloc(sizeof(SLTNode));
	node->data = val;
	node->next = NULL;
	return node;

}

一、增(头插 尾插 指定位置前或后插入)

插入数据时要考虑几种的情况:1.链表为空  2.链表中只有一个数据

1、头插

void SListPushFront(SLTNode** plist, SLTDataType val)
{
	SLTNode* node = BuySList(val);//创建一块空间 把数据存入该空间 再把指向该空间的地址与链表相连
	node->next = *plist;
	*plist = node;//这里要改变*plist 的值 传参时就要传*plist 的地址 所以形参是二级指针用来接受一级指针的地址(*plist是结构体指针)

}

2.头删

void SListPopFront(SLTNode** plist)
{
	if (*plist == NULL)
	{
		printf("SLTNode is NULL\n");
		return;//考虑链表是否为空 是空就无数据可删 返回
	}
	else
	{
		/*SLTNode* node= *plist;
		node = node->next;*/ 

		//(*plist) = (*plist)->next;
		SLTNode* node = (*plist)->next;//保存下一个结点的地址 
		free(*plist);//既然是头删就要释放第一个结点的地址指向的那块空间 
		*plist = NULL;//释放后要置空 防止内存泄漏
		*plist = node;//让保存的下一个结点的地址当新地址即新的头   把头删了要有新的头

	}

}

3、尾插

void SListPushBack(SLTNode** plist,SLTDataType val)
{
	SLTNode* newnode = BuySList(val);//既然是要插入数据 那开始就为其开辟一块空间 并且保存其地址 后面直接连在链表上即可
	if (*plist == NULL)
	{
	*plist = newnode;//考虑链表头是空的情况  这是就直接把开辟的新空间的地址当作头即可

	}
	
	else
	{
		SLTNode* tail = *plist;//如果不是头为空的情况就要去找链表的尾了 找到尾巴后在其后面把开始开辟的空空间连在后边 就是尾插了
		while (tail->next != NULL)
		{
			
			tail = tail ->next;//这里要注意加上括号 因为* 和-> 都是解引用 同级 要用括号括起来 
		}
		//(*plist)->next=newnode;
		tail->next = newnode;//连接


	}

}

4、尾删

void SListPopBack(SLTNode** plist)
{
	//考虑三种情况 
	//1.链表为空
	if (*plist == NULL)
	{
		printf("SLTNode is NULL!\n");
		return;
	}
	//2.链表只有一个元素(结点)
	else if ((*plist)->next == NULL)
	{
		free(*plist);
		*plist = NULL;
	}
	//3.链表有多个元素
	else
	{
		SLTNode* node = NULL;
		SLTNode* tail = *plist;
		while (tail->next != NULL)
		{
			node = tail;//尾删要找尾 同时还要让尾上一个结点的next为空 这就找到尾的上一个结点的地址了 这里是先保存一份尾 再让其往后走 形成一前一后的结构
			tail = tail->next;
		}
		free(tail);//释放尾置空 达到尾删的效果
		tail = NULL;
		node->next = NULL;//让尾的上一个元素的next为空 因为链表最后得是空的
	}

}

 二、查找数据

SLTNode* SListFound(SLTNode* plist, SLTDataType val)
{
	SLTNode* node = plist;//这里因为不要改变实参 所以传的实参的值 前面涉及到改变实参 所传的是实参的地址 
	while (node)//让头往后走 遍历链表的每个元素
	{
		if (node->data == val)//判断node指向的数据是不是我们要查找的值 如果是就返回其地主 不是的话就让node往后走 直到尾
		{
			return node;
		}
		else
		{
			node = node->next;
		}
	}
	return NULL;//如果上面都没有返回地址就会往下执行 那么就是没有找到就返回空指针

}

 

 根据返回值打印要查找的数据

三、删除指定数据与删除指定数据前或后端的数据

1.删除指定数据

void SListErasecur(SLTNode** plist, SLTDataType val)//删除指定的数据 
{
	SLTNode* node = *plist;//拷贝一份头地址 防止改变链表头
	SLTNode* prev = NULL;//定义一个前驱指针 形成一前一后地址的结构  好让链表能删除数据后还是连在一起的
	if (*plist == NULL)
	{
		printf("the SLTNode is already NULL!\n");
		return;
	}
	
	else
	{
		SLTNode* node = *plist;
		while (node->data != val)
		{
			prev = node;
			node = node->next;
		}//找到给定的数据的地址和其上一个元素的地址
		if (prev == NULL)//如果prev是空 那么就是第一个就是我们要删的数据 就是相当于头删了 
		{
			
				SLTNode* node1 = (*plist)->next;
				free(*plist);
				*plist = NULL;
				*plist = node1;
			
		}
		else//否则就让指定数据的前一块空间的next指向指定数据后面的那一块空间 释放给定的数据的空间
		{
			prev->next = prev->next->next;
			free(node);
			node = NULL;

		}
	}
}

2.删除指定数据后边的数据

void SListEraseAfter(SLTNode** plist, SLTDataType val)
{
	SLTNode* node = *plist;//拷贝头
	if (*plist == NULL)//如果为空链表
	{
		printf("NLTNode is NULL\n");
		return;
	}
	else if ((*plist)->next == NULL)//如果只有一个数据 无法实现
	{
		printf("error!\n");
		return;
	}
	else
	{
		node = SListFound(*plist, val);//调用上面的查找函数 找我们给定的那个数据的位置 再去对其后面的数据进行删除
		node->next = node->next->next;//跳过给定数据后面的元素 调整指向顺序
		free(node->next);//释放要删数据的空间
		node->next = NULL;
	}
}

3.删除指定元素前面的那个元素

思路: 转变一下思想直接交换给定元素和其前一个元素的值 在释放给定元素的空间可以达到相同效果 

void SListEraseBefore(SLTNode** plist, SLTDataType val)
{
	if (*plist == NULL)
	{
		printf("SLTNode is already NULL!\n");
		return;
	}
	else if ((*plist)->next == NULL)
	{
		*plist = NULL;
	}
	else
	{
		SLTNode* prev = NULL;
		SLTNode* node = *plist;

		while (node->data != val)
		{
			prev = node;
			node = node->next;
		}
		if (prev == NULL)
		{
			printf("error! \n");
			return;
		}
		else
		{
			/**plist = prev->next;
			free(prev);
			prev = NULL;*/
			SLTNode* swapnode=(SLTNode*)malloc(sizeof(SLTNode));
			swapnode->data = 0;
			swapnode->data = prev->data;
			prev->data = node->data;
			node->data =swapnode->data;
			prev->next = prev->next -> next;
			free(node);
			node = NULL;
		}


	}

}

四、指定数据前或后插入数据

1.指定数据后插入新数据

void SListInsertAfter(SLTNode** plist,SLTDataType des, SLTDataType val)
{
	SLTNode* newnode = BuySList(val);
	SLTNode* desnode=SListFound(*plist, des);
	if (desnode == NULL)
	{
		printf("not find!\n");
		return;
	}
	else
	{ 
		newnode->next = desnode->next;
		desnode->next = newnode;
	
	}


}

2.指定数据前插入数据

思路:常规的思路是去找指定位置的元素地址 弄个前驱指针 找到指定元素上一个元素的地址将其删除 但是删了后要让被删元素的上一个元素的next指向我们给定的数据的地址 但是这个不好找 很麻烦 所以转变思路 直接在给定数据后插入新数据 再交换两者的位置即可实现  

void SListInsertBefore(SLTNode** plist, SLTDataType des, SLTDataType val)
{
	SLTNode* newnode=BuySList(val);
	SLTNode* desnode = SListFound(*plist, des);
	SLTNode* swapnode = (SLTNode*)malloc(sizeof(SLTNode));
	swapnode->data = 0;
	if (*plist == NULL)
	{
		*plist = newnode;
	}
	else
	{
		newnode->next = desnode->next;
		desnode->next = newnode;
		swapnode->data = desnode->data;
		desnode->data = newnode->data;
		newnode->data = swapnode->data;
		/*swapnode = desnode;
		desnode= newnode;
		newnode= swapnode;*/


	}
}

五、改

这个就比较简单了 找到给定元素地址 将其指向的数据改变即可


void SListModify(SLTNode** plist, SLTDataType des, SLTDataType val)
{
	SLTNode* node = SListFound(*plist,des);
	if (node == NULL)
	{
		printf("error!\n");
		return;
	}
	else
	{
		node->data = val;
	}
}

最后给上代码运行图

 创作不易 点赞支持一下吧! 若有不足 多加指点 互相交流呀~

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值