【数据结构初阶(c语言实现)】——单链表

目录

 

基本概念

什么是单链表(SLT)

图解​

单链表的结构体类型的定义

代码​​​​​​实现

图解

输入新数据的单链表结点的生成

代码实现

图解

单链表的尾部插入

 二级指针在单链表中基本操作的应用(以尾部插入为例)

图解

代码实现

图解

单链表的尾部删除

代码实现

图解

打印单链表内数据​​​​​​

代码实现

图解

用尾部插入和尾部删除对单链表进行修改

代码实现

输出结果

单链表的头部插入

代码实现

图解

​编辑单链表的头部删除

代码实现

图解

用尾部插入和尾部删除对单链表进行修改

代码实现

输出结果

单链表的按值查找

代码实现

单链表的指定结点插入

代码实现

图解

单链表的指定结点删除

代码实现

图解​

用指定结点插入和指定结点删除对单链表进行修改

代码实现

输出结果​

单链表的销毁

代码实现


基本概念

什么是单链表(SLT)

        通俗点来说,单链表其实就是许多个结点通过指针连接起来,每个结点内包含一个数据以及指向下一个结点的指针,通过指针将每个结点连接起来,形成一条“链”。由于结点内只有指向下一个结点的指针,因此单链表只能从头往后访问结点。具有头指针指向头结点(第一个结点),尾结点(最后一个结点)指向空地址。

图解【数据结构初阶(c语言实现)】——单链表_第1张图片

单链表的结构体类型的定义

代码​​​​​​实现

typedef int SLTdatatype; // 对数据的变量类型重新定义,方便后续只修改这一项

typedef struct SLTNode   // 单链表结点的结构体类型的定义
{
	SLTdatatype data;        // 数据域
	struct SLTNode* next;    // 指针域
}SLTNode;

图解

【数据结构初阶(c语言实现)】——单链表_第2张图片

输入新数据的单链表结点的生成

代码实现

// 声明
SLTNode* BuySLTNode(SLTdatatype x);

// 定义
SLTNode* BuySLTNode(SLTdatatype x)  // 创立输入新数据的结点
{
	SLTNode* newnode = (SLTdatatype*)malloc(sizeof(SLTNode)); // 创建输入新的结点
	if (newnode == NULL)       // 判断新结点是否创建成功
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;        // 给新结点输入新数据
	newnode->next = NULL;     // 给新结点的指针域输入空指针
	return newnode;           // 返回新结点的地址
}

// 调用
SLTNode* newnode = BuySLTNode(x);  // 提供给单链表的其他操作使用
图解
生成新结点
【数据结构初阶(c语言实现)】——单链表_第3张图片
新结点输入数据并置空【数据结构初阶(c语言实现)】——单链表_第4张图片

单链表的尾部插入

        在单链表的尾结点后插入一个新结点。

 二级指针在单链表中基本操作的应用(以尾部插入为例)

         当单链表为空时(即头指针为空),用尾部插入第一个结点时,若只是传入头指针进尾部插入的函数,让头指针指向新结点时,只是函数内头指针的“投影”头指针指向新结点,而头指针仍为空,而传入头指针的地址进尾部插入的函数,通过解引用即可访问头指针对它进行修改。

图解
【数据结构初阶(c语言实现)】——单链表_第5张图片【数据结构初阶(c语言实现)】——单链表_第6张图片

代码实现

// 声明
void SLTPushBack(SLTNode** head, SLTdatatype x);

// 定义
void SLTPushBack(SLTNode** head, SLTdatatype x)     // 尾部插入
{
	assert(head);                        // 避免用户传错参数
	SLTNode* newnode = BuySLTNode(x);    // 创建输入新数据的结点
	if (*head == NULL)                   // 单链表没有结点的情况
	{
		*head = newnode;
	}
	else   // 单链表具有结点的情况
	{
		SLTNode* tail = *head;      // 创建"尾"指针
		while (tail->next != NULL)  
		{
			tail = tail->next;     // "尾"指针后移
		}
		tail->next = newnode;      // 连接新结点
	}
}

// 调用
SLTPushBack(&SList, x);  // x是具体要输入的数据
图解
生输入新数据的结点【数据结构初阶(c语言实现)】——单链表_第7张图片
单链表没有结点时的情况
【数据结构初阶(c语言实现)】——单链表_第8张图片单链表有结点时的情况
【数据结构初阶(c语言实现)】——单链表_第9张图片        创建“尾”指针【数据结构初阶(c语言实现)】——单链表_第10张图片
        “尾”指针移动到最后一个结点【数据结构初阶(c语言实现)】——单链表_第11张图片
        尾结点与新结点连接【数据结构初阶(c语言实现)】——单链表_第12张图片

