数据结构系列(单向链表)

本文介绍了单向链表的概念,对比了它相对于顺序表的优点,如空间利用率高和插入删除操作高效。并提供了C语言的具体代码实现,包括头插、尾插、头删、尾删、遍历打印、查找和插入、删除功能。
摘要由CSDN通过智能技术生成

什么是单向链表?

链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。
而单向链表的指针只指向下一个节点,如图
在这里插入图片描述

单向链表相对于顺序表的优点

笔者写过一篇关于顺序表的文章说过顺序表的缺点
不知道的读者可以点击此处查看
相对于顺序表有以下优点

  • 不会造成空间浪费
  • 可以在节点删除插入,不需要挪动数据,只要更改指针

具体代码实现

单向链表功能

  • 头插
  • 头删
  • 在指针为pos的位置前插入
  • 尾插
  • 尾删
  • 删除指针为pos的位置
  • 遍历打印链表
  • 查找

具体代码

创建一个节点

SLN* buy_SLN()
{
	SLN* tmp = (SLN*)malloc(sizeof(SLN));
	if (tmp == NULL)
	{
		perror("malloc fail");
	}
	tmp->next = NULL;
	return tmp;
}

头插

void pushfront(SLN** s1, NodeData x)
{
	assert(s1);
	SLN* tmp = buy_SLN();//获取空间
	tmp->a = x;
	tmp->next = *s1;
	*s1 = tmp;
}

如图
在这里插入图片描述

尾插

void pushback(SLN** s1, NodeData x)
{
	assert(s1);
	SLN* tmp = buy_SLN();
	if (*s1 == NULL)//空链表情况
	{
		tmp->a = x;
		tmp->next = NULL;
		*s1 = tmp;
	}
	else//非空链表情况
	{
		SLN* cut = *s1;
		while (cut->next != NULL)
		{
			cut = cut->next;
		}
		tmp->a = x;
		tmp->next = NULL;
		cut->next = tmp;
	}
}

分为两种情况,空链表情况单独处理
如图
在这里插入图片描述

头删

void popfront(SLN** s1)
{
	assert(s1);
	assert(*s1);
	SLN* tmp = *s1;
	*s1 = tmp->next;
	free(tmp);
	tmp = NULL;
}

在这里插入图片描述
注意断言!此处对*s1断言,保证链表不为空,有东西可删,下面的尾删同理

尾删

void popback(SLN** s1)
{
	assert(*s1);
	SLN* tmp = *s1;
	if ((*s1)->next == NULL)
	{
		free(*s1);
		*s1 = NULL;
	}
	else
	{
		while (tmp->next->next != NULL)
		{
			tmp = tmp->next;
		}
		free(tmp->next);
		tmp->next = NULL;
	}
}

分两种情况,链表只有一个数据,和有两个及以上
只有一个数据,直接将数据释放,将链表置空
在这里插入图片描述

遍历打印链表

void SLNprinf(SLN* s1)
{
	assert(s1);
	SLN* p = s1;
	while (p)//最后一个为空
	{
		printf("%d->", p->a);
		p = p->next;
	}
	printf("\n");
}

查找某数据

SLN* STFind(SLN* s1, NodeData x)
{
	SLN* tmp = s1;
	while (tmp)
	{
		if (tmp->a == x)
		{
			return tmp;
		}
		tmp = tmp->next;//去下一个节点
	}
	return NULL;
}

这里返找到数据的地址

在指针为pos的位置前插入

void SLInsert(SLN** s1, SLN* pos, NodeData x)
{
	assert(s1);
	assert(pos);
	SLN* cur = *s1;
	SLN* tmp = buy_SLN();
	tmp->a = x;
	if (*s1==pos)
	{
		pushfront(s1, x);
	}
	else
	{
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		tmp->next = cur->next;
		cur->next = tmp;
	}
}

如果插入位置是链表的第一个位置则直接调用头插函数
其他位置,如图
在这里插入图片描述

删除指针为pos的位置

void SLErase(SLN** s1, SLN* pos)
{
	assert(s1);
	assert(pos);
	SLN* tmp = *s1;
	if (*s1 == pos)
	{
		popfront(s1);
	}
	else
	{
		while (tmp->next!=pos)
		{
			tmp = tmp->next;
		}
		tmp->next = pos->next;
		free(pos);
	}
}

如果只有一个数据则调用头删
其他情况如图
在这里插入图片描述
以下是完整代码以及测试

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int  NodeData;
typedef struct SListNode
{
	NodeData a;
	struct SListNode* next;
}SLN;

