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

 

目录

一、链表的概念

二、单链表的接口实现

1.首先创建一个链表的结构

2.单链表的结点申请

3.单链表的打印 

4.单链表的尾插 

5.单链表的头插

6.单链表的尾删 

7.单链表的头删 

8.单链表的查找 

9.单链表在pos位置前插入一个结点 

10.单链表在pos位置后插入一个结点

11.单链表删除pos位置的结点 

12.单链表删除pos位置后的结点 

13.单链表的销毁 

三、源文件

SList.h 

SList.c 

Test.c 


一、链表的概念

        链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
        链表由两个部分组成:数据域和指针域,数据域用来存放数据,指针域用来链接到下一个数据,具体看下图;

         从图中可以看出,每个数据的下面的指针域里都存储了下一个数据的地址,那么我们通过访问第一个数据的地址,顺藤摸瓜就能访问到其他数据 

二、单链表的接口实现

1.首先创建一个链表的结构

typedef int SLDataType;//这里typedef可以用于存储类型之间的切换

typedef struct SListNode
{
	SLDataType Data;//数据域
	struct SListNode* next;//指针域
}SLNode;

2.单链表的结点申请

//动态申请一个结点
SLNode* BuyListNode(SLDataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->Data = x;       //x的值存放在newnode数据域中
	newnode->next = NULL;    //newnode的指针域置空
	return newnode;
}

3.单链表的打印 

//单链表的打印
void SListPrint(SLNode* phead)
{
	assert(phead);
	SLNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->Data);  //打印链表数据
		cur = cur->next;  //让cur指向下一个数据
	} //当cur为空时,链表打印结束
	printf("NULL\n\n");
}

  

4.单链表的尾插 

//单链表的尾插
void SListPushBack(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyListNode(x);//插入数据需要申请结点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
        //尾插--就去找链表的尾巴
		SLNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
        //找到尾巴,把尾结点的指针域指向插入的新结点的地址
		tail->next = newnode;
	}
}

5.单链表的头插

//单链表的头插
void SListPushFront(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyListNode(x);
	newnode->next = *pphead; //让newnode的指针域指向链表的数据域(就是链表的头)
	*pphead = newnode; //让newnode作新链表的头
}

6.单链表的尾删 

//单链表的尾删
void SListPopBack(SLNode** pphead)
{
	assert(*pphead);
    //如果链表为空
	if (*pphead == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
        //先要找到尾结点(tail),在记录一下尾结点的前一个结点(prev);
		SLNode* tail = *pphead;
		SLNode* prev = NULL;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
        //找到尾结点了,释放它并置空;
		free(tail);
		tail = NULL;
        //此时尾结点的前一个结点(prev)还保存着tail结点的数据的地址,要让它置空;
		prev->next = NULL;
	}
}

7.单链表的头删 

//单链表的头删
void SListPopFront(SLNode** pphead)
{
	assert(*pphead);
	SLNode* next = (*pphead)->next;//记录头结点的下一个结点的地址
	free(*pphead);//释放头结点
	*pphead = next;//头结点的下一个结点作新的头
}

8.单链表的查找 

//单链表的查找
SLNode* SListFind(SLNode* phead, SLDataType x)
{
	assert(phead);
	SLNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->Data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

9.单链表在pos位置前插入一个结点 

//单链表在pos位置前插入一个结点
void SListInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
	assert(pphead);
	assert(pos);
	SLNode* newnode = BuyListNode(x);//申请结点
    //如果只有一个结点
	if (*pphead == pos)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
        //找到pos位置的前一个结点,并记录
		SLNode* posprev = *pphead;
		while (posprev->next != pos)
		{
			posprev = posprev->next;
		}
        //让新结点的指针域指向pos,再让pos的前一个结点的指针域重新指向新结点的数据域地址,就很好的插入到pos的前面了
		newnode->next = pos;
		posprev->next = newnode;
	}
}

10.单链表在pos位置后插入一个结点

//单链表在pos位置后插入一个节点
void SListInsertAfter(SLNode* pos, SLDataType x)
{
	assert(pos);
	SLNode* newnode = BuyListNode(x);//申请结点
	newnode->next = pos->next;//让新结点的指针域指向pos位置的下一个结点的数据域地址
	pos->next = newnode;//pos的指针域指向新结点的数据域地址
}

