纵横数据结构与算法之单链表[C/C++]实现

纵横数据结构与算法

在这里插入图片描述

欢迎订阅本专栏,说在前面

今天给大家带来单链表数据结构的讲解!

博主为了本系列专栏,做了很多准备,争取图文并茂,让大家看明白!希望大家不要吝啬订阅,与关注,多多评论哦!!

一、前言

那么这里博主先安利一下其他一些干货满满的专栏啦!

玩转Linux操作系统,点击下方蓝字即可跳转:
欢迎订阅,玩转Linux+系统编程+网络编程

CCF相关真题,点击下方蓝字即可跳转:
CCF真题

刷题专栏,点击下方蓝字跳转:
刷题专栏

二 、正文

2.1 与顺序表的比较

关于顺序表问题:

  1. 中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:如何解决以上问题呢?下面给出了链表的结构来看看。

2.2 单链表

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
它由一系列节点组成,每个节点包含两个属性:数据域和指针域。单链表的每个节点都可以向前或向后移动,但不能向两端移动。

单链表的基本操作包括插入、删除、查找、遍历等。插入操作将新节点插入到单链表的头部或尾部,删除操作将指定节点从单链表中删除,查找操作查找指定节点在单链表中的位置,遍历操作按照指定节点的顺序访问单链表中的所有节点。

单链表的优点是结构简单、插入、删除、查找等操作的时间复杂度均为O(1)(我也会给出O(N)复杂度的向前插入算法),适合用于需要频繁插入、删除、查找等操作的场合。然而,单链表的空间复杂度较高,需要占用一定的存储空间,因此在一些场景下,可能需要使用其他数据结构来代替单链表。

在实际应用中,单链表通常与队列、栈等数据结构结合使用,以实现更复杂的功能。

2.3 相关代码实现与详解图

1. 结点描述与接口

typedef int SLDatatype;

typedef struct SListNode
{
	SLDatatype data;

	struct SListNode* next;
	
}SLTNode;

//打印
void SLPrint(SLTNode* phead);

//创建结点

SLTNode* CreateNode(SLDatatype x);

//头插尾插

void Push_Back_SLT(SLTNode** pphead,SLDatatype x);
void Push_Front_SLT(SLTNode** pphead,SLDatatype x);

//头删尾删

void Pop_Back_STL(SLTNode** pphead);
void Pop_Front_STL(SLTNode** pphead);

//查找

SLTNode* SLTFind(SLTNode* phead, SLDatatype x);

//O(N) 算法下的 向前插入 与 原地删除

void SLTInsert_forward(SLTNode** pphead, SLTNode* pos, SLDatatype x);
void SLTErase(SLTNode** pphead, SLTNode* pos);

//O(1)算法下的 向后插入 与 向后删除

void STLInsert_back(SLTNode** pphead, SLTNode* pos, SLDatatype x);
void STLErase_back(SLTNode** pphead, SLTNode* pos);

还是熟悉的增删查改,并且沿用了上一篇文章的相关技巧,代码通用性和命名风格!

2. 相关接口实现与配图

(1) 创建节点

根据以往经验,我们先写出打印函数与结点创建函数,方便复用


void SLPrint(SLTNode* phead)//这里传一级指针即可,不涉及对于头指针的修改
{

	if (phead == NULL)//当是空结点的时候
	{
		printf("This is NULL !");
	}
	else {

		while (phead->next != NULL)
		{
			printf("%d -> ", phead->data);
			phead = phead->next;
		}
		printf("%d -> NULL ",phead->data);
		//printf("-> NULL");

	}

}

SLTNode* CreateNode(SLDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);

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

	return newnode;

}
(2) 尾插与头插与配图

请添加图片描述
请添加图片描述

//这里传递二级指针,如果是空结点将涉及到在函数内对于phead的修改
void Push_Back_SLT(SLTNode** pphead, SLDatatype x)
{
	assert(pphead);
	
	if (*pphead == NULL)//空结点
	{
		*pphead = CreateNode(x);
	}
	else
	{
		SLTNode* cur = *pphead;

		while (cur->next != NULL)
		{
			cur = cur->next;
		}

		cur->next = CreateNode(x);

	}

}


