考研数据结构单链表的增删改查看这一篇就够了

目录

一. 单链表的特点

1.1 解引用拓展 🤖

二. 单链表的操作

2.1不带头节点的操作

2.1.1 打印 

2.1.1.1 创建结点

2.1.2 尾插(需要二级指针)

注意形参的值不改变实参:(精髓部分)

2.1.3 头插

2.1.4 尾删

2.1.5 头删

2.1.6 查找

2.1.7 在pos前插入

 一级指针和二级指针的区别

2.1.8 删除pos位置 

2.1.9 在pos之后插入

2.1.20 删除pos之后的元素

注意错误写法

2.1.21 销毁 

不带头结点的完整代码 


一. 单链表的特点

单链表的结点是随机的不是逻辑上相邻即物理上相邻的。用指针指向下一个节点的地址。

1.1 解引用拓展 🤖

解引用有两种,* ->

*p的意思是:是取p所指向内存的值,取多少大小的值,取决于结构体前的数据类型,如int                         取四个字节,char取一个字节。

p->的意思是: 结构体用->,取决于->后面是什么值,如果是->val则取data域的值,->next则                       取下个结点的地址。相当于(*p).next

二. 单链表的操作

单链表分为带头结点和不带头结点 ☆*: .。. o(≧▽≦)o .。.:*☆

2.1不带头节点的操作

2.1.1 打印 

打印不用二级指针的原因是:打印不用改变外部结构体指针,只用遍历打印就好了,当要改变结构体指针就要用二级指针。

void SLTPrint(SLNode* phead)
{
	SLNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}
2.1.1.1 创建结点
SLNode* CreateNode(SLNDataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}

	newnode->val = x;
	newnode->next = NULL;
	return newnode;
}

2.1.2 尾插(需要二级指针)

void SLTPushBack(SLNode** pphead, SLNDataType x)
{
	SLNode* newnode = CreateNode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		// 找尾
		SLNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		tail->next = newnode;
	}
}

注意形参的值不改变实参:(精髓部分)

接下来是很多人都迷迷糊糊搞不懂的地方。

当前函数定义的可以直接修改,不是当前当前函数定义的,无论是外部函数,还是malloc出来的空间,访问时都要用指针去通过地址修改。全局变量可以直接修改。

形参不不能改变实参的,要传实参的地址才能改变形参。

想用形参pphead改变外部指针phead(实参) ,先将实参的地址&plist,传给实参pphead,这时pphead代表的是plist地址(&plist),*pphead解引用所以*pphead代表是plist,这里是要改变SNode*,所以要node**。这里是要修改结构体的指针plist,所以是需要结构体指针的地址&plist传给*pphead。

如果要改变外部定义的结构指针SLNode*,要用二级指针SLNode**。

如果要改变外部定义的结构体Node,就要一级指针Node*。

2.1.3 头插

