单链表C语言实现 (不带头 带头两个版本)

链表就是许多节点在逻辑上串起来的数据存储方式 是通过结构体中的指针将后续的节点串联起来

typedef int SLTDataType;//数据类型
typedef struct SListNode//节点
{
	SLTDataType data;//存储的数据
	struct SListNode* next;//指向下一个节点地址的指针
}SLTNode;//结构体类型的简化

上面的代码只是声明 并没有定义 也就是没有创建结构体变量 

SListNode* pc = NULL;//定义结构体

下面开始实现链表的各个接口 让链表可以存储和删除数据

链表的打印

void SListPrint(SListNode* phead)
{
	while (phead != NULL)
	{
		printf("%d->", phead->val);
		phead = phead->next;
	}
	printf("NULL");
}

链表的插入会向内存申请空间 去创造一个新的节点 所以后续的 头部插入 尾部插入 还是任意位置的插入 都会进行相同的操作 所以这里将开辟节点这个过程单独分装为一个函数 方便后面的操作

static SListNode* CreatNode(DataType x)
{
	SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));
	if (tmp == NULL)
	{
		printf("failed to allocate memory.");
		exit(-1);
	}
	tmp->val = x;
	tmp->next = NULL;
	return tmp;
}

这里使用了static将函数的作用域限制在该函数所在的项目中 在其它项目中无法访问

链表的头部插入

void SListFront(SListNode** phead, DataType x)
{
	SListNode* tmp = CreatNode(x);
	tmp->next = *phead;
	tmp->val = x;
	*phead = tmp;
}

链表的尾部插入

链表的尾部插入相比于头部插入就显得不是那么好写

因为你要考虑 链表中是否有数据 也就是*phead是否为空  并且还要考虑当链表不为空时 尾插该怎样插入  我们可以使用if语句进行判断

void SListBackPush(SListNode** phead, DataType x)
{
	SListNode* newnode = CreatNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		SListNode* tail = *phead;//定义一个变量去向后寻找尾部
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

任意位置的插入(从1位置向后插 超出范围 将该节点插入到尾部)

当链表有数据时 尾部插入和头部插入是一样的流程 用一个临时变量tmp 向后寻找pos位置 再将该位置的下一个节点的地址给 要插入节点的指针 最后将要插入节点的地址给前一个节点的指针 这样就将该节点插进去了 

但是头部插入不太一样 因为头部之前没有节点了 就只能将头节点先给要插入的节点 再将要插入的节点作为头部 这样就完成了头部插入

void SListPosPush(SListNode** phead, DataType x , int pos)
{
	SListNode* newnode = CreatNode(x);
	if (pos == 1)
	{
		newnode->next = *phead;
		*phead = newnode;
	}
	else
	{
		SListNode* tmp = *phead;
		//如果所给的pos位置超出了链表的长度就将其插入到尾部
		while (tmp->next!=NULL && pos>2)
		{
			tmp = tmp->next;
			--pos;
		}
		newnode->next = tmp->next;
		tmp->next = newnode;
	}
}

运行结果如下 以上就是插入接口函数的实现

 删除的相关节点

头删

void SListPopFront(SListNode** phead)
{
	assert(*phead);
	SListNode* tmp = *phead;
	*phead = (*phead)->next;
	free(tmp);
	tmp = NULL;
}

尾删

尾删和头删不一样 因为当链表只有一个节点时 尾删就变成了头删 这样就和有多个数据时 尾部删除不一样 这就分情况 讨论

void SListPopBack(SListNode** phead)
{
	assert(*phead);
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		//找尾
		SListNode* tmp = *phead;//防止头节点丢失
		SListNode* str = *phead;
		while (tmp->next != NULL)
		{
			str = tmp;
			tmp = tmp->next;
		}
		free(tmp);
		tmp = NULL;
		str->next = NULL;
	}
}

上面是两个指针配合删除 下面是一个指针去完成

void SListPopBack(SListNode** phead)
{
	assert(*phead);
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		//找尾
		SListNode* tmp = *phead;//防止头节点丢失
		SListNode* str = *phead;
		while (tmp->next->next!=NULL)
		{
			tmp = tmp->next;
		}
		free(tmp->next);
		tmp->next = NULL;
	}
}

链表pos位置删除

void SListPopPos(SListNode** phead, int pos)
{
	assert(*phead);//空链表就不用删除了
	if (pos == 1)
	{
		SListNode* tmp = *phead;
		*phead = (*phead)->next;
		free(tmp);
		tmp = NULL;
	}
	else
	{
		SListNode* tmp = *phead;
		SListNode* str = tmp;
		while (pos > 1 && tmp->next != NULL)
		{
			str = tmp;
			tmp = tmp->next;
			--pos;
		}
		//找到pos位置之后将 pos的下一个节点的地址给pos前面的节点的指针
		//再将pos所在的节点进行释放
		str->next = tmp->next;
		free(tmp);
		tmp = NULL;
	}
}

