链表的基本操作(增删改查)超详细

什么是链表?
链表是一种物理存储上非连续、非顺序的存储结构。数据元素的逻辑顺序是通过链表中的指针链接次序实现的。就好比是班里的同学的学号、每个同学上课所坐的位置坑都不是固定的,但是通过他们之间学号的连续性,通过1号可以找到2号同学,链表就是这样,不仅存储了数值,还存储着下一个元素的地址

在这里插入图片描述
链表的基本操作有:初始化、销毁、增、删、改、查
首先给出链表结构体:
typedef struct Node{
int value;
struct Node* next;
}Node;
1、链表初始化`

void SListInit(Node** ppFirst)
{
	*ppFirst = NULL;
}

链表初始化就是将整个链表置空
2、链表销毁

void SListDestroy(Node* first)
{
	Node* next;
	for (Node* cur = first; cur != NULL; cur = next)
	{
		next = cur->next;
		free(cur);
	}
}

链表销毁就是将链表中每个元素所占用的空间都释放掉,这里用到遍历。
3、链表头插(插入的操作都要先申请空间)

void SListPushFront(Node** ppFirst,int v)
{
	Node* node = (Node*)malloc(sizeof(Node));
	node->value = v;
	node->next = *ppFirst;
	*ppFirst = node;
}

在这里插入图片描述
链表头插就是首先申请一个盒子(空间),把需要插入的数据,这里是v放入到盒子里,然后把这个盒子插入到链表中的第一个位置,需要变化的地方有*ppFirst,第一个元素的值,新插入的盒子的next此时指向了(*ppFirst)。
时间复杂度为O(1),这个我就不说明原因啦,有不懂得小伙伴们可以留言哦
4、链表头删

void SListPopFront(Node** ppFirst)
{
	assert(*ppFirst != NULL);
	Node* next = (*ppFirst)->next;
	free(*ppFirst);
	*ppFirst = next;
}

在这里插入图片描述
看代码,为什么首先要将第二个结点保存起来,原因是如果不事先将其保存下来的话,释放到第一个结点的空间后,就找不到第二个结点了。时间复杂度也是O(1)
5、链表的尾插

void SListPushBack(Node** ppFirst, int v)
{
	Node* node = (Node*)malloc(sizeof(Node));
	node->value = v;
	if (*ppFirst == NULL)
	{
		node->next = *ppFirst;
		*ppFirst = node;
		return;
	}
	Node* cur = *ppFirst;
	while (cur->next != NULL)
	{
		cur = cur->next;
	}
	cur->next = node;
	node->next = NULL;
}

链表尾插需要考虑链表中一个结点都没有的情况,其实链表中没有结点的头插就相当于尾插。尾插需要遍历整个链表,因为要找到最后一个结点,然后用最后一个结点的next指向新插入的结点,前提当然是先申请这个盒子用于存储需要尾插的那个数。走出while循环的cur就是最后一个结点。最后让新插入结点的next等于NULL。
6、链表尾删

void SListPopBack(Node** ppFirst)
{
	assert(*ppFirst != NULL);
	if ((*ppFirst)->next == NULL)
	{
		free(*ppFirst);
		*ppFirst = NULL;
		return;
	}
	Node* cur = *ppFirst;
	while (cur->next->next != NULL)
	{
		cur = cur->next;
	}
	free(cur->next);
	cur->next = NULL;
}

在这里插入图片描述
链表尾删也需要考虑链表中只有一个结点的情况,如果链表中只有一个结点就相当于头删,这里也需要遍历,找到倒数第二个结点,cur->next->next就表示倒数第二个结点,走出循环时就说明cur此时就是倒数第二个结点。让cur->next指向NULL。

猜你喜欢

转载自blog.csdn.net/www_zxf_csdn123/article/details/89740785