数据结构之链表(单链表)

目录

一、链表的概念

二、链表的分类

三、单链表的实现

1. 创建新的节点

2. 打印链表

3. 链表的头插和尾插

尾插:要注意第一次插入时链表为空的情况。

头插:

4. 单链表的头删和尾删

尾删:注意链表中只有一个元素的情况。且要保存尾节点的前一个节点。

头删:

5. 单链表的查找


一、链表的概念

        链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表

中的 指针链接 次序实现的 。链表实际上就像一列火车一样,每一个节点都是火车的一节车厢。
数据结构之链表(单链表)_第1张图片

 

注意:1.从图上可以看出,链式结构在逻辑上是是连续的,但是在物理上不一定连续

           2.现实中的节点一般都是从堆上申请出来的。

           3.从堆上申请的空间是按照一定的策略来分配的,连续两次申请的空间可能连续也可能不连续。 

二、链表的分类

        从严格上来讲,链表有:单向/双向, 带头/不带头(哨兵位头节点), 循环/不循环 这些形式,这些形式组合起来共有8种结构,但是本文章只会讲两种基本链表:单向不带头不循环(以下简称单链表)和双向带头循环链表(以下简称双向链表)。掌握以上两种链表的结构以后剩下的结构也就顺其自然的掌握了。

数据结构之链表(单链表)_第2张图片

三、单链表的实现

        我们要知道单链表是一种数据结构,而提到数据结构,无非就是增删查改这些功能 。

以下是单链表的相关声明。

// 1、无头+单向+非循环链表增删查改实现
typedef int SLTDateType;
typedef struct SListNode
{
 SLTDateType data;
 struct SListNode* next;
}SListNode;
// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);

        在进行增删查改之前,我们先写两个函数,一个是创建新节点,一个是打印链表从而观察操作是否实现。 

1. 创建新的节点

        如链表的概念所说,链表的节点是从堆上申请空间,所以我们要使用malloc函数来开辟一块空间,并将值存入data中。

// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	// 检查开辟空间是否成功
	if (newnode == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	newnode->data = x;
	// 节点的next置空防止野指针
	newnode->next = NULL;
	return newnode;
}

2. 打印链表

// 单链表打印
void SListPrint(SListNode* plist)
{
	// 定义一个指针来遍历链表
	SListNode* cur = plist;
	while (cur)
	{
		// 遍历过程
		printf("%d->",cur->data);
		cur = cur->next;
	}
	printf("NULL");
}

3. 链表的头插和尾插

尾插:要注意第一次插入时链表为空的情况。

// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	// 因为要对链表进行修改,所以我们要使用二级指针
	SListNode* head = *pplist;
	if (head == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		// 因为是尾插,所以要先找到尾
		// 找尾
		SListNode* tail = head;
		while (tail->next)
		{
			tail = tail->next;
		}
		// 尾插
		tail->next = newnode;
	}
	
}

头插:

// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	newnode->next = *pplist;
	*pplist = newnode;
}

4. 单链表的头删和尾删

尾删:注意链表中只有一个元素的情况。且要保存尾节点的前一个节点。

// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
    assert(*pplist);
	SListNode* phead = *pplist;
	if (phead->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* tail = phead;
		SListNode* prev = NULL;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;
		prev->next = NULL;
	}
}

头删:

// 单链表头删
void SListPopFront(SListNode** pplist)
{
	assert(*pplist);
	SListNode* phead = *pplist;
	if (phead->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* newHead = phead->next;
		free(phead);
		phead = NULL;
		*pplist = newHead;
	}
}

5. 单链表的查找

// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* cur = plist;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

单链表的修改就是在单链表的查找的基础上实现的,所以这里不会进行过多赘述。

你可能感兴趣的:(数据结构,链表,c语言)