单链表(c语言实现)(数据结构三)

发布于:2022-11-08 ⋅ 阅读:(311) ⋅ 点赞:(0)

 本文单链表详细,各位看官肯定一看就会(不行ctrl+c,ctrl+v)(^ v ^ )

目录

链表和顺序表的优缺对比

单链表展示

 原理:

学习单链表的意义

单链表实现

准备环节:

1.创建结构体

2.创建存储头节点地址的指针plist

3.可以创建首节点或者扩容的函数

最常用的结构

1.通过遍历找地址(演示代码为找尾地址)

2.安全检查

模块函数与思路

1.尾插函数

2.尾删函数

3.头插函数

4.头删

5.打印函数

6.查找函数

7.定点删除

8.插入函数

 9.销毁函数


因为顺序表存在缺点,由此衍生出来了链表,本文介绍链表中的单链表(需要一定的思维量,请做好准备,我尽量描述清楚,建议先学好指针,仔细看图,本文通篇依靠指针(一级指针,二级指针))

链表和顺序表的优缺对比

单链表展示

(节点即为结构体)

 原理:

创建一个有指针的结构体,在前一个结构体的指针中放后一个结构体的地址,从而形成可以链式访问(像链条一样,由第一个结构体可以单向一直访问到最后一个)

学习单链表的意义


1.很多oj题考查的都是单链表,
2.单链表更多作为更复杂数据结构的子结构。如:哈希桶,邻接表

注:但单链表的缺陷很多,单纯增删查改意义不大,链表存储数据还是要看双向链表,but,增删查改是做oj题的基础,写完了这些功能,你就半个脚指头进了链表基础

单链表实现

准备环节:

1.创建结构体

typedef int SLTDateType;//定义int为SLDateType
typedef struct SLTNode
{
	SLTDateType data;//用于存放数据
	SLTNode* next;//用于存放下一个结构体的地址
}SLTNode;//定义结构体SLTNode为SLTNode

2.创建存储头节点地址的指针plist

(由于未有后面的节点,先将指针变量赋NULL,防止越界访问)

	SLTNode* plist = NULL//创建名为plist的头节点的指针(地址),并将其赋空

3.可以创建首节点或者扩容的函数

使用malloc来申请空间(不知道malloc的请去看http://t.csdn.cn/TgXqD

函数需返回SLTNode*(即申请的结构体的地址),用于后面存储地址的操作

SLTNode* BuyListNode(SLTDateType x)//传所需要存的数据x(买内存,有意思)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//使用malloc向内存申请一个节点空间

	//防止内存申请失败(虽然现在操作系统基本不可能出现此情况,但要有安全意识)
    assert(newnode != NULL);

	newnode->data = x;//在结构体中存x
	newnode->next = NULL;//将新结构体的指针变量赋NULL
	return newnode;//返回申请的结构体的地址

}

如顺序表一样,单链表也有打印,头插,尾插,头删,尾删,查找,插入,定点删除,销毁

最常用的结构

1.通过遍历找地址(演示代码为找尾地址)


		SLTNode* tail = *pphead;//把头节点地址给新变量tail
		while (tail->next != NULL)//重点循环找最后一个节点的地址
		{
			tail = tail->next;
		}

2.安全检查

#include<assert>	
assert(!pphead);

用assert断言,为假终止程序并报错,养成好的代码素养

模块函数与思路

1.尾插函数

思路:先遍历找到尾节点,再将新节点连上即可

 

 代码实现

//为了可以修改首地址,需要传首地址的地址,即二级指针**pphead,x为所插入的数
void SListPushBack(SLTNode** pphead, SLTDateType x)
{
	SLTNode* newnode = BuyListNode(x);//使用刚刚买内存的函数来创建结构体

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)//重点循环
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}

}

2.尾删函数

思路:找到尾节点的地址,将其free,并将前一个节点的next附空(尾插的逆过程)

代码实现

void SListPopBack(SLTNode** pphead)
{
	assert(*pphead != NULL);//安全检查
	if ((*pphead)->next == NULL)//只有一个节点的情况
	{
		free((*pphead)->next);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		SLTNode* pos = tail;
		while (tail->next != NULL)
		{
			pos = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;
		pos->next = NULL;
	}

}

3.头插函数

思路:先买内存,然后将其的next附头地址,头地址附新节点地址

 代码实现

void SListPushFront(SLTNode** pphead, SLTDateType x)

{

	SLTNode* newnode = BuyListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}
v

4.头删

思路:头插的逆过程

代码实现

void SListPopFront(SLTNode** pphead)
{
	assert(*pphead != NULL);
	SLTNode* front = *pphead;
	*pphead = (*pphead)->next;
	free(front);
}

5.打印函数

思路:遍历加打印

代码实现

void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	if (cur == NULL)
	{
		printf("NULL ");//好看,提醒已经到头了
	}

}

6.查找函数

思路:遍历+比对

代码实现

//注意需要传回找到数据的地址
SLTNode* SListFind(SLTNode* phead, SLTDateType x)

{
	SLTNode* plist = phead;
	while (plist != NULL)
	{
		if (plist->data == x)
		{
			return plist;
		}
		else
		{
			plist = plist->next;
		}
	}
	return NULL;
}

7.定点删除

实现方法一:

思路:找到所需要删的数据的地址,用遍历找到他的上一个地址去删除他

 代码实现

//pos即find函数找到的地址
void SListEaserFornt(SLTNode** pphead, SLTNode* pos)

{assert(*pphead!=NULL);
	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SLTNode* pos2 = *pphead;
		while (pos2 ->next!= pos)
		{
			pos2 = pos2->next;
		}

		pos2->next = pos->next;
		free(pos);
	}
}

 

实现方法二:

思路:找到所需要删的数据的地址,直接删除他的下一个节点的内容

//pos即为find函数找到的函数地址
void SListEaserback(SLTNode* pos)
{
	assert(pos != NULL);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

 两种方法各有优劣

方法一:理解操作简单,但时间复杂度是O(n)

方法二:时间复杂度是O(1)

8.插入函数

思路:找到所需要的位置,返回地址,通过地址插入节点

代码实现

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)

{
	if (pos == *pphead)
	{
		SLTNode* newnoda = BuyListNode(x);
		newnoda->next = *pphead;
		*pphead = newnoda;

	}
	else {
		SLTNode* newnoda = BuyListNode(x);
		SLTNode* prev = *pphead;
		while (prev->next != pos) {
			prev = prev->next;
		}
		prev->next = newnoda;
		newnoda->next = pos;
	}
}

 9.销毁函数

思路:双指针,遍历加销毁,并将头指针附NULL,

代码实现

//思路:用pos定位当前节点,next定位下一个节点,free(pos),再把next赋给pos,next向下走一个
void SListDestroy(SLTNode** pphead)
{
	SLTNode* pos = *pphead;//将头节点地址传给pos和pphead
	SLTNode* next = *pphead;
	
	while (next != NULL)
	{   pos = next;
	next = next->next;
		free(pos);
		
	}
	*pphead = NULL;

	 
}

以上就是单链表整体函数的讲解,链表这玩意主要靠细节和画图,注意当前指针是哪个节点,next包含哪个节点,链表就没有难点

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到