void Push_Front_SLT(SLTNode** pphead, SLDatatype x)
{

	assert(pphead);

	if (*pphead == NULL)//空结点
	{
		*pphead = CreateNode(x);
	}
	else
	{
		SLTNode* curFront = CreateNode(x);

		curFront->next = *pphead;
		
		*pphead = curFront;

	}

}


(3) 尾删头删与配图

请添加图片描述

请添加图片描述

void Pop_Back_STL(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead); //空的不能删


	//一个结点
	if ((*pphead) ->next == NULL) //注意两次解引用* -> 带括号区分优先级
	{

		free(*pphead);
		*pphead = NULL;
	}
	else
	{

		SLTNode* prev = *pphead;

		while (prev->next->next != NULL)
		{
			prev = prev->next;
		}

		free(prev->next);

		prev->next = NULL;

	}
	

}

void Pop_Front_STL(SLTNode** pphead)
{
	assert(pphead);

	assert(*pphead);//判空

	

	//一个结点
	if ((*pphead)->next == NULL) //注意两次解引用* -> 带括号区分优先级
	{

		free(*pphead);
		*pphead = NULL;
	}
	else
	{

		SLTNode* next = (*pphead)->next;

		free(*pphead);

		*pphead = next;

	}

}
(4) 查找

请添加图片描述

SLTNode*  SLTFind(SLTNode* phead, SLDatatype x)
{
	assert(phead);

	
	SLTNode* cur = phead;

	while (cur)
	{
		

		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;

	}


	return NULL;
	
}
(5)O(N)复杂度下的向前插与原地删除

请添加图片描述
请添加图片描述

void SLTInsert_forward(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
	assert(pphead);
	assert(pos);

	if ( (*pphead)->next == NULL)

	{  

		Push_Front_SLT(pphead,x);

	}
	else {

		SLTNode* cur = *pphead;
		SLTNode* prev = *pphead;

		while (cur != pos)
		{
			prev = cur;

			cur = cur->next;
		}

		SLTNode* newnode = CreateNode(x);

		prev->next = newnode;

		newnode->next = pos;

	}


}

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if ((*pphead)->next == NULL)

	{

		Pop_Front_STL(pphead);

	}
	else {

		SLTNode* cur = *pphead;
		SLTNode* prev = *pphead;

		while (cur != pos)
		{
			prev = cur;

			cur = cur->next;
		}

		prev->next = cur->next;
		free(cur);
		cur = NULL;
	}



}
(6)O(1)复杂度下的向后插入与向后删除

请添加图片描述

请添加图片描述

void STLInsert_back(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		Push_Front_SLT(pphead,x);
	}
	else
	{
		SLTNode* newnode = CreateNode(x);
		SLTNode* next = pos->next;

		pos->next = newnode;
		newnode->next = next;

	}
}


void STLErase_back(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		Pop_Back_STL(pphead);
	}
	else if (pos->next == NULL)
	{
		return;
	}
	else
	{
		SLTNode* posnext = pos->next->next;

		free(pos->next);

		pos->next = posnext;
	}
		


}

3. 完整代码

.h
#pragma once

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


typedef int SLDatatype;

typedef struct SListNode
{
	SLDatatype data;

	struct SListNode* next;
	
}SLTNode;


//打印
void SLPrint(SLTNode* phead);

//创建结点

SLTNode* CreateNode(SLDatatype x);

//头插尾插

void Push_Back_SLT(SLTNode** pphead,SLDatatype x);
void Push_Front_SLT(SLTNode** pphead,SLDatatype x);

//头删尾删

void Pop_Back_STL(SLTNode** pphead);
void Pop_Front_STL(SLTNode** pphead);

//查找

SLTNode* SLTFind(SLTNode* phead, SLDatatype x);

//O(N) 算法下的 向前插入 与 原地删除

void SLTInsert_forward(SLTNode** pphead, SLTNode* pos, SLDatatype x);
void SLTErase(SLTNode** pphead, SLTNode* pos);

//O(1)算法下的 向后插入 与 向后删除

void STLInsert_back(SLTNode** pphead, SLTNode* pos, SLDatatype x);
void STLErase_back(SLTNode** pphead, SLTNode* pos);

.cpp
#include "SList.h"


void SLPrint(SLTNode* phead)//这里传一级指针即可,不涉及对于头指针的修改
{

	if (phead == NULL)//当是空结点的时候
	{
		printf("This is NULL !");
	}
	else {

		while (phead->next != NULL)
		{
			printf("%d -> ", phead->data);
			phead = phead->next;
		}
		printf("%d -> NULL ",phead->data);
		//printf("-> NULL");

	}

}