单链表的尾部删除

        删除单链表最后一个结点。

代码实现

// 声明
void SLTPopBack(SLTNode** head);

// 定义
void SLTPopBack(SLTNode** head)  // 尾部删除
{
	assert(head);                // 避免用户传错参数
	assert(*head);               // 单链表没有结点的情况下断言报错
	if ((*head)->next == NULL)   // 单链表只有一个结点的情况
	{
		free(*head);             // 删除结点
		*head = NULL;
	}
	else                         // 单链表有多个结点的情况
	{
		SLTNode* tail = *head;   // 创建"尾"指针
		while ( tail->next->next != NULL )
		{
			tail = tail->next;   // "尾"指针后移
		}
		free(tail->next);        // 删除尾部最后一个结点
		tail->next = NULL;      
	}
}

// 调用
SLTPopBack(&SList);
图解
单链表有一个结点时的情况【数据结构初阶(c语言实现)】——单链表_第13张图片
单链表有多个结点时的情况【数据结构初阶(c语言实现)】——单链表_第14张图片
        创建“尾”指针
【数据结构初阶(c语言实现)】——单链表_第15张图片
        “尾”指针移到倒数第二个结点【数据结构初阶(c语言实现)】——单链表_第16张图片
        删除最后一个结点【数据结构初阶(c语言实现)】——单链表_第17张图片

打印单链表内数据​​​​​​

代码实现

// 声明
void SLTPrint(SLTNode* SList);

// 定义
void SLTPrint(SLTNode* SList)  // 打印单链表内数据
{
	SLTNode* cur = SList;      // 创建了"遍历"指针
	while (cur)
	{
		printf("%d->", cur->data);  // 打印数据
		cur = cur->next;            // "遍历"指针后移
	}
	printf("NULL\n");
}

// 调用
SLTPrint(SList);
图解
创建“遍历”指针【数据结构初阶(c语言实现)】——单链表_第18张图片
遍历结点打印数据【数据结构初阶(c语言实现)】——单链表_第19张图片

用尾部插入和尾部删除对单链表进行修改

代码实现

// 相关头文件
#include    
#include   
#include   

// 需要的相关定义

// 单链表结点的结构体类型的定义
typedef int SLTdatatype; // 对数据的变量类型重新定义,方便后续只修改这一项
typedef struct SLTNode   
{
	SLTdatatype data;        // 数据域
	struct SLTNode* next;    // 指针域
}SLTNode;

// 创立输入新数据的结点
SLTNode* BuySLTNode(SLTdatatype x)  
{
	SLTNode* newnode = (SLTdatatype*)malloc(sizeof(SLTNode)); // 创建输入新的结点
	if (newnode == NULL)       // 判断新结点是否创建成功
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;        // 给新结点输入新数据
	newnode->next = NULL;     // 给新结点的指针域输入空指针
	return newnode;           // 返回新结点的地址
}

// 尾部插入
void SLTPushBack(SLTNode** head, SLTdatatype x)     
{
	assert(head);                        // 避免用户传错参数
	SLTNode* newnode = BuySLTNode(x);    // 创建输入新数据的结点
	if (*head == NULL)                   // 单链表没有结点的情况
	{
		*head = newnode;
	}
	else   // 单链表具有结点的情况
	{
		SLTNode* tail = *head;      // 创建"尾"指针
		while (tail->next != NULL)
		{
			tail = tail->next;     // "尾"指针后移
		}
		tail->next = newnode;      // 连接新结点
	}
}

// 尾部删除
void SLTPopBack(SLTNode** head)  
{
	assert(head);                // 避免用户传错参数
	assert(*head);               // 单链表没有结点的情况下断言报错
	if ((*head)->next == NULL)   // 单链表只有一个结点的情况
	{
		free(*head);             // 删除结点
		*head = NULL;
	}
	else                         // 单链表有多个结点的情况
	{
		SLTNode* tail = *head;   // 创建"尾"指针
		while (tail->next->next != NULL)
		{
			tail = tail->next;   // "尾"指针后移
		}
		free(tail->next);        // 删除尾部最后一个结点
		tail->next = NULL;
	}
}

// 打印单链表内数据
void SLTPrint(SLTNode* SList)  
{
	SLTNode* cur = SList;      // 创建了"遍历"指针
	while (cur)
	{
		printf("%d->", cur->data);  // 打印数据
		cur = cur->next;            // "遍历"指针后移
	}
	printf("NULL\n");
}

