无头+单向+非循环链表的实现

1. 链表

1.1 链表的概念及结构

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

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.2 链表的分类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
在这里插入图片描述

在这里插入图片描述

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。

2. 接口实现

接口就是函数定义

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;//数据域
	struct SListNode* next;//指针域
}SLTNode;

//打印链表
void SLTPrint(SLTNode* phead);

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

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

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

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

//单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);

//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);

//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);

//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);

//删除pos位置之后的值
void SLEraseAfter(SLTNode* pos);

//链表的释放
void SLDestroy(SLTNode** pphead);

3. 链表的实现

3.1 打印链表

//打印链表
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;//找结构体下一个指针,循环才能走起来
	}
	printf("NULL\n");
}

3.2 头插

//头插
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//链表为空,pphead也不为空,因为它是头指针plist的地址
	//assert(*pphead);//不能断言,链表为空,也需要插入
	SLTNode* newnode = BuyLTNode(x);

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

3.3 尾插

//尾插
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//链表为空,pphead也不为空,因为它是头指针plist的地址
	//assert(*pphead);//链表为空,可以插入
	SLTNode* newnode = BuyLTNode(x);
	//空链表
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	//非空链表
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		SLTNode* newnode = BuyLTNode(x);
		tail->next = newnode;
	}
	
}

3.4 头删

//头删
void SLPopFront(SLTNode** pphead)
{
	assert(pphead);//链表为空,pphead也不为空,因为它是头指针plist的地址
	assert(*pphead);//空链表,不能头删

	一个节点
	//if (((*pphead)->next) == NULL)
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	多个节点
	//else
	//{
	//	SLTNode* head = *pphead;
	//	*pphead = head->next;
	//	free(head);
	//}

	SLTNode* del = *pphead;
	*pphead = del->next;
	free(del);
}

3.5 尾删

//尾删
void SLPopBack(SLTNode** pphead)
{
	//没有节点(空链表)
	//暴力检查
	assert(*pphead);

	//一个节点
	if (((*pphead)->next) == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{

		//多个节点
		SLTNode* tail = *pphead;
		//找尾
		while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

3.6 单链表查找

//单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}
	return NULL;
}

3.7 在pos之前插入

//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);

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

		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

3.8 在pos之后插入

//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	SLTNode* newnode = BuyLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

3.9 删除pos位置的值

//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SLPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
	}
}

3.10 删除pos位置之后的值

//删除pos位置之后的值
void SLEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

3.11 链表的释放

//链表的释放
void SLDestroy(SLTNode** pphead)
{
	assert(pphead);

	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	*pphead = NULL;
}

3.12 动态申请一个节点

//动态开辟一个newnode结构体,不free不释放
SLTNode* BuyLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("BuyLTNode");
		return NULL;
	}

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

4. 链表的测试

4.1 尾插测试

//尾插测试
void TestSLish1()
{
	SLTNode* plist = NULL;
	SLPushFront(&plist, 1);
	SLPushFront(&plist, 2);
	SLPushFront(&plist, 3);
	SLPushFront(&plist, 4);

	SLTPrint(plist);
	SLPushBack(&plist, 5);
	SLTPrint(plist);
}

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

运行演示:
在这里插入图片描述

4.2 空链表头插

//空链表头插
void TestSLish2()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLTPrint(plist);
}

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

运行演示:
在这里插入图片描述

4.3 尾删测试

//尾删测试
void TestSLish3()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	
	SLPopBack(&plist);
	SLTPrint(plist);
	SLPopBack(&plist);
	SLTPrint(plist);
	SLPopBack(&plist);
	SLTPrint(plist);
}

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

运行演示:
在这里插入图片描述

4.4 查找修改测试

//查找修改测试
void TestSLish4()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLTPrint(plist);

	SLTNode* pos = STFind(plist, 3);
	if (pos)
	pos->data = 30;
	SLTPrint(plist);
}

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

运行演示:
在这里插入图片描述

4.5 pos前插和后插以及删除pos位置的值

//pos前插和后插,测试
//删除pos位置的值
void TestSLish5()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLTPrint(plist);

	SLTNode* pos = STFind(plist, 3);
	if (pos)
	{
		SLInsert(&plist, pos, 30);
	}
	SLTPrint(plist);

	pos = STFind(plist, 2);
	if (pos)
	{
		SLInsertAfter(pos, 20);
	}
	SLTPrint(plist);

    pos = STFind(plist, 2);
	if (pos)
	{
		SLErase(&plist, pos);
	}
	SLTPrint(plist);

	SLDestroy(&plist);
}

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

运行演示:
在这里插入图片描述

5. 整体代码

5.1 test.c 文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

//尾插测试
void TestSLish1()
{
	SLTNode* plist = NULL;
	SLPushFront(&plist, 1);
	SLPushFront(&plist, 2);
	SLPushFront(&plist, 3);
	SLPushFront(&plist, 4);

	SLTPrint(plist);
	SLPushBack(&plist, 5);
	SLTPrint(plist);
}

