单链表的简单应用

目录

一、顺序表的问题及思考

二、链表的概念及结构

三、单链表的实现

3.1 增

3.1.1 尾插

3.1.2 头插

3.1.3 指定位置前插入

3.1.4 指定位置后插入

3.2 删

3.2.1 尾删

3.2.2 头删

3.2.3 指定位置删除

3.2.4 指定位置后删除

3.2.5 链表的销毁

3.3 查

3.4 改

四、完整代码



一、顺序表的问题及思考

1.1 

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

1.2 

增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

1.3 

增容⼀般是呈2倍的增长,势必会有⼀定的空间浪费。例如当前容量为

100,满了以后增容到 200,我们再继续插⼊了5个数据,

后面没有数据插入了,那么就浪费了95个数据空间。

二、链表的概念及结构

是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链
接次序实现的 。
链表的结构跟火车车厢相似,淡季时车次的车厢会相应减少,旺季时车次的车厢会额外增加几节。
只需要将火车里的某节车厢去掉/加上,不会影响其他车厢,每节车厢都是独立存在的。
车厢是独立存在的,且每节车厢都有车门。想象⼀下这样的场景,假设每节车厢的车门都是锁上的
状态,需要不同的钥匙才能解锁,每次只能携带⼀把钥匙的情况下如何从车头走到车尾? 最简单
的做法:每节车厢里都放⼀把下⼀节车厢的钥匙。
与顺序表不同的是,链表里的每节"车厢"都是独立申请下来的空间,称之为“结点/节点”
#include<stdio.h>

typedef int luck;//存储不同类型数据,重命名,这样后面就只用改这里的int
struct SListNOde
{
	luck data;//存储的数据
	SListNOde* next;//指向下一个节点的指针
};
节点的组成主要有两个部分:当前节点要保存的数据和保存下⼀个节点的地址(指针变量)。
为什么还需要指针变量来保存下⼀个节点的位置?
链表中每个节点都是独立申请的(即需要插入数据时才去申请⼀块节点的空间),我们需要通过指
针变量来保存下⼀个节点位置才能从当前节点找到下⼀个节点。
首先,来完成结点申请空间的函数。
这里选择使用malloc函数(因为每个节点的都是相对独立存在的,不涉及到扩容的操作)
SLNO* SLNOIn()//节点申请空间
{
	SLNO* newnode = (SLNO*)malloc(sizeof(SLNO));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	return newnode;
}

不要忘了强制类型转换成节点类型

再来写一个打印每个节点数据的函数
void SLNOprint(SLNO* phead)
{
	SLNO* pres = phead;
	while (pres)//节点处不是空指针就有数据
	{
		printf("%d->", pres->data);
		pres = pres->next;
	}
	printf("NULL\n");
}

这里来简单做个测试

void test01()
{
	SLNO* n1 = SLNOIn();
	n1->data = 1;
	n1->next = NULL;
	SLNOprint(n1);
}

int main()
{
	test01();
	return 0;
}

再多弄几个节点

void test02()
{
	SLNO* phead = NULL;
	SLNO* n1;
	SLNO* n2;
	SLNO* n3;
	SLNO* n4;
	n1 = SLNOIn();
	n2 = SLNOIn();
	n3 = SLNOIn();
	n4 = SLNOIn();
	phead = n1;//指向第一个节点的指针

	n1->data = 1;
	n2->data = 2;
	n3->data = 3;
	n4->data = 4;

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;

	SLNOprint(phead);
}

int main()
{
	test02();
	return 0;
}

目前测试着是可行的,接下来实现单链表

三、单链表的实现

3.1 增

与顺序表类似,往链表中插入额外的数据

3.1.1 尾插

将一个新的节点插入到链表尾部。

(图中所列地址仅为方便展示而顺手写的)

那么首先我们需要找到尾节点

那么判断是否是尾节点的条是?

通过该图我们可以看出,尾节点中的指针是NULL

