数据结构与算法之美:单链表

        Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!

我的博客:<但凡.

我的专栏:《数据结构与算法之美》、《编程之路》、《题海拾贝》

欢迎点赞,关注!

目录

 

1、什么是链表

2、链表的实现 (C语言)

2.1节点的初始化

2.2节点的打印

2.3节点的插入

2.3.1头插

2.3.2尾插

2.3.3与顺序表对比

2.4节点的删除

2.4.1头删

2.4.2尾删

2.5查找

2.6指定位置之前插入数据

2.7指定位置之前插入数据

2.8删除数据

2.8.1删除指定位置之后的数据

2.8.2删除指定位置的数据

2.9销毁链表

3、C++实现部分单链表


 

1、什么是链表

        虽然链表在之前结构体那一部分介绍过,但是我们在这里再介绍一下。

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

        说白了就是我们给定几块空间存储数据,这几块空间就叫节点。每个节点除了存储数据以外还存储着下一个节点的地址,这样就能用过一个节点访问下一个节点。

数据结构与算法之美:单链表_第1张图片

2、链表的实现 (C语言)

        我们还是向上次顺序表的实现一样,创建头文件和两个源文件,我们链表的增删查改和顺序表一样都用函数包装起来。同样我们的节点由结构体实现,包含两个元素:1、存放数据,2、下一个节点的地址。

2.1节点的初始化

        我们为节点开辟空间,这个操作也叫新建节点:

SListNode* BuySListNode(SLTDateType x)
{
	SListNode* Node = (SListNode*)malloc(sizeof(SListNode));
	Node->data = x;
	return Node;
}

        其中,x是我们想存入的数据,在初始化节点的时候我们给定节点存储的数据。

2.2节点的打印

        现在假设我们存入了几个节点的数据,我们想要打印一下:

void SListPrint(SListNode* plist)
{
	SListNode* pcur = plist;
	while (pcur->next)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL");
}

        我们先创建一个指针变量叫pcur,让他一次遍历这个链表的每一个节点,从而打印出每一个结点的数据。

2.3节点的插入

2.3.1头插

void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SListNode* phead = *pplist;
		newnode->next = phead;
		phead = newnode;
	}
}

        需要注意的是,在头插时我们要先判断这个链表是否为空,如果为空那直接让新建节点作为这个链表的头节点就好了。

2.3.2尾插

void SListPushBack(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode=BuySListNode(x);
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SListNode* pcur = *pplist;
		while (pcur->next)
		{
			pcur - pcur->next;
		}
		pcur->next = newnode;
	}
}

2.3.3与顺序表对比

        在顺序表中,头插的时间复杂度为O(n),尾插的时间复杂度为O(1),而在链表中恰恰相反。所以说对于链表和顺序表的使用不是绝对的。(以上所说的链表是指单链表)

2.4节点的删除

2.4.1头删

void SListPopFront(SListNode** pplist)
{
	assert(pplist && *pplist);
	SListNode* pcur = (*pplist)->next;
	free(pplist);
	*pplist = pcur;
}

2.4.2尾删

void SListPopBack(SListNode** pplist)
{
	assert(pplist&&*pplist);
	SListNode* pcur = *pplist;
	SListNode* p = *pplist;
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	while (pcur->next)
	{
		p = pcur;
		pcur = pcur->next;
	}
	p->next = NULL;//先断开
	free(pcur);//先释放,再置空
	pcur = NULL;
}

        需要注意的是,只要是删除节点,我们就需要先判断一下,这个链表是否为空。这里我们选择直接断言一下。

2.5查找

SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* pcur = plist;
	while (pcur->data != x)
	{
		pcur = pcur->next;
	}
	return pcur;
}

        我们这里想要查找的是存储数据为x的节点,返回类型为SListNode*

2.6指定位置之前插入数据

void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	SListNode* pcur = pos->next;
	pos->next = newnode;
	newnode->next = pcur;
}

我们需要判断一下这个节点是否存在。

2.7指定位置之前插入数据

void SListInsert(SListNode** head,SListNode* pos, SLTDateType x)
{
	assert(pos);
	if (pos == *head)
	{
		SListPopFront(pos);
	}
	else
	{
		SListNode* newnode = BuySListNode(x);
		SListNode* pcur = *head;
		while ((pcur->next) != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = newnode;
		newnode->next = pos;
	}
}

        这里我们先判断一下,如果pos是第一个节点,那就直接调用头插的函数。

2.8删除数据

2.8.1删除指定位置之后的数据

void SListEraseAfter(SListNode* pos)
{
	assert(pos&&pos->next);
	SListNode* del = pos->next;
	pos->next = del->next;
	free(del);//别忘了释放内存
	del = NULL;
}

2.8.2删除指定位置的数据

void SLTErase(SListNode** pphead, SListNode* pos)
{
	assert(pos);
	if (pos == *pphead)
	{
		SListPopFront(pphead);
	}
	SListNode* pcur = *pphead;
	while (pcur->next != pos)
	{
		pcur = pcur->next;
	}
	pcur->next = pos->next;
	free(pos);
	pos = NULL;
}

2.9销毁链表

        和顺序表一样,我们用完了应该销毁链表

void SLTDestroy(SListNode** pphead)
{
	assert(pphead);
	SListNode* pcur = *pphead;
	while (pcur)
	{
		SListNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

3、C++实现部分单链表

#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;
typedef struct Node
{
	int a;
	Node* next;

}Node;
Node* BuyNode(int n)
{
	Node* NewNode = new Node;
	if (NewNode == NULL)
	{
		cout << "节点内存申请失败!" << endl;
		return NULL;
	}
	NewNode->a = n;
	NewNode->next = NULL;
	return NewNode;
}
void TouCha(Node* & ph, int x)
{
	Node* NewNode = BuyNode(x);
	NewNode->next = ph;
	ph = NewNode;
}
int main()
{
	//初始化链表
	Node* phead=BuyNode(5);
	//节点的插入
	TouCha(phead,5);
	cout << phead->a << endl;
	return 0;
}

        我们还没说C++,所以这部分只是作为了解,后续我会出一篇从C到C++过度的博客,所以说现在这部分内容可以不看,当然我也没写全,就写了个简单的头插。

        在C语言实现单链表时,我们对链表进行更改(插入、删除等)需要传入头节点的地址,也就是说形参是个双指针。而在C++中,我i们不用双指针,使用&重命名就可以完成这个操作(如上代码)。

        好了,今天的内容就分享到这,我们下期再见!

 

 

你可能感兴趣的:(数据结构与算法之美,c语言,数据结构,c++)