单链表增删改查详细讲解

链表

链表结构
在这里插入图片描述
顺序表是连续的类似数组,链表可以不连续,物理上不是线性的
当只是尾插尾删只用顺序表也可以,但是需要到头插头删的时候,还是需要链表,因为链表没有增容的代价,链表是需要一个就开辟一个

在这里插入图片描述

phead和plist的关系

在这里插入图片描述

pphead、*pphead、plist的关系

在这里插入图片描述

打印函数

void SListPrint(SLTNode* phead)
{
	SLTNode *cur = phead;//新建一个节点指向phead,phead为plist的地址,要用指针指向地址
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");//最后一个结点指向NULL,输出在结尾
}

在这里插入图片描述

尾插和前插函数

  • 需要借助BuySListNode来开辟新的结点
SLTNode* BuySListNode(SLTDataType x)
{//新建(开辟)新的结点
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if(node == NULL)
	{
		printf("malloc fail\n");
		exit(-1);//内存申请不出来,就通过exit来以-1的标志退出
	}
	node->data = x;
	node->next = NULL;
	return node;
}
//尾插
void SListPushBack(SLTNode **pphead, SLTDataType x)
{
	//结点为空的时候 - 新创建的结点直接插入当作尾结点
	if (*pphead == NULL)
	{
		SLTNode *newnode = BuySListNode(x);
		*pphead = newnode;
	}
	//结点不为空的时候
	else
	{
		//用tail(结构体)指针来指向头节点一直往下找直到找到尾结点
		SLTNode* tail = *pphead;//tail(结构体)指针来指向头节点
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		SLTNode *newnode = BuySListNode(x);//找到尾结点后直接插入新的结点当作新的尾结点
		tail->next = newnode;//连接(插入)新的尾结点
	}
}
//前插
void SListPushFront(SLTNode **pphead, SLTDataType x)
{
	SLTNode *newnode = BuySListNode(x);
	newnode->next = *pphead;//把第一个节点的地址(plist(*pphead)指向第一个节点的地址)传给新节点中
	*pphead = newnode;//把新节点的地址传给pliat
}

尾插
在这里插入图片描述
尾插失败的原因:不能使用指针传值
在这里插入图片描述
解决指针传值的问题:传二级指针(函数里面需要改变phead的时候就需要传二级指针,不需要的时候就传一级指针)
在这里插入图片描述
在这里插入图片描述
头插
理解方法1:
在这里插入图片描述
理解方法2:
在这里插入图片描述
在这里插入图片描述

尾删和头删函数

//尾删- -两种方法
void SListPopBack(SLTNode **pphead)
{
	assert(pphead);//判断参数是否传错

	//没有结点断言报错- -处理暴躁
	assert(*pphead);//链表为空的时候还在调用尾删会报错
	//温和处理
	//if (*pphead == NULL)
	//{
	//	return;
	//}

	//一个结点
	if ((*pphead)->next == NULL)//plist->next就是d1的next
	{
		free(*pphead);
		*pphead = NULL;
	}
   //多个结点
	else
	{
		//方法1
		SLTNode *prev = NULL;
		SLTNode *tail = *pphead;//pphead解引用找到plist
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
		//方法2
		//SLTNode *tail = *pphead;
		//while (tail->next->next != NULL)
		//{
        //   tail = tail->next;
		//}
		//free(tail->next);
		//tail->next = NULL;
	}
}
//头删
void SListPopFront(SLTNode** pphead)
{
	assert(pphead);//plist一定有地址,所以pphead不为空
	//链表为空
	assert(*pphead);
	
	//只有一个结点
	//if ((*pphead)->next == NULL)//(*pphead)->next就是第一个结点的next
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	多个结点
	//else
	//{
	//	SLTNode* next = (*pphead)->next;//plist->next就是d1的next即d2
	//	free(*pphead);//free掉的是d1
	//	(*pphead) = next;//plist指向d2
	//}

	//可以同时处理一个和多个结点
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	(*pphead) = next;
}


尾删
在这里插入图片描述
test.c中一直尾删(链表可能会为空)会出错,所以得考虑代码节点的多种情况
在这里插入图片描述

头删
在这里插入图片描述

在这里插入图片描述

size、Empty函数

//链表的空间
int SListSize(SLTNode* phead)
{
	int size = 0;
	SLTNode* cur = phead;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}
bool SListEmpty(SLTNode* phead)//bool只有0和1两种情况
{
	//方法1
	//return phead == NULL;
	//方法2
	/*if (phead == NULL)
	{
	return true;
	}
	else
	{
	return false;
	}*/
	//方法3
	return phead == NULL ? true : false;
}

test.c
在这里插入图片描述

销毁

//销毁
void SListDestory(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;//销毁完所有的节点以后plist要指向空而不是原来的节点
}

查找 - 修改函数

SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

text.c
在这里插入图片描述

任意位置的插入函数

单链表更适合在pos位置之后的插入,在pos之前很麻烦还得找到pos

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	
	// 1、头插
	// 2、后面插入
	if (*pphead == pos)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		// 找到pos位置的前一个节点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySListNode(x);
		newnode->next = pos;
		prev->next = newnode;
	}
}

// 在pos位置后面去插入x
void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);//防止pos输入不规范

	SLTNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

在这里插入图片描述

删除函数

删除pos

// 删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	// 1、头删
	// 2、后面节点删除
	if (pos == *pphead)
	{
		SListPopFront(pphead);
	}
	else
	{
		// 找到pos位置的前一个节点
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

在这里插入图片描述
删除pos后一个位置

//删除
void SListEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);//pos的下一个位置不能为空,防止next为空下面会出错

	SLTNode* next = pos->next;
	pos->next = next->next;

	free(next);
	next = NULL;
}

在这里插入图片描述

总代码

总代码

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值