那么判断条件就是
 

SLNO* pres = phead;
while (pres->next)
	{
		pres = pres->next;//尾部节点是该节点保存的下个节点地址为NULL
	}

同时,因为需要在原链表中新增一个节点

所以需要进行空间申请

在这之后,空间申请成功后,这块空间的地址需要由原尾节点进行保存

依此,我们可以写出如下函数

void SLNOInsertback(SLNO* phead, luck X)
{

	SLNO* pres = pphead;//用来查找尾
    SLNO* newnode =  SLNOIn();
    newnode->data = X;
	newnode->next = NULL;

    //要从尾部进行插入,那就要找到尾部节点
	while (pres->next)
	{
		pres = pres->next;//尾部节点是该节点保存的下个节点地址为NULL
	}
	pres->next = newnode;//申请一个新节点
	

}

那么这里只是就链表中已经有数据的情况写出这个函数

如果拿到的链表是一个空链表,那么进行pres->next就肯定会报错,不能对NULL进行解应用

那就单独判断一下
  

无论是哪种情况,插入一个数据肯定都是要申请一个新节点的


void SLNOInsertback(SLNO* phead, luck X)
{
	
      SLNO* pres = phead;//用来查找尾
      SLNO* newnode =  SLNOIn();//申请一个新节点
      newnode->data = X;
	  newnode->next = NULL;

     //如果拿到的链表是一个空链表,那么进行pres->next就肯定会报错,不能对NULL进行解应用
	 
	 //那就单独判断一下
	
	 //如果是这种情况,那么就直接申请一个新节点就好了
	
	if (pres == NULL)
	{
       phead = SLNOIn();
	   return;
	}

	 //要从尾部进行插入,那就要找到尾部节点
	while (pres->next)
	{
		pres = pres->next;//尾部节点是该节点保存的下个节点地址为NULL
	}
	pres->next = newnode;
}

测试一下

void test03()
{
	SLNO* phead = NULL;
	SLNOInsertback(phead,3);
	SLNOprint(phead);
}
int main()
{
	test03();
	return 0;
}

这里可以看到出现了问题,调试看下是个什么情况

这里可以看到

newnode的值改变了,但是phead的值没有改变

即只改变了形参的值,没有改变实参的值

说明这里是传值调用,我们需要用传址调用

这里phead自身是个指针变量,那么要存放一级指针变量的地址,就要使用二级指针接收。

对应的函数也需要做出改变。

void SLNOInsertback(SLNO** pphead, luck X)
{
	assert(pphead);
	SLNO* pres = *pphead;//用来查找尾
	SLNO* newnode =  SLNOIn();
	newnode->data = X;
	newnode->next = NULL;

	//如果拿到的链表是一个空链表,那么进行pres->next就肯定会报错,不能对NULL进行解应用

	//那就单独判断一下

	//如果是这种情况,那么就直接申请一个新节点就好了

	if (pres == NULL)
	{
	   *pphead = newnode;
		return;
	}

	//要从尾部进行插入,那就要找到尾部节点
	while (pres->next)
	{
		pres = pres->next;//尾部节点是该节点保存的下个节点地址为NULL
	}
	pres->next = newnode;
}
void test03()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);
}
int main()
{
	test03();
	return 0;
}

这会儿可以看到,是成功输出出来了

3.1.2 头插

即把数据从链表的头部进行插入

插入的这个新结点作为链表的头部节点

(图中所列地址仅为方便展示而顺手写的)

如图,插入该节点后,首节点就是这个新插入的节点,这个节点的指针保存的就是原首节点的地址

//头部数据插入
void SLNOInsertfront(SLNO** pphead, luck X)
{
	assert(pphead);
	SLNO* pres = *pphead;//保存头节点的位置
	SLNO* newnode = SLNOIn();//申请空间
    newnode->data = X;
	newnode->next = pres;
	*pphead = newnode;
}

在这里就不用判断要插入数据的链表为不为空了,因为是直接从头部插入,不需要查找节点