11.单链表删除pos位置的结点 

//单链表删除pos位置的节点
void SListErase(SLNode** pphead, SLNode* pos)
{
	assert(*pphead);
    //如果链表只有一个结点
	if (*pphead == pos)
	{
		SListPopBack(pphead);
	}
	else
	{
        //找到pos位置的前一个位置(posPrev),并记录
		SLNode* posPrev = *pphead;
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
        //让pos位置的前一个结点posPrev的指针域指向pos结点的指针域,如果pos位置还有结点,即指向了pos位置的下一个结点的数据域的地址;
		posPrev->next = pos->next;
		free(pos);//释放pos
	}
}

12.单链表删除pos位置后的结点 

//单链表删除pos位置后的节点
void SListEraseAfter(SLNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

13.单链表的销毁 

        单链表的销毁并不能和顺序表一样直接释放,顺序表是开辟的连续的空间,链表不是;

要将链表释放需要释放每一个结点;

        可以采用双指针; 

//单链表的销毁
void SListDestroy(SLNode** pphead)
{
	assert(*pphead);
	SLNode* cur = *pphead;
	while (cur != NULL)
	{
		SLNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

三、源文件

SList.h 

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


typedef int SLDataType;

typedef struct SListNode
{
	SLDataType Data;
	struct SListNode* next;
}SLNode;


//动态申请一个节点
SLNode* BuyListNode(SLDataType x);

//单链表的打印
void SListPrint(SLNode* phead);

//单链表的尾插
void SListPushBack(SLNode** pphead, SLDataType x);

//单链表的头插
void SListPushFront(SLNode** pphead, SLDataType x);

//单链表的尾删
void SListPopBack(SLNode** pphead);

//单链表的头删
void SListPopFront(SLNode** pphead);

//单链表的查找
SLNode* SListFind(SLNode* phead, SLDataType x);

//单链表在pos位置前插入一个节点
void SListInsert(SLNode** pphead, SLNode* pos, SLDataType x);

//单链表在pos位置后插入一个节点
void SListInsertAfter(SLNode* pos, SLDataType x);

//单链表删除pos位置的节点
void SListErase(SLNode** pphead, SLNode* pos);

//单链表删除pos位置后的节点
void SListEraseAfter(SLNode* pos);

//单链表的销毁
void SListDestroy(SLNode** pphead);

SList.c 

#include "SList.h"


//动态申请一个结点
SLNode* BuyListNode(SLDataType x)
{
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->Data = x;       //x的值存放在newnode数据域中
	newnode->next = NULL;    //newnode的指针域置空
	return newnode;
}

//单链表的打印
void SListPrint(SLNode* phead)
{
	assert(phead);
	SLNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->Data);  //打印链表数据
		cur = cur->next;  //让cur指向下一个数据
	} //当cur为空时,链表打印结束
	printf("NULL\n\n");
}

//单链表的尾插
void SListPushBack(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyListNode(x);//插入数据需要申请结点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//尾插--就去找链表的尾巴
		SLNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		//找到尾巴,把尾结点的指针域指向插入的新结点的地址
		tail->next = newnode;
	}
}

//单链表的头插
void SListPushFront(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyListNode(x);
	newnode->next = *pphead; //让newnode的指针域指向链表的数据域(就是链表的头)
	*pphead = newnode; //让newnode作新链表的头
}

//单链表的尾删
void SListPopBack(SLNode** pphead)
{
	assert(*pphead);
	//如果链表为空
	if (*pphead == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//先要找到尾结点(tail),在记录一下尾结点的前一个结点(prev);
		SLNode* tail = *pphead;
		SLNode* prev = NULL;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		//找到尾结点了,释放它并置空;
		free(tail);
		tail = NULL;
		//此时尾结点的前一个结点(prev)还保存着tail结点的数据的地址,要让它置空;
		prev->next = NULL;
	}
}