// 主函数
SLTNode* SList = NULL; // 创建空的头指针
int main()
{
	// 生成一个 1-5 的单链表
	SLTPushBack(&SList, 1);
	SLTPushBack(&SList, 2);
	SLTPushBack(&SList, 3);
	SLTPushBack(&SList, 4);
	SLTPushBack(&SList, 5);
	SLTPrint(SList);        
	// 删除最后一个结点
	SLTPopBack(&SList);
	SLTPrint(SList);
}

输出结果

【数据结构初阶(c语言实现)】——单链表_第20张图片

        用尾部插入创建的单链表数据跟输入顺序一样,尾部删除就是从最后一个结点删除。

单链表的头部插入

        在单链表的头结点前插入一个新结点。

代码实现

// 声明
void SLTPushFront(SLTNode** head, SLTdatatype x);

// 定义
void SLTPushFront(SLTNode** head, SLTdatatype x)// 头部插入
{
	assert(head);                     // 避免用户传错参数    
	SLTNode* newnode = BuySLTNode(x); // 创建输入新数据的新结点
	newnode->next = *head;            
	*head = newnode;                  // 单链表指针与新结点连接
}

// 调用
SLTPushFront(&SList, x); // x是具体要输入的数据
图解
【数据结构初阶(c语言实现)】——单链表_第21张图片

【数据结构初阶(c语言实现)】——单链表_第22张图片单链表的头部删除

        删除单链表的第一个结点。

代码实现

// 声明
void SLTPopFront(SLTNode** head);

// 定义
void SLTPopFront(SLTNode** head)// 头部删除
{
	assert(head);              // 避免用户传错参数
	assert(*head);             // 单链表没有结点的情况下断言报错
	SLTNode* first = *head;	   // 创建"头"指针
	*head = first->next;
	free(first);               // 释放头结点
	first = NULL;
}

// 调用
SLTPopFront(&SList);

图解【数据结构初阶(c语言实现)】——单链表_第23张图片【数据结构初阶(c语言实现)】——单链表_第24张图片【数据结构初阶(c语言实现)】——单链表_第25张图片

用尾部插入和尾部删除对单链表进行修改

代码实现

// 相关头文件
#include    
#include   
#include   

// 需要的相关定义

// 单链表结点的结构体类型的定义
typedef int SLTdatatype; // 对数据的变量类型重新定义,方便后续只修改这一项
typedef struct SLTNode
{
	SLTdatatype data;        // 数据域
	struct SLTNode* next;    // 指针域
}SLTNode;

// 创立输入新数据的结点
SLTNode* BuySLTNode(SLTdatatype x)
{
	SLTNode* newnode = (SLTdatatype*)malloc(sizeof(SLTNode)); // 创建输入新的结点
	if (newnode == NULL)       // 判断新结点是否创建成功
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;        // 给新结点输入新数据
	newnode->next = NULL;     // 给新结点的指针域输入空指针
	return newnode;           // 返回新结点的地址
}

// 头部插入
void SLTPushFront(SLTNode** head, SLTdatatype x)
{
	assert(head);                     // 避免用户传错参数    
	SLTNode* newnode = BuySLTNode(x); // 创建输入新数据的新结点
	newnode->next = *head;
	*head = newnode;                  // 单链表指针与新结点连接
}

// 头部删除
void SLTPopFront(SLTNode** head)
{
	assert(head);              // 避免用户传错参数
	assert(*head);             // 单链表没有结点的情况下断言报错
	SLTNode* first = *head;	   // 创建"头"指针
	*head = first->next;
	free(first);               // 释放头结点
	first = NULL;
}

// 打印单链表内数据
void SLTPrint(SLTNode* SList)
{
	SLTNode* cur = SList;      // 创建了"遍历"指针
	while (cur)
	{
		printf("%d->", cur->data);  // 打印数据
		cur = cur->next;            // "遍历"指针后移
	}
	printf("NULL\n");
}

// 主函数
SLTNode* SList = NULL; // 创建空的头指针
int main()
{
	// 生成一个 5-1 的单链表
	SLTPushFront(&SList, 1);
	SLTPushFront(&SList, 2);
	SLTPushFront(&SList, 3);
	SLTPushFront(&SList, 4);
	SLTPushFront(&SList, 5);
	SLTPrint(SList);
	// 删除第一个结点
	SLTPopFront(&SList);
	SLTPrint(SList);
}

输出结果