SLTNode* CreateNode(SLDatatype x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	assert(newnode);

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

	return newnode;

}
//这里传递二级指针,如果是空结点将涉及到在函数内对于phead的修改
void Push_Back_SLT(SLTNode** pphead, SLDatatype x)
{
	assert(pphead);
	
	if (*pphead == NULL)//空结点
	{
		*pphead = CreateNode(x);
	}
	else
	{
		SLTNode* cur = *pphead;

		while (cur->next != NULL)
		{
			cur = cur->next;
		}

		cur->next = CreateNode(x);

	}

}


void Push_Front_SLT(SLTNode** pphead, SLDatatype x)
{

	assert(pphead);

	if (*pphead == NULL)//空结点
	{
		*pphead = CreateNode(x);
	}
	else
	{
		SLTNode* curFront = CreateNode(x);

		curFront->next = *pphead;
		
		*pphead = curFront;

	}

}


void Pop_Back_STL(SLTNode** pphead)
{
	assert(pphead);
	assert(*pphead); //空的不能删


	//一个结点
	if ((*pphead) ->next == NULL) //注意两次解引用* -> 带括号区分优先级
	{

		free(*pphead);
		*pphead = NULL;
	}
	else
	{

		SLTNode* prev = *pphead;

		while (prev->next->next != NULL)
		{
			prev = prev->next;
		}

		free(prev->next);

		prev->next = NULL;

	}
	

}

void Pop_Front_STL(SLTNode** pphead)
{
	assert(pphead);

	assert(*pphead);//判空

	

	//一个结点
	if ((*pphead)->next == NULL) //注意两次解引用* -> 带括号区分优先级
	{

		free(*pphead);
		*pphead = NULL;
	}
	else
	{

		SLTNode* next = (*pphead)->next;

		free(*pphead);

		*pphead = next;

	}

}


SLTNode*  SLTFind(SLTNode* phead, SLDatatype x)
{
	assert(phead);

	
	SLTNode* cur = phead;

	while (cur)
	{
		

		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;

	}


	return NULL;
	
}

void SLTInsert_forward(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
	assert(pphead);
	assert(pos);

	if ( (*pphead)->next == NULL)

	{  

		Push_Front_SLT(pphead,x);

	}
	else {

		SLTNode* cur = *pphead;
		SLTNode* prev = *pphead;

		while (cur != pos)
		{
			prev = cur;

			cur = cur->next;
		}

		SLTNode* newnode = CreateNode(x);

		prev->next = newnode;

		newnode->next = pos;

	}


}

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if ((*pphead)->next == NULL)

	{

		Pop_Front_STL(pphead);

	}
	else {

		SLTNode* cur = *pphead;
		SLTNode* prev = *pphead;

		while (cur != pos)
		{
			prev = cur;

			cur = cur->next;
		}

		prev->next = cur->next;
		free(cur);
		cur = NULL;
	}



}

void STLInsert_back(SLTNode** pphead, SLTNode* pos, SLDatatype x)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		Push_Front_SLT(pphead,x);
	}
	else
	{
		SLTNode* newnode = CreateNode(x);
		SLTNode* next = pos->next;

		pos->next = newnode;
		newnode->next = next;

	}
}


void STLErase_back(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		Pop_Back_STL(pphead);
	}
	else if (pos->next == NULL)
	{
		return;
	}
	else
	{
		SLTNode* posnext = pos->next->next;

		free(pos->next);

		pos->next = posnext;
	}
		


}

4. 测试用例

test.cpp
#include "SList.h"


void test1()//测试SLprint
{
	SLTNode* plist1= (SLTNode*)malloc(sizeof(SLTNode));
	assert(plist1);
	SLTNode* plist2= (SLTNode*)malloc(sizeof(SLTNode));
	assert(plist2);
	SLTNode* plist3= (SLTNode*)malloc(sizeof(SLTNode));
	assert(plist3);
	SLTNode* plist4= (SLTNode*)malloc(sizeof(SLTNode));
	assert(plist4);

	plist1->data = 1;
	plist2->data = 2;
	plist3->data = 3;
	plist4->data = 4;

	plist1->next = plist2;
	plist2->next = plist3;
	plist3->next = plist4;
	plist4->next = NULL;

	
	SLPrint(plist1);

}