测试一下

void test04()
{
	SLNO* phead = NULL;
	SLNOInsertfront(&phead, 3);
	SLNOInsertfront(&phead, 4);
	SLNOInsertfront(&phead, 5);
	SLNOInsertfront(&phead, 6);
	SLNOprint(phead);
}
int main()
{
	test04();
	return 0;
}

是可行的

3.1.3 指定位置前插入

在某一个指定位置前插入,首先需要找到该指定位置,该位置的前一节点的指向新插入的节点,

原指向节点为新插入位置所指向的节点

(图中所列地址仅为方便展示而顺手写的)

void SLNOInsertbefore(SLNO** pphead, SLNO* pos, luck X)
{
	assert(pphead && pos);//要插入的位置肯定不能为空
	SLNO* pres = *pphead;
	SLNO* newnode = SLNOIn();//申请新节点
	while (pres->next != pos)
	{
		pres = pres->next;
	}//找插入位置前的那个节点
	SLNO* temp = pres->next;//保存插入位置前节点指向的下一个节点的指针
	newnode->data = X;
	newnode->next = temp;//原指向节点为新插入位置所指向的节点
	pres->next = newnode;//让插入为位置前节点的指向变更为插入的节点
}

这里,由于是链表,pos是一个指针,我们需要找到我们想要插入数据的位置就要先将对应的地址找

出来,这里再来一个find函数

SLNO* Find(SLNO* phead, luck num)
{
	SLNO* pres = phead;
	while (num != pres->data && pres != NULL)
	{
		pres = pres->next;
	}
	if (pres == NULL)
	{
		return NULL;
	}
	return pres;
}

测试一下

void test03()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOInsertbefore(&phead, find, 10);
	SLNOprint(phead);

	 find = Find(phead, 7);
	SLNOInsertbefore(&phead, find, 9);
	SLNOprint(phead);
}

这次测试没有问题,我们再来看一组测试


void test04()
{
	SLNO* phead = NULL;
	SLNOInsertfront(&phead, 3);
	SLNOInsertfront(&phead, 4);
	SLNOInsertfront(&phead, 5);
	SLNOInsertfront(&phead, 6);
	SLNOInsertbefore(&phead, phead, 10);
	SLNOprint(phead);
}


int main()
{
	test04();
	return 0;
}

可以看到代码异常退出了

这里调试看看

通过调试可以看出,这里的错误是对空指针解应用了

回到代码的逻辑

想在头节点前插入一个节点,那么pos就是直接传的头节点phead,在代码的判定逻辑中,在

循环判定这个位置就不能找到pos。

我们发现这里的插入实际上就是前文所写的头插,那我们在进行插入时进行判断,如果是这种

情况调用一下头插函数就好了

void SLNOInsertbefore(SLNO** pphead, SLNO* pos, luck X)
{
	assert(pphead && pos);//要插入的位置肯定不能为空
	SLNO* pres = *pphead;
	if (pos == pres)
	{
		SLNOInsertfront(pphead, X);
		return;
	}
	SLNO* newnode = SLNOIn();//申请新节点
	while (pres->next != pos)
	{
		pres = pres->next;
	}//找插入位置前的那个节点
	SLNO* temp = pres->next;//保存插入位置前节点指向的下一个节点的指针
	newnode->data = X;
	newnode->next = temp;//原指向节点为新插入位置所指向的节点
	pres->next = newnode;//让插入为位置前节点的指向变更为插入的节点
}

在测试一下

void test04()
{
	SLNO* phead = NULL;
	SLNOInsertfront(&phead, 3);
	SLNOInsertfront(&phead, 4);
	SLNOInsertfront(&phead, 5);
	SLNOInsertfront(&phead, 6);
	SLNOInsertbefore(&phead, phead, 1);
	SLNOprint(phead);
}


int main()
{
	test04();
	return 0;
}

这样就解决了。