//单链表的头删
void SListPopFront(SLNode** pphead)
{
	assert(*pphead);
	SLNode* next = (*pphead)->next;//记录头结点的下一个结点的地址
	free(*pphead);//释放头结点
	*pphead = next;//头结点的下一个结点作新的头
}

//单链表的查找
SLNode* SListFind(SLNode* phead, SLDataType x)
{
	assert(phead);
	SLNode* cur = phead;
	while (cur != NULL)
	{
		if (cur->Data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

//单链表在pos位置前插入一个结点
void SListInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
	assert(pphead);
	assert(pos);
	SLNode* newnode = BuyListNode(x);//申请结点
	//如果只有一个结点
	if (*pphead == pos)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		//找到pos位置的前一个结点,并记录
		SLNode* posprev = *pphead;
		while (posprev->next != pos)
		{
			posprev = posprev->next;
		}
		//让新结点的指针域指向pos,再让pos的前一个结点的指针域重新指向新结点的数据域地址,就很好的插入到pos的前面了
		newnode->next = pos;
		posprev->next = newnode;
	}
}

//单链表在pos位置后插入一个节点
void SListInsertAfter(SLNode* pos, SLDataType x)
{
	assert(pos);
	SLNode* newnode = BuyListNode(x);//申请结点
	newnode->next = pos->next;//让新结点的指针域指向pos位置的下一个结点的数据域地址
	pos->next = newnode;//pos的指针域指向新结点的数据域地址
}

//单链表删除pos位置的节点
void SListErase(SLNode** pphead, SLNode* pos)
{
	assert(*pphead);
	//如果链表只有一个结点
	if (*pphead == pos)
	{
		SListPopBack(pphead);
	}
	else
	{
		//找到pos位置的前一个位置(posPrev),并记录
		SLNode* posPrev = *pphead;
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
		//让pos位置的前一个结点posPrev的指针域指向pos结点的指针域,如果pos位置还有结点,即指向了pos位置的下一个结点的数据域的地址;
		posPrev->next = pos->next;
		free(pos);//释放pos
	}
}

//单链表删除pos位置后的节点
void SListEraseAfter(SLNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

//单链表的销毁
void SListDestroy(SLNode** pphead)
{
	assert(*pphead);
	SLNode* cur = *pphead;
	while (cur != NULL)
	{
		SLNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

Test.c 

#include "SList.h"


void TestList1()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);
	SListPrint(plist);
	//SListPushFront(&plist, 1);
	//SListPushFront(&plist, 2);
	//SListPushFront(&plist, 3);
	//SListPushFront(&plist, 4);
	//SListPushFront(&plist, 5);
	//SListPrint(plist);
}

void TestList2()
{
	SLTNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);
	SListPopFront(&plist);
	SListPopFront(&plist);

	SListPrint(plist);
}

void TestList3()
{
	SLTNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 5);
	SListPrint(plist);

	SLTNode* pos = SListFind(plist, 2);
	int i = 1;
	while (pos)
	{
		printf("%d个pos节点:%p->%d\n", i++, pos, pos->data);
		pos = SListFind(pos->next, 2);
	}

	pos = SListFind(plist, 3);
	if (pos)
	{
		pos->data = 30;
	}
	SListPrint(plist);
}

void TestList4()
{
	SLTNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);

	SListPrint(plist);

	//SLTNode* pos = SListFind(plist, 5);

	//if (pos)
	//{
	//	SListInsert(&plist, pos, 30);
	//}
	//SListPrint(plist);
}

void TestList5()
{
	SLTNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);

	SListPrint(plist);

	/*
	SLTNode* pos = SListFind(plist, 5);
	SListInsertAfter(pos, 20);
	SListPrint(plist);
	*/

	/*
	SLTNode* pos = SListFind(plist, 3);
	SListEase(&plist, pos);
	SListPrint(plist);
	*/
	SLTNode* pos = SListFind(plist, 5);
	SListEaseAfter(pos);
	SListPrint(plist);
	SListDestory(&plist);
}
int main()
{
	TestList1();
	//TestList2();
	//TestList3();
	//TestList4();
	//TestList5();
	return 0;
}

下一期更新顺序表和链表相关的LeetCode题

  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霄沫凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值