//空链表头插
void TestSLish2()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLTPrint(plist);
}

//尾删测试
void TestSLish3()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	
	SLPopBack(&plist);
	SLTPrint(plist);
	SLPopBack(&plist);
	SLTPrint(plist);
	SLPopBack(&plist);
	SLTPrint(plist);
}

//查找修改测试
void TestSLish4()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLTPrint(plist);

	SLTNode* pos = STFind(plist, 3);
	if (pos)
	pos->data = 30;
	SLTPrint(plist);
}

//pos前插和后插,测试
//删除pos位置的值
void TestSLish5()
{
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPushBack(&plist, 5);
	SLTPrint(plist);

	SLTNode* pos = STFind(plist, 3);
	if (pos)
	{
		SLInsert(&plist, pos, 30);
	}
	SLTPrint(plist);

	pos = STFind(plist, 2);
	if (pos)
	{
		SLInsertAfter(pos, 20);
	}
	SLTPrint(plist);

    pos = STFind(plist, 2);
	if (pos)
	{
		SLErase(&plist, pos);
	}
	SLTPrint(plist);

	SLDestroy(&plist);
}



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

5.2 SList.h 文件

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int SLTDataType;
typedef struct SListNode
{
	SLTDataType data;//数据域
	struct SListNode* next;//指针域
}SLTNode;

//打印链表
void SLTPrint(SLTNode* phead);

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

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

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

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

//单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x);

//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);

//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x);

//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);

//删除pos位置之后的值
void SLEraseAfter(SLTNode* pos);

//链表的释放
void SLDestroy(SLTNode** pphead);

5.3 SList.c 文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

//打印链表
void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;//找结构体下一个指针,循环才能走起来
	}
	printf("NULL\n");
}

//动态开辟一个newnode结构体,不free不释放
SLTNode* BuyLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("BuyLTNode");
		return NULL;
	}

	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

//头插
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//链表为空,pphead也不为空,因为它是头指针plist的地址
	//assert(*pphead);//不能断言,链表为空,也需要插入
	SLTNode* newnode = BuyLTNode(x);

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

//尾插
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);//链表为空,pphead也不为空,因为它是头指针plist的地址
	//assert(*pphead);//链表为空,可以插入
	SLTNode* newnode = BuyLTNode(x);
	//空链表
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	//非空链表
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}

		SLTNode* newnode = BuyLTNode(x);
		tail->next = newnode;
	}
	
}

//头删
void SLPopFront(SLTNode** pphead)
{
	assert(pphead);//链表为空,pphead也不为空,因为它是头指针plist的地址
	assert(*pphead);//空链表,不能头删

	一个节点
	//if (((*pphead)->next) == NULL)
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	多个节点
	//else
	//{
	//	SLTNode* head = *pphead;
	//	*pphead = head->next;
	//	free(head);
	//}

	SLTNode* del = *pphead;
	*pphead = del->next;
	free(del);
}

//尾删
void SLPopBack(SLTNode** pphead)
{
	//没有节点(空链表)
	//暴力检查
	assert(*pphead);

	//一个节点
	if (((*pphead)->next) == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{

		//多个节点
		SLTNode* tail = *pphead;
		//找尾
		while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

//单链表查找
SLTNode* STFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}
	return NULL;
}

//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);

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

		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

//在pos之后插入
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	SLTNode* newnode = BuyLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SLPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
	}
}

//删除pos位置之后的值
void SLEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

//链表的释放
void SLDestroy(SLTNode** pphead)
{
	assert(pphead);

	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	*pphead = NULL;
}

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-09 14:32:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-09 14:32:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-06-09 14:32:01       82 阅读
  4. Python语言-面向对象

    2024-06-09 14:32:01       91 阅读

热门阅读

  1. !力扣70. 爬楼梯

    2024-06-09 14:32:01       25 阅读
  2. 微信小程序:实现音乐播放器的功能

    2024-06-09 14:32:01       26 阅读
  3. oracle10g的dataguard测试

    2024-06-09 14:32:01       24 阅读
  4. 电商系统中热库和冷库的使用与数据转换

    2024-06-09 14:32:01       28 阅读
  5. Python R用法:深度探索与实用技巧

    2024-06-09 14:32:01       29 阅读
  6. K-means聚类模型

    2024-06-09 14:32:01       28 阅读
  7. RAGFlow 学习笔记

    2024-06-09 14:32:01       22 阅读
  8. tcpdump 抓包

    2024-06-09 14:32:01       30 阅读
  9. TypeScript看这篇就够了

    2024-06-09 14:32:01       24 阅读
  10. 【分享】使用 Reducer 和 Context 拓展你的应用

    2024-06-09 14:32:01       28 阅读
  11. 力扣2799.统计完全子数组的数目

    2024-06-09 14:32:01       36 阅读