3.1.4 指定位置后插入

所插入位置的节点指针更改为指向这个插入的节点,原指向位置由插入的新节点指向

(图中所列地址仅为方便展示而顺手写的)

//插入指定位置后
void SLNOInsertafter(SLNO** pphead, SLNO* pos, luck X)
{
	assert(*pphead && pos);
	SLNO* pres = *pphead;
	SLNO* newnode = SLNOIn();
	while (pres != pos)
	{
		pres = pres->next;
	}//找到插入节点
	SLNO* temp = pres->next;//保存插入原指向位置
	newnode->data = X;
	newnode->next = temp;
	pres->next = newnode;
}

 测试一下

void test05()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOInsertafter(&phead, find, 10);
	SLNOprint(phead);

	find = Find(phead, 7);
	SLNOInsertafter(&phead, find, 9);
	SLNOprint(phead);

	SLNOInsertafter(&phead, phead, 1);
	SLNOprint(phead);
}


int main()
{
	test05();
	return 0;
}

可以看到这里没有问题

3.2 删

将一个节点删除,实际上就是将该节点所申请的空间释放掉。

3.2.1 尾删

从链表的尾部进行删除

 将尾部节点释放后,指向尾部节点的这个节点现在指向需要变更为NULL

(图中所列地址仅为方便展示而顺手写的)

void SLNOdelback(SLNO** pphead)
{
	assert(*pphead && pphead);//要进行删除操作,链表肯定不能为空
	SLNO* pres = *pphead;
	while (pres->next->next != NULL)
	{
		pres = pres->next;//找尾节点之前的那个节点
	}
	free(pres->next->next);//释放尾节点
	pres->next->next = NULL;//将指向尾节点的节点指向置空
	pres->next = NULL;
}
void test06()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);
}

int main()
{
	test06();
	return 0;
}

这里的测试没有问题


void test06()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

}

int main()
{
	test06();
	return 0;
}

这里看到出现了错误

前面几次函数都能正常运行

可见问题是出现在最后依次删除

void test06()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	/*SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);*/

	SLNOdelback(&phead);
	SLNOprint(phead);

}

int main()
{
	test06();
	return 0;
}

进行一下调试

这里可以看到错误是对空指针解应用,说明该函数在对链表仅剩一个节点时存在漏洞,这里进行一

下改进

void SLNOdelback(SLNO** pphead)
{
	assert(*pphead && pphead);//要进行删除操作,链表肯定不能为空
	SLNO* pres = *pphead;
	if (pres->next == NULL)
	{
		free(pres);
		*pphead = NULL;
		return;
	}//判断是不是头节点
	while (pres->next->next != NULL)
	{
		pres = pres->next;//找尾节点之前的那个节点
	}
	free(pres->next->next);//释放尾节点
	pres->next->next = NULL;//将指向尾节点的节点指向置空
	pres->next = NULL;
}
void test06()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

}

int main()
{
	test06();
	return 0;
}

这样就可以了

3.2.2 头删

从链表的头部进行删除

首节点指向的节点成为新的首节点

(图中所列地址仅为方便展示而顺手写的)


void test07()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);


}

int main()
{
	test07();
	return 0;
}

可以看到是可行的

3.2.3 指定位置删除

删除指定位置

删除后,该位置前的节点指向该位置之后的节点

(图中所列地址仅为方便展示而顺手写的)

void SLNOdel(SLNO** pphead, SLNO* pos)
{
	
	assert(pphead && *pphead);//要删除的链表不能为空
	assert(pos);//要删除的位置不能为空
	SLNO* pres = *pphead;
	while (pres->next != pos)
	{
		pres = pres->next;
	}//找删除位置的上一个节点
	pres->next = pos->next;//让pos的前一个节点指向pos的后一个节点
	free(pos);
	pos = NULL;
}
void test08()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOdel(&phead, find);
	SLNOprint(phead);

	find = Find(phead, 7);
	SLNOdel(&phead, find);
	SLNOprint(phead);
}


