秒懂单链表

各位武林侠士猜猜今天小阿紫给大家带来了什么呢???

想必大家都猜出来了吧!!!

学完顺序表,我们肯定要学习链表了呀!

首先我们看看我们来看看顺序表有哪些不足:静态顺序表我们就不多说了,我们来看一下动态顺序表。

动态顺序表:①插入数据,空间不够要增容 ②要求数据是依次存储的

缺陷:①如果空间不够,就需要增容。增容会付出一定的性能消耗,其次可能出现一定的性能浪费 ②头部或者中部左右的插入删除效率低------》O(n)


如何去解决它了,那就需要进入我们今天的重头戏——链表

链表的特点:①空间上,按需给空间  ②不要求物理空间连续,头部和中间的插入删除,不需要挪动数据

概念:链表是一种物理存储结构非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。

逻辑结构图

 通过以上的逻辑结构图,我们可以看出每个结点都包括两部分:数据和指针,通过指针指向下一个结点。每个结点我们都可以设置为结构体类型。

物理结构图:

通过以上的物理结构图,我们可以看除了最后一个结点的指针指向NULL,其余的指针都指向下一个结点的数据域地址。


 那我们接下来,做一下对单链表的增删查找等操作。

跟上我的步伐哦!相信各位勇敢者学完定有收获

 首先定义一个头文件:

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

#pragma once

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

//打印
void SListPrint(SLTNode* phead);

//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);

//头插
void SListPushFront(SLTNode** pphead, SLTDataType x);

//头删
void SListPopFront(SLTNode** pphead);

//尾删
void SListPopBack(SLTNode** pphead);

//查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
//在pos的前面插入x
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos);

这个头文件主要包含了:1.库函数的声明  2.定义了一个SLTNod的结构体类型 3.自定义函数的声明

在定义一个.c的测试文件:

#include"SList.h"

void TestSList1()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushFront(&plist,5);
	SListPushFront(&plist,6);
	SListPushFront(&plist,7);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	//想在3的前面插入一个30
	SLTNode* pos = SListFind(plist, 6);
	if (pos)
	{
		SListInsert(&plist, pos, 10);
	}
	SListPrint(plist);

	pos = SListFind(plist, 10);
	if (pos)
	{
		SListErase(&plist, pos);
	}
	SListPrint(plist);
}

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

测试文件:通过主函数的调用TestList1,TestList1中首先定义了一个结构体指针指向SLTNode类型的结构体.

请各位思考一下为什么不定义一个结构体变量,反而定义一个结构体指针了???

如图所示:

 观图所知,定义一个同类型的结构体指针,可以指向头结点,然后通过头结点的指针域访问第二个结点,依次如下。反而结构体变量却达不到这种效果。

那我们接下来实现头文件中的自定义函数吧!!!

//打印
void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

疑惑解答: 

疑惑①:大家学到这里是不是有点疑惑,但是千万别划走哦,有些侠士会想连插入都没有学,怎么打印呀!先别急,首先打印操作在单链表中相对其它操作简单一点,所以先让大家有先接受一下打印。

疑惑②:现在大家肯定还有一个疑惑,怎么让phead指针指向头结点地址了。

别急、别急学完插入之后,你就将知道phead指针是怎么指向头结点的地址了

跟上我的步伐,开始学习插入了

//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;
	newnode->next = NULL;

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		//找到尾节点的指针
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		//链接尾节点
		tail->next = newnode;
	}
}

 学到这里大家,应该知道phead指针是怎样指向头结点的了吧!大家应该也知道如何实现尾插了吧!

大家可以自己先不看下面的讲解,自己先试一试头插,然后写完之后再看看与代码中的头插有哪里不同吧!!!

//头插
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;
	newnode->next = NULL;

	newnode->next = *pphead;
	*pphead = newnode;

}

注:要是链表为空,那我们新增的结点的指针域本来就是空,然后*pphead里面也是空,把*pphead指向新结点,新结点的指针域为空,在赋一个空,不影响 

//头删
void SListPopFront(SLTNode** pphead)
{
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

//尾删
void SListPopBack(SLTNode** pphead)
{
	//1.空
	if (*pphead == NULL)
	{
		return;
	}
	//2.一个结点
	else if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	//3.多个节点,至少两个
	else
	{
		SLTNode* prev = NULL;
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}

}

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

//在查找的那个数的前面插入x
void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;
	newnode->next = NULL;

	if (pos == *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}

}

//删除pos位置的值
void SListErase(SLTNode** pphead, SLTNode* pos)
{
	//1.为第一个结点或者为空
	if (pos==*pphead)
	{
		SListPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
	
}

好了今天就到这了,想必大家也学会到了许多啦!!!

        我们继续加油哦!

                        下篇博客是双链表哦!

QQ:2186529582
要是有不懂的地方随时加我哦!

猜你喜欢

转载自blog.csdn.net/m0_66488562/article/details/123402883