SLN* buy_SLN();
void pushback(SLN** s1, NodeData x);//尾插
void pushfront(SLN** s1, NodeData x);//头插
void SLNprinf(SLN* s1);//打印
void popfront(SLN** s1);//头删
void popback(SLN** s1);//尾删
SLN* STFind(SLN* s1, NodeData x);//查找
void SLInsert(SLN**s1, SLN* pos, NodeData x);//在地址为pos前位置添加
void SLInsertAfte(SLN* pos, NodeData x);//在地址为pos后的位置添加
void SLErase(SLN** s1, SLN* pos);//删除pos位置
void SLEraseAfter(SLN* pos);//删除pos后的位置
#include"lianbiao.h"
SLN* buy_SLN()
{
	SLN* tmp = (SLN*)malloc(sizeof(SLN));
	if (tmp == NULL)
	{
		perror("malloc fail");
	}
	tmp->next = NULL;
	return tmp;
}
void pushback(SLN** s1, NodeData x)
{
	assert(s1);
	SLN* tmp = buy_SLN();
	if (*s1 == NULL)//空链表情况
	{
		tmp->a = x;
		tmp->next = NULL;
		*s1 = tmp;
	}
	else//非空链表情况
	{
		SLN* cut = *s1;
		while (cut->next != NULL)
		{
			cut = cut->next;
		}
		tmp->a = x;
		tmp->next = NULL;
		cut->next = tmp;
	}
}
void pushfront(SLN** s1, NodeData x)
{
	assert(s1);
	SLN* tmp = buy_SLN();//获取空间
	tmp->a = x;
	tmp->next = *s1;
	*s1 = tmp;
}

void SLNprinf(SLN* s1)
{
	assert(s1);
	SLN* p = s1;
	while (p)
	{
		printf("%d->", p->a);
		p = p->next;
	}
	printf("\n");
}

void popfront(SLN** s1)
{
	assert(s1);
	assert(*s1);
	SLN* tmp = *s1;
	*s1 = tmp->next;
	free(tmp);
	tmp = NULL;
}
void popback(SLN** s1)
{
	assert(*s1);
	SLN* tmp = *s1;
	if ((*s1)->next == NULL)
	{
		free(*s1);
		*s1 = NULL;
	}
	else
	{
		while (tmp->next->next != NULL)
		{
			tmp = tmp->next;
		}
		free(tmp->next);
		tmp->next = NULL;
	}
}

SLN* STFind(SLN* s1, NodeData x)
{
	SLN* tmp = s1;
	while (tmp)
	{
		if (tmp->a == x)
		{
			return tmp;
		}
		tmp = tmp->next;
	}
	return NULL;
}

void SLInsert(SLN** s1, SLN* pos, NodeData x)
{
	assert(s1);
	assert(pos);
	SLN* cur = *s1;
	SLN* tmp = buy_SLN();
	tmp->a = x;
	if (*s1==pos)
	{
		pushfront(s1, x);
	}
	else
	{
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		tmp->next = cur->next;
		cur->next = tmp;
	}
}

void SLInsertAfte(SLN* pos, NodeData x)
{
	assert(pos);
	SLN* cur=pos;
	SLN* tmp = buy_SLN();
	tmp->a = x;
	tmp->next = cur->next;
	cur->next = tmp;
}

void SLErase(SLN** s1, SLN* pos)
{
	assert(s1);
	assert(pos);
	SLN* tmp = *s1;
	if (*s1 == pos)
	{
		popfront(s1);
	}
	else
	{
		while (tmp->next!=pos)
		{
			tmp = tmp->next;
		}
		tmp->next = pos->next;
		free(pos);
	}
}
void SLEraseAfter(SLN* pos)
{
    assert(pos);
	assert(pos->next);
	SLN* tmp = pos->next;
	pos->next = tmp->next;
	free(tmp);
}
#include"lianbiao.h"

int main()
{
	SLN* phead = NULL;
	pushback(&phead, 7);
	pushback(&phead, 1);
	pushback(&phead, 1);
	SLNprinf(phead);
	pushfront(&phead, 5);
	pushfront(&phead, 2);
	pushfront(&phead, 2);
	SLNprinf(phead);
	popfront(&phead);
	popback(&phead);
	popback(&phead);
	SLNprinf(phead);
    SLN*tmp=STFind(phead,5);
	printf("%d\n", tmp->a);
	SLInsert(&phead, tmp, 9);
	printf("%d\n", tmp->a);
	SLNprinf(phead);
	SLInsertAfte(tmp, 8);
	SLNprinf(phead);
	SLErase(&phead, tmp);
	SLNprinf(phead);
	SLN* tm = STFind(phead, 9);
	SLEraseAfter(tm);
	SLNprinf(phead);
}

在这里插入图片描述
单向链表的分享到这里就结束嘞!
希望大家多多三连,若有错误之处,还请不吝赐教!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值