int main()
{
	test08();
	return 0;
}

这里测试着是没啥问题

测试一下首节点


void test08()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOdel(&phead, find);
	SLNOprint(phead);

	find = Find(phead, 7);
	SLNOdel(&phead, find);
	SLNOprint(phead);

	SLNOdel(&phead, phead);
	SLNOprint(phead);
}


int main()
{
	test08();
	return 0;
}

代码异常退出了,调试看看

对空指针解应用了

优化一下针对首节点这种情况

//任意位置删除
void SLNOdel(SLNO** pphead, SLNO* pos)
{
	
	assert(pphead && *pphead);//要删除的链表不能为空
	assert(pos);//要删除的位置不能为空
	SLNO* pres = *pphead;
	if (pres == pos)
	{
		*pphead = pres->next;//让首节点指向的节点成为新的首节点
		free(pres);
		pres = NULL;
		return;
	}//判断是不是首节点
	while (pres->next != pos)
	{
		pres = pres->next;
	}//找删除位置的上一个节点
	pres->next = pos->next;//让pos的前一个节点指向pos的后一个节点
	free(pos);
	pos = NULL;
}

void test08()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOdel(&phead, find);
	SLNOprint(phead);

	find = Find(phead, 7);
	SLNOdel(&phead, find);
	SLNOprint(phead);

	SLNOdel(&phead, phead);
	SLNOprint(phead);
}


int main()
{
	test08();
	return 0;
}

这样就可行了

3.2.4 指定位置后删除

将指定位置后的那个节点删除

指定位置指向被删除节点所指向的节点

(图中所列地址仅为方便展示而顺手写的)

pos不能是个空指针,且pos后的这个节点不能为空

void SLNOdelafter(SLNO** pphead, SLNO* pos)//任意位置后删除
{
	assert(pphead && *pphead);
	SLNO* pres = *pphead;
	while (pres != pos)
	{
		pres = pres->next;
	}//找到pos
	SLNO* temp = pres->next->next;//pos后的节点所指向节点
	free(pres->next);//释放pos后的一个节点
	pres->next = temp;//pos指向被删除的节点所指向的节点
}

void test10()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);
	SLNO* find = Find(phead, 4);

	SLNOdelafter(&phead, find);
	SLNOprint(phead);

	SLNOdelafter(&phead, phead);
	SLNOprint(phead);
	
	//find = Find(phead, 7);
	//SLNOdelafter(&phead, find);
   //	SLNOprint(phead);

}

int main()
{
	test10();
	return 0;
}

3.2.5 链表的销毁

链表是动态申请来的空间,那肯定就会有释放

将所有节点都释放,链表就被销毁了

在进行一个节点的释放的时候,要先将该节点指向的下一个节点的地址先保存起来

void SLNODestory(SLNO** pphead)
{
	assert(pphead && *pphead);
	SLNO* pres = *pphead;
	while (pres)
	{
		SLNO* temp = pres->next;//保存下一节点的地址
		free(pres);
		pres = NULL;//释放
		pres = temp;//再到下个节点
	}
	*pphead = NULL;//最后再让头节点置空
	return;
}
void test09()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNODestory(&phead);
	SLNOprint(phead);
}


int main()
{
	test09();
	return 0;
}

可以看到也是可行的

3.3 查

在指定位置插入删除的函数中已经说过了,这里就不再赘述

SLNO* Find(SLNO* phead, luck num)
{
	SLNO* pres = phead;
	while (num != pres->data && pres != NULL)
	{
		pres = pres->next;
	}
	if (pres == NULL)
	{
		return NULL;
	}
	return pres;
}

3.4 改

找到要修改的位置进行修改就好了

void SLNOmodify(SLNO** pphead, SLNO* pos, luck X)
{
	assert(pphead && *pphead);
	assert(pos);
	SLNO* pres = *pphead;
	while (pres != pos)
	{
		pres = pres->next;
	}
	pres->data = X;
}