【数据结构初阶(c语言实现)】——单链表_第26张图片

        用头部插入创建的单链表数据跟输入顺序相反,头部删除就是从第一个结点删除。

单链表的按值查找

代码实现

// 声明
SLTNode* SLTFind(SLTNode* head, SLTdatatype x);

// 定义
SLTNode* SLTFind(SLTNode* head, SLTdatatype x) // 按值查找
{
	SLTNode* cur = head;      // 创建"遍历"指针
	while (cur)
	{
		if (cur->data == x)   // 寻找单链表中与数据x匹配的结点
		{
			return cur;       // 返回数据匹配结点的地址  
		}
		cur = cur->next;      // "遍历"指针后移
	}
	return NULL;              // 单链表中找不到与数据x匹配的结点,返回空指针
}

// 调用
SLTNode* pos = SLTFind(SList,x); // x是想要在单链表内具体查找的数据

        原理跟打印单链表内数据的代码类似,可参考其图解自己画图理解

单链表的指定结点插入

        通过按值查找,找到对应数据的结点位置进行插入(例如:找到数值为2的结点是2号结点,则在2号结点插入新数据,而原来的2号结点变为3号结点),若不理解可结合后续——用指定结点插入和指定结点删除的代码输出结果进行理解。

代码实现

        原理是先在指定结点后插入一个新结点,再将指定结点的数据与新结点的数据互换,做到在指定结点插入新结点的效果。

// 声明
void SLTInsert(SLTNode* pos, SLTdatatype x);

// 定义
void SLTInsert(SLTNode* pos, SLTdatatype x) // pos结点处插入
{
	assert(pos);
	SLTNode* newnode = BuySLTNode(x);     // 创建新结点
	newnode->next = pos->next;            // 新结点与pos结点的下一个结点连接
	pos->next = newnode;                  // pos结点与新结点连接

	int temp = newnode->data;             // 创建临时变量存储新结点内数据
	newnode->data = pos->data;            // 新结点内数据改为pos结点内数据
	pos->data = temp;                     // pos结点内数据改为新结点内数据
}

// 调用
SLTInsert(pos,x); // x是具体要输入的数据
图解

【数据结构初阶(c语言实现)】——单链表_第27张图片【数据结构初阶(c语言实现)】——单链表_第28张图片【数据结构初阶(c语言实现)】——单链表_第29张图片

单链表的指定结点删除

        通过按值查找,找到对应数据的结点位置进行删除(例如:找到数值为2的结点是2号结点,则删除2号结点,而原来的3号结点变为新的2号结点),若不理解可结合后续——用指定结点插入和指定结点删除的代码输出结果进行理解。 

代码实现

        原理是将指定结点的数据换成下一个结点的数据,删除下一个结点的,做到在指定结点删除的效果。

// 声明
void SLTErase(SLTNode* pos);

// 定义
void SLTErase(SLTNode* pos)      // pos结点处删除
{
	assert(pos);
	assert(pos->next);           // pos结点的下一个结点不能为空
	SLTNode* temp = pos->next;   // 创建临时指针指向pos指向结点的下一个结点

	pos->data = temp->data;      // pos结点内数据改为下一结点内数据

	pos->next = temp->next;      // pos结点与其下下个结点连接
	free(temp);                  // 删除pos结点的下一个结点
	temp = NULL;
}

// 调用
SLTErase(pos);

    图解【数据结构初阶(c语言实现)】——单链表_第30张图片【数据结构初阶(c语言实现)】——单链表_第31张图片【数据结构初阶(c语言实现)】——单链表_第32张图片【数据结构初阶(c语言实现)】——单链表_第33张图片

用指定结点插入和指定结点删除对单链表进行修改

代码实现

// 相关头文件
#include    
#include   
#include   

// 需要的相关定义

// 单链表结点的结构体类型的定义
typedef int SLTdatatype; // 对数据的变量类型重新定义,方便后续只修改这一项
typedef struct SLTNode
{
	SLTdatatype data;        // 数据域
	struct SLTNode* next;    // 指针域
}SLTNode;

// 创立输入新数据的结点
SLTNode* BuySLTNode(SLTdatatype x)
{
	SLTNode* newnode = (SLTdatatype*)malloc(sizeof(SLTNode)); // 创建输入新的结点
	if (newnode == NULL)       // 判断新结点是否创建成功
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;        // 给新结点输入新数据
	newnode->next = NULL;     // 给新结点的指针域输入空指针
	return newnode;           // 返回新结点的地址
}