void SLTPushFront(SLNode** pphead, SLNDataType x)
{
	SLNode* newnode = CreateNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

2.1.4 尾删

删后n-1个不需要二级指针,直接free();

但是只有一个空间的时候,删掉,plist就成了没有指向空间的野指针,所以需要将plist置空,需要改变结构体指针。 

 

void SLTPopBack(SLNode** pphead)
{
	// 温柔的检查
	//if (*pphead == NULL)
	//	return;

	// 空
	assert(*pphead);

	// 1、一个节点
	// 2、一个以上节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		// 找尾
		/*SLNode* prev = NULL;
		SLNode* tail = *pphead;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}

		free(tail);
		tail = NULL;

		prev->next = NULL;*/

		SLNode* tail = *pphead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

2.1.5 头删

void SLPopFront(SLNode** pphead) {

	assert(pphead);
	if ((*pphead)== NULL) {
		free(*pphead);
		*pphead= NULL;
	}
	else {
		/**pphead = (*pphead)->next;
		free(*pphead);*/
		SLNode* tmp = *pphead;
		*pphead = (*pphead)->next;
		free(tmp);


	}

}

2.1.6 查找

查找的作用主要是,和Insert()配合着使用。 

void SLTFind(SLNode* phead, SLDataType x)
{
	SLNode* cur = phead;
	while (cur)
	{
		if (cur->val == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}

	return NULL;
}

2.1.7 在pos前插入

 

//在pos之前插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLDataType x)
 {

	assert(pphead);	//plist地址不可能为NULL
	assert(!*pphead && !pos || *pphead && pos);	//	*pphead为空时pos也为NULL,
	SLNode* newnode = CreateNode(x);
	if (*pphead==pos)
	{
		SLPushFront(pphead,x);					//这里不是在测试类,没有将plist地址给pphead,*pphead表示解引用使用plist
	}
	else {
		SLNode* pre = *pphead;
		while (pre->next != pos) {
			pre = pre->next;
		}
		newnode->next = pos;
		pre->next = newnode;
	}

}

 一级指针和二级指针的区别

2.1.8 删除pos位置 

//删除pos位置的元素
void SLTErase(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	SLNode* pre = *pphead;
	if (*pphead==pos) {
		SLPopFront(pphead);
	}
	else 
	{
		while (pre->next != pos) {
			pre = pre->next;
		}
		pre->next = pos->next;
		free(pos);
		pos->next = NULL;
	}

}

2.1.9 在pos之后插入

//在pos的之后插入
void SLTInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x)
{
	assert(pphead);
	assert(!*pphead && !pos || *pphead && pos);
	SLNode* newnode = CreateNode(x);
	if (*pphead == pos) 
	{
		SLPushFront(pphead, x);
	}
	else 
	{
		newnode->next = pos->next;
		pos->next = newnode;
	}


}

2.1.20 删除pos之后的元素

//删除指定位置的下一个位置
void SLTEraseAfter(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos->next != NULL);
	SLNode* tmp = pos->next;
	pos->next = pos->next->next;
	free(tmp);
	tmp = NULL;
}
注意错误写法

 pos->next = pos->next->next;

 free(pos->next);

错误原因:这里pos->next已经赋值成pos->next->next。所以真正删除的其实是 pos->next->next,其次这样会导致单链表断裂,后面的结点会没有指针指向成野结点。

 

2.1.21 销毁 

//销毁
void STDestory(SLNode** pphead)
{
	assert(pphead);
	SLNode* cur = *pphead;
	while (cur != NULL) {
		SLNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
	*pphead = NULL;
}

不带头结点的完整代码 

//头文件
#pragma once
#include<stdio.h>
#include<stdlib.h>

typedef int SLDataType;
typedef struct SListNode {

	SLDataType val;
	struct SListNode* next;

}SLNode;

void SLPrint(SLNode* phead);
void SLPushBack(SLNode** pphead, SLDataType x);
void SLPopBack(SLNode** pphead);
void SLPushFront(SLNode** pphead, SLDataType x);
void SLPopFront(SLNode** pphead);
SLNode* SLTFind(SLNode* phead, SLDataType x);
void SLTInsert(SLNode** pphead, SLNode* pos, SLDataType x);
void SLTInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x);
void SLTErase(SLNode** pphead, SLNode* pos);
void SLTEraseAfter(SLNode** pphead, SLNode* pos);
void STDestory(SLNode** pphead);

//SList.c文件
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include"SList.h"
#include<assert.h>
//打印
void SLPrint(SLNode* phead) {

	assert(phead);
	while (phead!= NULL) {
		printf("%d->", phead->val);
		phead = phead->next;
	}
	printf("NULL\n");
}
//创建新节点

SLNode* CreateNode(SLDataType x) {

	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	while (newnode == NULL) {
		perror("malloc fail");
		exit(-1);
	}
	newnode->val = x;
	newnode->next = NULL;
	return newnode;

}

//尾插
void SLPushBack(SLNode** pphead, SLDataType x) {
	SLNode* newnode = CreateNode(x);		//调用函数,实参没有数据类型
	if (*pphead == NULL) {
		*pphead = newnode;
	}
	else {
	
		SLNode* tail = *pphead;
		while (tail->next != NULL) {
			tail = tail->next;
		}
		newnode->next = NULL;
		tail->next = newnode;
	}

}
//尾删
void SLPopBack(SLNode** pphead) {

	assert(*pphead);
	if ((*pphead)->next == NULL) {
		free(*pphead);
		*pphead = NULL;
	}
	else {
		SLNode* pre = NULL;
		SLNode* tail = *pphead;
		while (tail->next != NULL) {
			pre = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;
		pre->next = NULL;

	}

}
//头插
void SLPushFront(SLNode** pphead, SLDataType x) {
	SLNode* newnode = CreateNode(x);
	/*if (*pphead == NULL) {
		*pphead = newnode;
	}*/    //可以不要
	newnode->next = *pphead;
	*pphead= newnode;
}
//头删
void SLPopFront(SLNode** pphead) {

	assert(*pphead);
	if ((*pphead)== NULL) {
		free(*pphead);
		*pphead= NULL;
	}
	else {
		/**pphead = (*pphead)->next;
		free(*pphead);*/
		SLNode* tmp = *pphead;
		*pphead = (*pphead)->next;
		free(tmp);


	}

}
// 查找
SLNode* SLTFind(SLNode* phead, SLDataType x)
{
	SLNode* cur = phead;		//查找不需要二级指针,所以phead为头指针
	while (cur)
	{
		if (cur->val == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}

	return NULL;
}
//在pos之前插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLDataType x)
 {

	assert(pphead);	//plist地址不可能为NULL
	assert(!*pphead && !pos || *pphead && pos);	//	*pphead为空时pos也为NULL,
	SLNode* newnode = CreateNode(x);
	if (*pphead==pos)
	{
		SLPushFront(pphead,x);					//这里不是在测试类,没有将plist地址给pphead,*pphead表示解引用使用plist
	}
	else {
		SLNode* pre = *pphead;
		while (pre->next != pos) {
			pre = pre->next;
		}
		newnode->next = pos;
		pre->next = newnode;
	}

}
//在pos的之后插入
void SLTInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x)
{
	assert(pphead);
	assert(!*pphead && !pos || *pphead && pos);
	SLNode* newnode = CreateNode(x);
	if (*pphead == pos) 
	{
		SLPushFront(pphead, x);
	}
	else 
	{
		newnode->next = pos->next;
		pos->next = newnode;
	}


}
//删除pos位置的元素
void SLTErase(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	SLNode* pre = *pphead;
	if (*pphead==pos) {
		SLPopFront(pphead);
	}
	else 
	{
		while (pre->next != pos) {
			pre = pre->next;
		}
		pre->next = pos->next;
		free(pos);
		pos->next = NULL;
	}

}
//删除指定位置的下一个位置
void SLTEraseAfter(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos->next != NULL);
	SLNode* tmp = pos->next;
	pos->next = pos->next->next;
	free(tmp);
	tmp = NULL;
}
//销毁
void STDestory(SLNode** pphead)
{
	assert(pphead);
	SLNode* cur = *pphead;
	while (cur != NULL) {
		SLNode* tmp = cur->next;
		free(cur);
		cur = tmp;
	}
	*pphead = NULL;
}

//Test.c 文件
#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"

void Test1() {
	SLNode* splist=NULL;
	SLPushBack(&splist, 1);
	SLPushBack(&splist, 2);
	SLPushBack(&splist, 3);
	SLPushBack(&splist, 4);
	SLPrint(splist);
	SLPopBack(&splist);
	SLPopBack(&splist);
	SLPrint(splist);
	SLPushFront(&splist, 1);
	SLPushFront(&splist, 2);
	SLPushFront(&splist, 3);
	SLPushFront(&splist, 4);
	SLPrint(splist);
	SLPopFront(&splist);
	SLPopFront(&splist);
	SLPrint(splist);
	
}
void Test2() {
	SLNode* splist = NULL;
	SLPushBack(&splist, 1);
	SLPushBack(&splist, 2);
	SLPushBack(&splist, 3);
	SLPushBack(&splist, 4);
	SLPrint(splist);
	SLNode* pos = SLTFind(splist, 3);
	SLTInsert(&splist, pos, 20);
	SLPrint(splist);
	pos = SLTFind(splist, 4);
	SLTInsertAfter(&splist, pos, 30);
	SLPrint(splist);
	pos = SLTFind(splist, 2);
	SLTErase(&splist, pos);
	SLPrint(splist);
	pos = SLTFind(splist, 3);
	SLTEraseAfter(&splist, pos);
	SLPrint(splist);
	STDestory(&splist);
	SLPrint(splist);
}
int main() {
	Test2();
	return 0;

}

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值