void test11()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOmodify(&phead, find, 2);
	SLNOprint(phead);

}


int main()
{
	test11();
	return 0;
}

四、完整代码

#pragma once

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

//Seqlist.h

typedef int luck;//存储不同类型数据,重命名,这样后面就只用改这里的int
struct SListNOde
{
	luck data;//存储的数据
	struct SListNOde* next;//指向下一个节点的指针
};

typedef struct SListNOde  SLNO;


void SLNOprint(SLNO* phead);//打印每个节点处的数据

SLNO* SLNOIn();//初始化节点

void SLNOInsertback(SLNO** pphead, luck X);//单链表的尾插

void SLNOInsertfront(SLNO** pphead, luck X);//单链表的头插

void SLNOInsertbefore(SLNO** pphead, SLNO* pos, luck X);//任意位置前插入

SLNO* Find(SLNO* phead, luck num);//这是查找函数

void SLNOInsertafter(SLNO** pphead, SLNO* pos, luck X);//任意位置后插入

void SLNOdelback(SLNO** pphead);//尾删

void SLNOdelfront(SLNO** pphead);//头删

void SLNOdel(SLNO** pphead, SLNO* pos);//任意位置删除

void SLNOdelafter(SLNO** pphead, SLNO* pos);//任意位置后删除

void SLNODestory(SLNO** pphead);//链表的销毁

void SLNOmodify(SLNO** pphead, SLNO* pos, luck X);//链表的修改

 

#include"Seqlist.h"

//Seqlist.c

//打印每个节点处的数据

void SLNOprint(SLNO* phead)
{
	SLNO* pres = phead;
	while (pres)//节点处不是空指针就有数据
	{
		printf("%d->", pres->data);
		pres = pres->next;
	}
	printf("NULL\n");
}

SLNO* SLNOIn()//节点申请空间
{
	SLNO* newnode = (SLNO*)malloc(sizeof(SLNO));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	return newnode;
}

//尾插数据
void SLNOInsertback(SLNO** pphead, luck X)
{
	assert(pphead);
	SLNO* pres = *pphead;//用来查找尾
	SLNO* newnode = SLNOIn();
	newnode->data = X;
	newnode->next = NULL;

	//如果拿到的链表是一个空链表,那么进行pres->next就肯定会报错,不能对NULL进行解应用

	//那就单独判断一下

	//如果是这种情况,那么就直接申请一个新节点就好了

	if (pres == NULL)
	{
		*pphead = newnode;
		return;
	}

	//要从尾部进行插入,那就要找到尾部节点
	while (pres->next)
	{
		pres = pres->next;//尾部节点是该节点保存的下个节点地址为NULL
	}
	pres->next = newnode;
}

//头部数据插入
void SLNOInsertfront(SLNO** pphead, luck X)
{
	assert(pphead);
	SLNO* pres = *pphead;//保存头节点的位置
	SLNO* newnode = SLNOIn();//申请空间
	newnode->data = X;
	newnode->next = pres;
	*pphead = newnode;
}

//查找节点位置
SLNO* Find(SLNO* phead, luck num)
{
	SLNO* pres = phead;
	while (pres != NULL && num != pres->data  )
	{
		pres = pres->next;//找到要查找数据的地址
	}
	if (pres == NULL)
	{
		return NULL;//为空了说明链表中没有要查找的数据
	}
	return pres;
}

//插入指定位置前
void SLNOInsertbefore(SLNO** pphead, SLNO* pos, luck X)
{
	assert(pphead && pos && *pphead);//要插入的位置肯定不能为空
	SLNO* pres = *pphead;
	if (pos == pres)
	{
		SLNOInsertfront(pphead, X);
		return;
	}//如果是在首节点处插入就直接调用头插函数了
	SLNO* newnode = SLNOIn();//申请新节点
	while (pres->next != pos)
	{
		pres = pres->next;
	}//找插入位置前的那个节点
	SLNO* temp = pres->next;//保存插入位置前节点指向的下一个节点的指针
	newnode->data = X;
	newnode->next = temp;//原指向节点为新插入位置所指向的节点
	pres->next = newnode;//让插入为位置前节点的指向变更为插入的节点
}