void test2()
{

	/*SLTNode* plist = (SLTNode*)malloc(sizeof(SLTNode));
	assert(plist);
	plist->data = 0;
	plist->next = NULL;//测试正常情况多结点下的尾插
	*/

	SLTNode* plist = NULL;

	Push_Back_SLT(&plist, 1);
	Push_Back_SLT(&plist, 2);
	Push_Back_SLT(&plist, 3);
	Push_Back_SLT(&plist, 4);

	SLPrint(plist);
}

void test3()
{

	/*SLTNode* plist = (SLTNode*)malloc(sizeof(SLTNode));
	assert(plist);
	plist->data = 0;
	plist->next = NULL;//测试正常情况多结点下的尾插
	*/

	SLTNode* plist = NULL;


	Push_Back_SLT(&plist, 1);
	Push_Back_SLT(&plist, 2);
	Push_Back_SLT(&plist, 3);
	Push_Back_SLT(&plist, 4);

	Push_Front_SLT(&plist, -1);
	Push_Front_SLT(&plist, -2);
	Push_Front_SLT(&plist, -3);

	SLPrint(plist);

}

void test4()
{

	/*SLTNode* plist = (SLTNode*)malloc(sizeof(SLTNode));
	assert(plist);
	plist->data = 0;
	plist->next = NULL;//测试正常情况多结点下的尾插
	*/

	SLTNode* plist = NULL;


	Push_Back_SLT(&plist, 1);
	Push_Back_SLT(&plist, 2);
	Push_Back_SLT(&plist, 3);
	Push_Back_SLT(&plist, 4);

	Push_Front_SLT(&plist, -1);
	Push_Front_SLT(&plist, -2);
	Push_Front_SLT(&plist, -3);

	SLPrint(plist);

	printf("\n");
	Pop_Back_STL(&plist);
	Pop_Back_STL(&plist);
	Pop_Back_STL(&plist);
	Pop_Back_STL(&plist);

	SLPrint(plist);
	printf("\n");


	Pop_Front_STL(&plist);
	Pop_Front_STL(&plist);
	Pop_Front_STL(&plist);
	

	SLPrint(plist);
	printf("\n");
}

void test5()
{
	SLTNode* plist = NULL;


	Push_Back_SLT(&plist, 1);
	Push_Back_SLT(&plist, 2);
	Push_Back_SLT(&plist, 3);
	Push_Back_SLT(&plist, 4);

	Push_Front_SLT(&plist, -1);
	Push_Front_SLT(&plist, -2);
	Push_Front_SLT(&plist, -3);

	SLPrint(plist);
	printf("\n");
	SLTInsert_forward(&plist, SLTFind(plist,2), 999);

	SLPrint(plist);
	printf("\n");

	SLTErase(&plist, SLTFind(plist, -2));

	SLPrint(plist);
	printf("\n");
	printf("#########");
	printf("\n");
//测试一个结点
	SLTNode* plist2 = NULL;

	Push_Back_SLT(&plist2, 1);

	SLTInsert_forward(&plist2, SLTFind(plist2, 1), 999);

	SLPrint(plist2);
	printf("\n");

	SLTErase(&plist2, SLTFind(plist2, 1));

	SLPrint(plist2);
	printf("\n");
}
void test6()
{
	SLTNode* plist = NULL;


	Push_Back_SLT(&plist, 1);
	Push_Back_SLT(&plist, 2);
	Push_Back_SLT(&plist, 3);
	Push_Back_SLT(&plist, 4);

	Push_Front_SLT(&plist, -1);
	Push_Front_SLT(&plist, -2);
	Push_Front_SLT(&plist, -3);

	SLPrint(plist);
	printf("\n");
	STLInsert_back(&plist, SLTFind(plist, 2), 999);

	SLPrint(plist);
	printf("\n");

	STLErase_back(&plist, SLTFind(plist, -2));

	SLPrint(plist);
	printf("\n");
}

int main()
{

	//test1();//测试打印函数与结构体定义
	//test2();
	//test3(); //测试头插尾插
	//test4(); //测试头删尾删
	//test5(); //测试前插原地删除
	test6();

	return 0;
}

三、 结尾

感谢大家的点赞关注,希望大家多多评论!!!

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AF帆_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值