对链表的查找 修改 其实就是将链表遍历一遍 上面的寻找尾部 打印链表就是遍历链表 所以大家可以将其修改修改 

对单链表就介绍到这里 后续还有双向链表

带头结点的单链表  没动态开辟头节点 只是定义了一个 结构体变量 用来存储后续插入的节点 用一级指针去传参 你也可以动态开辟头节点 传二级指针 这里只是说 带头结点的很好实现链表的增删查改

头文件  函数各个接口

#pragma once

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

typedef int DataType;
typedef struct SList
{
	DataType val;
	struct SList* next;
}SListNode;


//单链表打印
void SListPrint(SListNode* pc);

//单链表的尾部插入
void SListPushBack(SListNode* pc, DataType x);

//链表的头部插入
void SListPushFront(SListNode* pc, DataType x);

//单链表头删
void SListPopFront(SListNode* pc);

//单链表尾删
void SListPopBack(SListNode* pc);


//删除pos位置
void SListPopPos(SListNode* pc, int pos);

//pos之后插入
void SListPushPos(SListNode* pc, int pos, DataType x);


//链表的查找
void SListFind(SListNode* pc, DataType x);


//链表的销毁
void SListDestroy(SListNode* pc);

函数实现 接口实现

#define _CRT_SECURE_NO_WARNINGS 1


#include"SList.h"

static SListNode* CreatNode(DataType x)
{
	SListNode* tmp = (SListNode*)malloc(sizeof(SListNode));
	if (tmp == NULL)
	{
		printf("Failed to allocate SListNode.");
		exit(-1);
	}
	tmp->next = NULL;
	tmp->val = x;
	return tmp;
}

void SListPrint(SListNode* pc)
{
	SListNode* tmp = pc->next;
	while (tmp)
	{
		printf("%d->", tmp->val);
		tmp = tmp->next;
	}
	printf("NULL\n");
}

void SListPushBack(SListNode* pc, DataType x)
{
	while (pc->next != NULL)
	{
		pc = pc->next;
	}
	pc->next = CreatNode(x);
}

void SListPushFront(SListNode* pc, DataType x)
{

	SListNode* tmp = pc->next;
	pc->next = CreatNode(x);
	pc->next->next = tmp;
}

void SListPopFront(SListNode* pc)
{
	SListNode* tmp = pc->next;
	pc->next = pc->next->next;
	free(tmp);
	tmp = NULL;
}


void SListPopBack(SListNode* pc)
{
	SListNode* tmp = pc,*cur = pc;
	while (cur->next != NULL)
	{
		tmp = cur;
		cur = cur->next;
	}
	if (cur != pc)//不释放哨兵卫
	{
		free(cur);
		cur = NULL;
	}
	tmp->next = NULL;
}


void SListPopPos(SListNode* pc, int pos)
{
	SListNode* tmp = pc->next,*str = pc;
	while (tmp->next != NULL&&pos>1)
	{
		str = tmp;
		tmp = tmp->next;
		--pos;
	}
	if (pos != 1)
	{
		printf("pos位置超出链表范围.");
		return;
	}
	str->next = tmp->next;
	free(tmp);
	tmp = NULL;
}

void SListPushPos(SListNode* pc, int pos, DataType x)
{
	SListNode* newnode = CreatNode(x);
	SListNode* tmp = pc;
	while (tmp->next != NULL&&pos>1)
	{
		tmp = tmp->next;
		--pos;
	}
	if (pos != 1)
	{
		printf("要插入的位置超出了链表的长度.\n");
		return;
	}
	newnode->next = tmp->next;
	tmp->next = newnode;
}


void SListFind(SListNode* pc, DataType x)
{
	SListNode* tmp = pc->next;
	int pos = 0;
	while (tmp)
	{
		pos++;
		if (tmp->val == x)
		{
			printf("找到了该元素在第%d个\n", pos);
			return;
		}
		tmp = tmp->next;
	}
	printf("没有找到.\n");
}

void SListDestroy(SListNode* pc)
{
	//所有申请的空间都要释放
	SListNode* tmp = pc->next;
	pc = pc->next->next;
	while (pc)
	{
		free(tmp);
		tmp = NULL;
		tmp = pc;
		pc = pc->next;
	}
}

测试文件

#define _CRT_SECURE_NO_WARNINGS 1


#include"SList.h"

void test1()
{
	//不是哨兵卫 就是一个结构体变量
	SListNode pc;
	pc.next = NULL;


	SListPushBack(&pc, 1);
	SListPushBack(&pc, 2);
	SListPushBack(&pc, 3);

	SListPushFront(&pc, -1);
	SListPushFront(&pc, -2);

	SListPopFront(&pc);
	SListPopBack(&pc);

	SListPushPos(&pc, 1, 5);
	SListPushPos(&pc, 5, 6);

	SListPopPos(&pc, 1);


	SListFind(&pc, 6);

	SListPrint(&pc);
	SListDestroy(&pc);
}

int main()
{
	test1();

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

菜鸡爱玩

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

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

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

打赏作者

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

抵扣说明:

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

余额充值