//插入指定位置后
void SLNOInsertafter(SLNO** pphead, SLNO* pos, luck X)
{
	assert(*pphead && pos);
	SLNO* pres = *pphead;
	SLNO* newnode = SLNOIn();
	while (pres != pos)
	{
		pres = pres->next;
	}//找到插入节点
	SLNO* temp = pres->next;//保存插入原指向位置
	newnode->data = X;
	newnode->next = temp;
	pres->next = newnode;
}

//尾删
void SLNOdelback(SLNO** pphead)
{
	assert(*pphead && pphead);//要进行删除操作,链表肯定不能为空
	SLNO* pres = *pphead;
	if (pres->next == NULL)
	{
		free(pres);
		*pphead = NULL;
		return;
	}//判断是不是头节点
	while (pres->next->next != NULL)
	{
		pres = pres->next;//找尾节点之前的那个节点
	}
	free(pres->next->next);//释放尾节点
	pres->next->next = NULL;//将指向尾节点的节点指向置空
	pres->next = NULL;
}

//头删
void SLNOdelfront(SLNO** pphead)
{
	assert(pphead && *pphead);//要删除链表不能为空
	SLNO* pres = *pphead;
	*pphead = pres->next;//让首节点指向的节点成为新的节点
	free(pres);//释放原首节点
	pres = NULL;
}

//任意位置删除
void SLNOdel(SLNO** pphead, SLNO* pos)
{
	
	assert(pphead && *pphead);//要删除的链表不能为空
	assert(pos);//要删除的位置不能为空
	SLNO* pres = *pphead;
	if (pres == pos)
	{
		*pphead = pres->next;//让首节点指向的节点成为新的首节点
		free(pres);
		pres = NULL;
		return;
	}//判断是不是首节点
	while (pres->next != pos)
	{
		pres = pres->next;
	}//找删除位置的上一个节点
	pres->next = pos->next;//让pos的前一个节点指向pos的后一个节点
	free(pos);
	pos = NULL;
}

//指定位置后删除
void SLNOdelafter(SLNO** pphead, SLNO* pos)
{
	assert(pphead && *pphead);
	assert(pos->next);
	SLNO* pres = *pphead;
	while (pres != pos)
	{
		pres = pres->next;
	}//找到pos
	SLNO* temp = pres->next->next;//pos后的节点所指向节点
	free(pres->next);//释放pos后的一个节点
	pres->next = temp;//pos指向被删除的节点所指向的节点
}

//链表的销毁
void SLNODestory(SLNO** pphead)
{
	assert(pphead && *pphead);
	SLNO* pres = *pphead;
	while (pres)
	{
		SLNO* temp = pres->next;//保存下一节点的地址
		free(pres);
		pres = NULL;//释放
		pres = temp;//再到下个节点
	}
	*pphead = NULL;//最后再让头节点置空
	return;
}

void SLNOmodify(SLNO** pphead, SLNO* pos, luck X)
{
	assert(pphead && *pphead);
	assert(pos);
	SLNO* pres = *pphead;
	while (pres != pos)
	{
		pres = pres->next;
	}
	pres->data = X;
}

 

#include"Seqlist.h"

//test.c

void test01()
{
	SLNO* n1 = SLNOIn();
	n1->data = 1;
	n1->next = NULL;
	SLNOprint(n1);
}
void test02()
{
	SLNO* phead = NULL;
	SLNO* n1;
	SLNO* n2;
	SLNO* n3;
	SLNO* n4;
	n1 = SLNOIn();
	n2 = SLNOIn();
	n3 = SLNOIn();
	n4 = SLNOIn();
	phead = n1;//指向第一个节点的指针

	n1->data = 1;
	n2->data = 2;
	n3->data = 3;
	n4->data = 4;

	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = NULL;

	SLNOprint(phead);
}