// 尾部插入
void SLTPushBack(SLTNode** head, SLTdatatype x)
{
	assert(head);                        // 避免用户传错参数
	SLTNode* newnode = BuySLTNode(x);    // 创建输入新数据的结点
	if (*head == NULL)                   // 单链表没有结点的情况
	{
		*head = newnode;
	}
	else   // 单链表具有结点的情况
	{
		SLTNode* tail = *head;      // 创建"尾"指针
		while (tail->next != NULL)
		{
			tail = tail->next;     // "尾"指针后移
		}
		tail->next = newnode;      // 连接新结点
	}
}

// 尾部删除
void SLTPopBack(SLTNode** head)
{
	assert(head);                // 避免用户传错参数
	assert(*head);               // 单链表没有结点的情况下断言报错
	if ((*head)->next == NULL)   // 单链表只有一个结点的情况
	{
		free(*head);             // 删除结点
		*head = NULL;
	}
	else                         // 单链表有多个结点的情况
	{
		SLTNode* tail = *head;   // 创建"尾"指针
		while (tail->next->next != NULL)
		{
			tail = tail->next;   // "尾"指针后移
		}
		free(tail->next);        // 删除尾部最后一个结点
		tail->next = NULL;
	}
}

// 打印单链表内数据
void SLTPrint(SLTNode* SList)
{
	SLTNode* cur = SList;      // 创建了"遍历"指针
	while (cur)
	{
		printf("%d->", cur->data);  // 打印数据
		cur = cur->next;            // "遍历"指针后移
	}
	printf("NULL\n");
}

// 按值查找
SLTNode* SLTFind(SLTNode* head, SLTdatatype x) 
{
	SLTNode* cur = head;      // 创建"遍历"指针
	while (cur)
	{
		if (cur->data == x)   // 寻找单链表中与数据x匹配的结点
		{
			return cur;       // 返回数据匹配结点的地址  
		}
		cur = cur->next;      // "遍历"指针后移
	}
	return NULL;              // 单链表中找不到与数据x匹配的结点,返回空指针
}

// pos结点处插入
void SLTInsert(SLTNode* pos, SLTdatatype x)
{
	assert(pos);
	SLTNode* newnode = BuySLTNode(x);     // 创建新结点
	newnode->next = pos->next;            // 新结点与pos结点的下一个结点连接
	pos->next = newnode;                  // pos结点与新结点连接

	int temp = newnode->data;             // 创建临时变量存储新结点内数据
	newnode->data = pos->data;            // 新结点内数据改为pos结点内数据
	pos->data = temp;                     // pos结点内数据改为新结点内数据
}

// pos结点处删除
void SLTErase(SLTNode* pos)      
{
	assert(pos);
	assert(pos->next);           // pos结点的下一个结点不能为空
	SLTNode* temp = pos->next;   // 创建临时指针指向pos指向结点的下一个结点

	pos->data = temp->data;      // pos结点内数据改为下一结点内数据

	pos->next = temp->next;      // pos结点与其下下个结点连接
	free(temp);                  // 删除pos结点的下一个结点
	temp = NULL;
}

// 主函数
SLTNode* SList = NULL; // 创建空的头指针
int main()
{
	// 生成一个 1-5 的单链表
	SLTPushBack(&SList, 1);
	SLTPushBack(&SList, 2);
	SLTPushBack(&SList, 3);
	SLTPushBack(&SList, 4);
	SLTPushBack(&SList, 5);
	SLTPrint(SList);
	
	SLTNode* pos1 = SLTFind(SList, 2); // 找到数值为2的结点
	SLTInsert(pos1, 100);              // 在数值为2的结点处插入数值为100的结点
	SLTPrint(SList);

	SLTNode* pos2 = SLTFind(SList, 100); // 找到数值为100的结点
	SLTErase(pos2);                      // 删除数值为100的结点
	SLTPrint(SList);
}

输出结果【数据结构初阶(c语言实现)】——单链表_第34张图片

单链表的销毁

        利用尾部删除,将单链表内的结点从后往前删除,直到最后删除所有结点,头指针置空。

代码实现

// 声明
void SLTDestroy(SLTNode** SList);

// 定义
void SLTDestroy(SLTNode** SList)  // 链表的销毁
{
	assert(SList);           // 避免用户传错
	assert(*SList);          // 判断链表是否为空

	while (*SList)           // 链表为空时停止循环
	{
		SLTPopBack(SList);   // 利用尾部删除来删除结点
	}


// 调用
SLTDestroy(&SList);

你可能感兴趣的:(【数据结构初阶(C语言实现)】,算法,数据结构,图论,c语言)