void test03()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOInsertbefore(&phead, find, 10);
	SLNOprint(phead);

	find = Find(phead, 7);
	SLNOInsertbefore(&phead, find, 9);
	SLNOprint(phead);
}

void test04()
{
	SLNO* phead = NULL;
	SLNOInsertbefore(&phead, phead, 1);

	//SLNOInsertfront(&phead, 3);
	//SLNOInsertfront(&phead, 4);
	//SLNOInsertfront(&phead, 5);
	//SLNOInsertfront(&phead, 6);
	SLNOprint(phead);
}


void test05()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOInsertafter(&phead, find, 10);
	SLNOprint(phead);

	find = Find(phead, 7);
	SLNOInsertafter(&phead, find, 9);
	SLNOprint(phead);

	SLNOInsertafter(&phead, phead, 1);
	SLNOprint(phead);
}

void test06()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

	SLNOdelback(&phead);
	SLNOprint(phead);

}

void test07()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);

	SLNOdelfront(&phead);
	SLNOprint(phead);


}

void test08()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOdel(&phead, find);
	SLNOprint(phead);

	find = Find(phead, 7);
	SLNOdel(&phead, find);
	SLNOprint(phead);

	SLNOdel(&phead, phead);
	SLNOprint(phead);
}

void test09()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNODestory(&phead);
	SLNOprint(phead);
}

void test10()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);
	SLNO* find = Find(phead, 4);

	SLNOdelafter(&phead, find);
	SLNOprint(phead);

	SLNOdelafter(&phead, phead);
	SLNOprint(phead);
	
	//find = Find(phead, 7);
	//SLNOdelafter(&phead, find);
   //	SLNOprint(phead);

}
void test11()
{
	SLNO* phead = NULL;
	SLNOInsertback(&phead, 3);
	SLNOInsertback(&phead, 4);
	SLNOInsertback(&phead, 5);
	SLNOInsertback(&phead, 6);
	SLNOInsertback(&phead, 7);
	SLNOprint(phead);

	SLNO* find = Find(phead, 4);
	SLNOmodify(&phead, find, 2);
	SLNOprint(phead);

}


int main()
{
	test11();
	return 0;
}

  • 35
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单单链表应用实例代码,使用C语言实现: ```c #include <stdio.h> #include <stdlib.h> // 定义链表结构体 typedef struct Node { int data; struct Node* next; } node; // 创建新节点 node* create_node(int data) { node* new = (node*)malloc(sizeof(node)); new->data = data; new->next = NULL; return new; } // 在链表末尾插入节点 void insert_node(node** head, int data) { node* new = create_node(data); if (*head == NULL) { *head = new; return; } node* current = *head; while (current->next != NULL) { current = current->next; } current->next = new; } // 删除指定值的节点 void delete_node(node** head, int data) { if (*head == NULL) { return; } if ((*head)->data == data) { node* temp = *head; *head = (*head)->next; free(temp); return; } node* current = *head; while (current->next != NULL && current->next->data != data) { current = current->next; } if (current->next != NULL) { node* temp = current->next; current->next = current->next->next; free(temp); } } // 打印链表 void print_list(node* head) { printf("Linked List: "); while (head != NULL) { printf("%d ", head->data); head = head->next; } printf("\n"); } int main() { node* head = NULL; insert_node(&head, 1); insert_node(&head, 2); insert_node(&head, 3); insert_node(&head, 4); insert_node(&head, 5); print_list(head); delete_node(&head, 3); print_list(head); delete_node(&head, 1); print_list(head); return 0; } ``` 该代码实现了一个单链表的基本操作,包括创建节点、在末尾插入节点、删除指定值的节点和打印链表。可以通过在 `main` 函数中调用这些函数来使用这个单链表

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值