【数据结构与算法】单链表的增删查改(代码+图解)

news2025/7/17 8:21:37

目录

顺序表和链表的特点:

时间复杂度:

分析:

单链表结构体和数据类型:

开辟一个节点和存储数据:

打印

尾插

         尾删

头插

头删:

查找单链表中的元素

在pos后插入x

在pos前插入x

删除pos后的一个元素:

删除pos位置的元素:

摧毁单链表:

完整代码:


80f3084b30fd4543b4719418da39509b.png

顺序表和链表的特点:

 顺序表使用数组存储线形的元素,其特点是可以随机存取,但是,因为逻辑上相邻的元素物理上也相邻,所以插入删除需要移动元素.

链表使用指针链表示线形表元素的逻辑关系,插入和删除只需修改指针,不能随机存取.

单向链表增加删除节点简单。 遍历时候不会死循环;

时间复杂度:

链表中数据元素之间的逻辑关系靠的是节点之间的指针,当需要在链表中某处插入或删除节点时,只需改变相应节点的指针指向即可,无需大量移动元素,因此链表中插入、删除或移动数据所耗费的时间复杂度为 O(1)

顺序表中,插入、删除和移动数据可能会牵涉到大量元素的整体移动,因此时间复杂度至少为 O(n);

链表存储数据一次只开辟存储一个节点的物理空间,如果后期不够还可以再申请。

分析:

单链表结构体和数据类型:

typedef int SLTDataType;
typedef struct SLlistNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

 

开辟一个节点和存储数据:

SLTNode* BuySLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

malloc函数开辟一个大小为sizeof(SLTNode)字节即一个结构体大小的空间,原本malloc返回值是void类型的指针,但是代码里面的(SLTNode*)将malloc的返回值强制类型转换为SLTNode类型,这样方便了后面数据的存储;

打印


void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

调用SLTPrint()函数使,用*phead接收传进来的参数

尾插

void SLTPushBack(SLTNode** phead,SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		SLTNode* ptail = *phead;
		while (ptail->next)     //找尾
		{
			ptail = ptail->next;
		}
		ptail->next = newnode;
	}
}

图解:

66eba0284d364d38b8a51ccb6ee89624.png

首先用ptail记住链表头部的位置,再ptail=ptail->next寻找最后一个节点 

尾删

void SLTPopBack(SLTNode** phead)
{
	assert(*phead);
	//只有一个指针
	if ((*phead)->next==NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		SLTNode* pre = NULL;                //记录倒数第二个指针,
		SLTNode* ptail = *phead;
		while (ptail->next)
		{
			pre = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		pre->next = NULL;                     //将被释放的那个指针置空,避免出现野指针

	}
}

 assert(*phead)实现对*phead判空;这里解释一下为什么传参要用双指针,因为改变的是指针,而不是指针的值;

例如:

//单指针传参交换指针指向的值
void Swap1(int *p1,int *p2)
{
    int tmp= *p1;   //这里p1解引用之后就是p1指针指向的值
    *P1 = *p2;
    *p2= tmp;
}

//双指针传参交换指针
void Swap2(int ** pp1,int **pp2)
{
    int *tmp=*pp1;
    *pp1 = *pp2;
    *pp2 = tmp;
}

int main()
{
    int a=0,b=1;
    Swap1(&a,&b);
    
    int *ptr1  = &a,ptr2 = &b;
    Swap2(&ptr1,&ptr2); 
}

Swap2(&ptr1,&ptr2)通过交换a、b的地址来交换值,而Swap1通过改变指针指向的值来交换值;

总结:1、改变int,传递int *给形参,*形参进行交换改变

           2、改变int*,传递int**给形参,*形参进行交换改变

头插

void SLTPushFront(SLTNode** phead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *phead;                      //
	*phead = newnode;                           //换头
}

33881d44edb9473aa8caab5882b2a364.png

先将newnode->next指向phead(头节点),再phead=newnode; 

头删:

void SLTPopFront(SLTNode** phead)
{
	assert(*phead);
	SLTNode* newnode = NULL;
	newnode = (*phead)->next;          //存储第二个指针
	free(*phead);                            //释放第一个指针空间
	*phead = newnode;                        //换头
}

查找单链表中的元素

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

在pos后插入x

void SLTInsertAfter(SLTNode** phead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* cur = *phead;
	while (cur != pos)
	{
		cur = cur->next;
	}
	SLTNode* newnode = BuySLTNode(x);
		newnode->next = cur->next;
		cur->next = newnode;

}

在pos前插入x

void SLTInsert(SLTNode**phead,SLTNode* pos, SLTDataType x)
{
	assert(pos);
	if (*phead == pos)
	{
		SLTPushFront(phead,x);           //头插
	}
	else
	{
		SLTNode* pre = *phead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		SLTNode* newnode = BuySLTNode(x);
		pre->next = newnode;
		newnode->next = pos;
	}
}

删除pos后的一个元素:

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	if (pos->next = NULL)
	{
		return;
	}
	else
	{
		SLTNode* next = pos->next;
		pos->next = next->next;
		free(next);
	}

}

图解: 

f9164a69043f4ba59d4d05166642001b.png

删除pos位置的元素:


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

	if (*phead == pos)
	{
		SLTPopFront(phead);                   //这里不用*phead,因为传过去的是**phead;解引用的时候改变的是*phead;
	}
	else
	{
		SLTNode* pre = *phead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		pre->next = pos->next;
		free(pos);
	}

}

摧毁单链表:

void SLTDestroy(SLTNode** phead)
{
	SLTNode* cur = *phead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*phead = NULL;
}

完整代码:

我用SList.h存放函数的声明和一些头文件:

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

typedef int SLTDataType;
typedef struct SLlistNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

SLTNode* BuySLTNode(SLTDataType x);
SLTNode* CreateSList(int n);
void SLTPrint(SLTNode* phead);

void SLTPushBack(SLTNode** phead, SLTDataType x);
void SLTPopBack(SLTNode** pphead);
void SLTPushFront(SLTNode** phead, SLTDataType x);
void SLTPopFront(SLTNode** phead);

SLTNode* SLTFind(SLTNode* phead, SLTDataType x);// 查找链表中的元素
void SLTInsertAfter(SLTNode**phead,SLTNode* pos,SLTDataType x);//在pos位置之后插入x
void SLTInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);//在pos位置前面插入x

void SLTEraseAfter(SLTNode* pos);//删除pos后面的一个元素

void SLTErase(SLTNode** phead, SLTNode* pos);//删除pos位置的元素
void SLTDestroy(SLTNode** phead);

用SList.c定义函数:

#define  _CRT_SECURE_NO_WARNI
#include"SList.h"

SLTNode* BuySLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
//SLTNode* CreateSList(int n)
//{
//	SLTNode* phead = NULL;
//	SLTNode* ptail = NULL;
//	int x;
//	for (int i = 0; i < n; i++)
//	{
//		SLTNode* newnode = BuySLTNode(i);
//		if (phead == NULL)
//		{
//			ptail = phead = newnode;
//		}
//		else
//		{
//			ptail->next = newnode;
//			ptail = newnode;
//		}
//	}
//	return phead;
//}

void SLTPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

void SLTPushBack(SLTNode** phead,SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	if (*phead == NULL)
	{
		*phead = newnode;
	}
	else
	{
		SLTNode* ptail = *phead;
		while (ptail->next)     //找尾
		{
			ptail = ptail->next;
		}
		ptail->next = newnode;
	}
}
void SLTPopBack(SLTNode** phead)
{
	assert(*phead);
	//只有一个指针
	if ((*phead)->next==NULL)
	{
		free(*phead);
		*phead = NULL;
	}
	else
	{
		SLTNode* pre = NULL;                //记录倒数第二个指针,
		SLTNode* ptail = *phead;
		while (ptail->next)
		{
			pre = ptail;
			ptail = ptail->next;
		}
		free(ptail);
		pre->next = NULL;                     //将被释放的那个指针置空,避免出现野指针

	}
}

void SLTPushFront(SLTNode** phead, SLTDataType x)
{
	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *phead;                      //
	*phead = newnode;                           //换头
}


void SLTPopFront(SLTNode** phead)
{
	assert(*phead);
	SLTNode* newnode = NULL;
	newnode = (*phead)->next;          //存储第二个指针
	free(*phead);                            //释放第一个指针空间
	*phead = newnode;                        //换头
}

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void SLTInsertAfter(SLTNode** phead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* cur = *phead;
	while (cur != pos)
	{
		cur = cur->next;
	}
	SLTNode* newnode = BuySLTNode(x);
		newnode->next = cur->next;
		cur->next = newnode;

}

void SLTInsert(SLTNode**phead,SLTNode* pos, SLTDataType x)
{
	assert(pos);
	if (*phead == pos)
	{
		SLTPushFront(phead,x);           //头插
	}
	else
	{
		SLTNode* pre = *phead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		SLTNode* newnode = BuySLTNode(x);
		pre->next = newnode;
		newnode->next = pos;
	}
}

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	if (pos->next = NULL)
	{
		return;
	}
	else
	{
		SLTNode* next = pos->next;
		pos->next = next->next;
		free(next);
	}

}

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

	if (*phead == pos)
	{
		SLTPopFront(phead);                   //这里不用*phead,因为传过去的是**phead;解引用的时候改变的是*phead;
	}
	else
	{
		SLTNode* pre = *phead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		pre->next = pos->next;
		free(pos);
	}

}

void SLTDestroy(SLTNode** phead)
{
	SLTNode* cur = *phead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*phead = NULL;
}

用test.c写主函数和函数调用:

#define  _CRT_SECURE_NO_WARNINGS
#include"SList.h"
//void test1()
//{
//	SLTNode* plist = NULL;
//	plist=CreateSList(3);
//	SLTPrint(plist);
//}
//
//void testSLTNode2()
//{
//	SLTNode* plist = NULL;
//	SLTPushBack(&plist, 1);
//	SLTPushBack(&plist, 2);
//	SLTPushBack(&plist, 3);
//	SLTPushBack(&plist, 4);
//	
//	SLTPopBack(&plist);
//	
//	SLTPrint(plist);
//}
//
//void testSTLNode3()
//{
//	SLTNode* plist = NULL;
//	SLTPushFront(&plist,1);
//	SLTPushFront(&plist,2);
//	SLTPushFront(&plist,3);
//	SLTPushFront(&plist,4);
//	SLTPushFront(&plist,5);
//
//	SLTPrint(plist);
//
//	SLTPopFront(&plist);
//	SLTPopFront(&plist);
//
//	printf("\n");
//	SLTPrint(plist);
//}
//void testSLTNode4()
//{
//	SLTNode* plist = NULL;
//	SLTPushFront(&plist, 2);
//	SLTPushFront(&plist, 3);
//	SLTPushFront(&plist, 5);
//
//	SLTNode *p = SLTFind(plist, 5);
//	SLTInsertAfter(p, 99);
//
//	SLTPrint(plist);
//}

//void testSLTNode5()
//{
//	SLTNode* plist = NULL;
//	SLTPushBack(&plist, 1);
//	SLTPushBack(&plist, 2);
//	SLTPushBack(&plist, 5);
//
//	SLTNode* p = SLTFind(plist, 1);
//	SLTInsertAfter(&plist,p , 6);
//
//	SLTPrint(plist);
//}

void testSLTNode6()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPushFront(&plist, 5);
	SLTPushFront(&plist, 6);

	SLTNode* p = SLTFind(plist, 4);
	SLTInsert(&plist,p, 100);

	SLTPrint(plist);

	p = SLTFind(plist, 5);
	SLTInsertAfter(&plist,p, 200);

	SLTPrint(plist);

	p = SLTFind(plist, 100);
	SLTErase(&plist, p);
	SLTPrint(plist);
	p = NULL;

	SLTDestroy(&plist);
	SLTPrint(plist);
}

int main()
{
	//test1();
	//testSLTNode2();
	//testSTLNode3();
	//testSLTNode4();
	//testSLTNode5();
	testSLTNode6();
	return 0;
}

707ac00b85a147ffa61c5dc06a2fe02f.png

 运行结果:

261e991e051e47cc9825ea58ce16e0f7.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/6719.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【详细学习SpringBoot源码之自定义监听器实战演练-6(自定义监听器、自定义监听事件、指定监听事件)】

一.知识回顾 【0.SpringBoot专栏的相关文章都在这里哟&#xff0c;后续更多的文章内容可以点击查看】 【1.SpringBoot初识之Spring注解发展流程以及常用的Spring和SpringBoot注解】 【2.SpringBoot自动装配之SPI机制&SPI案例实操学习&SPI机制核心源码学习】 【3.详细学…

RabbitMQ - 交换机

文章目录1. Exchanges2. 临时队列3. 绑定 bindings4. Fanout exchange4.1 Fanout 介绍4.2 Fanout 实战5. Direct exchange5.1 Direct 介绍5.2 多重绑定5.3 Direct 实战6. Topics exchange6.1 Topic 的介绍6.2 Topic 匹配案例6.3 Topic 实战1. Exchanges RabbitMQ 消息传递模型…

Techsmith Camtasia Studio2023专业电脑屏幕录制工具

Techsmith Camtasia Studio2023电脑版版下载! 从今天开始&#xff0c;我们开始讲解短视频制作。一提到短视频制作&#xff0c;很多人可能首先想到的&#xff0c;是PR、绘声给影。的确&#xff0c;PR和绘声绘影功能非常强大&#xff0c;毕竟它们是影视制作的专业软件&#xff0…

【Spring Boot】Day01

文章目录一、Spring Boot的引入创建总结演示运行可能出现的错误二、使用idea创建Spring Boot创建运行http://localhost:8080/hello三、介绍配置文件介绍application.yml一、Spring Boot的引入 创建 总结 # 项目中集成spring和springmvc 1. 新建项目 2. 拷贝jar包(maven) &…

5G核心网技术基础自学系列 | SMF提供的服务

书籍来源&#xff1a;《5G核心网 赋能数字化时代》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G核心网技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 SMF提供两种服务&#xff0c; 即N…

图文详解Linux基础经典教程(06)——CentOS安装JDK

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 概述 从本节教程开始&#xff0c;我们尝试在CentOS上搭建Java开发环境&#xff0c;即在CentOS中安装&#xff1a;JDK、Tomcat、MySQL等开发工具。 这些工具均可选用YUM或压…

225. 用队列实现栈、232. 用栈实现队列、622. 设计循环队列

LeetCode题解前言用队列实现栈用栈实现队列循环队列总结前言 这三道题都是比较经典的一道题&#xff0c;主要想要考察我们对于栈、队列的性质的应用&#xff0c;也是笔试题的常客&#xff01;&#xff01;&#xff01;接下来就让我们一起来手撕它&#xff01;&#xff01;&…

【C】程序环境和预处理

&#x1f648;个人主页&#xff1a;阿伟t &#x1f449;系列专栏&#xff1a;【C语言–大佬之路】 &#x1f388;今日心语&#xff1a;你所看到的惊艳&#xff0c;都曾被平庸所历练。 本章重点&#xff1a; 程序的翻译环境程序的执行环境详解&#xff1a;C语言程序的编译链接…

next-key lock案例

1. 加锁规则 在默认的可重复读的隔离级别下&#xff0c;加锁规则可以总结为&#xff1a;两个原则&#xff0c;两个优化和一个bug。 原则1&#xff1a;加锁的基本单位是next-key lock。next-key lock是前开后闭的区间原则2&#xff1a;查询过程中访问到的对象才会加锁优化1&am…

计算机网络-数据交换技术

数据交换&#xff1a;实现在大规模网络核心上进行数据传输的技术基础。 常见的数据交换技术包括&#xff1a;电路交换、报文交换和分组交换。 一、电路交换 电路&#xff1a;通过中间交换结点为两台主机之间建立一条专用的通信线路。电路交换&#xff1a;是最早出现的一种交换…

这个神器,让我的 Python 代码运行速度快了100倍

Python 已经得到了全球程序员的喜爱&#xff0c;连续多期稳坐编程语言排行榜第一把交椅。但是还是遭到一些人的诟病&#xff0c;原因之一就是认为它运行缓慢。 要是有一款能够自动优化我们代码的神器该有多好啊&#xff01; 于是&#xff0c;大家都在想尽各种办法来提高 Pytho…

弘扬企业家精神!闪马智能创始人兼CEO彭垚再获殊荣

随着新业态、新活力涌现&#xff0c;诞生了一批又一批新的优秀企业家。11月8日&#xff0c;闪马智能创始人兼CEO彭垚荣膺胡润百富“2022青年产业领袖”。 自2016年起&#xff0c;彭垚连续创业&#xff0c;7年内成功培育并壮大了两家行业独角兽企业&#xff0c;2019年4月成立闪…

创龙AD+全志T3 ad_display 开发案例 (2)

上一篇&#xff1a;创龙AD全志T3 ad_display 开发案例(1) 前 言 本文主要介绍基于全志科技T3(ARM Cortex-A7)处理器的8/16通道AD采集开发案例&#xff0c;使用核芯互联CL1606/CL1616AD芯片&#xff0c;亦适用于ADI AD7606/AD7616。CL1606/CL1616与AD7606/AD7616软硬件兼容。 …

OpenCV图像处理——图像梯度

总目录 图像处理总目录←点击这里 七、图像梯度 7.1、图像梯度-Sobel算子 原图 直接计算 不建议这么算 sobelxycv2.Sobel(img,cv2.CV_64F,1,1,ksize3) sobelxy cv2.convertScaleAbs(sobelxy) cv_show(sobelxy,sobelxy)分别计算——合并 当前位置的像素值等于sobel算…

spring security调用过程;及自定义改造

认证/授权概述 一般系统都有登录接口来校验用户是否存在&#xff0c;密码是否正确&#xff0c;然后会颁发一个token给客户端&#xff0c;后续客户端就可以带着这个token来请求&#xff0c;代表自己是合法请求。 spring security责任链 请求->UsernamePasswordAuthenticati…

【MFC】打砖块小游戏(上)(5)

创建WIN32项目的时候&#xff0c;可以去掉勾选【空项目】可以减少工作量。 创建项目 文件-》新建-》 项目-》WIN32项目-》取消勾选空项目&#xff0c;完成创建 创建完成后&#xff0c;多出了很多文件&#xff0c;当然很多代码是前面已经手动写过了的&#xff1a; stdafx.h …

聚醚羰基铑功能化离子液体{[CH3O(CH2CH2O)nmim][Rhx(CO)y]}

聚醚羰基铑功能化离子液体{[CH3O(CH2CH2O)nmim][Rhx(CO)y]} 离子液体种类 目前研究较多的离子液体阳离子&#xff0c;根据有机母体的不同主要可分四种&#xff0c;即咪唑类离子[R1R3Im]、吡啶类离子[RPy]、烷基季铵类离子[NRxH4-x]以及烷基季膦类离子[PRxH4-x]。这四类阳离子…

【Designing ML Systems】第 9 章 :生产中的持续学习和测试

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

MySQL数据库的约束

文章目录一、约束是什么&#xff1f;二、约束的具体操作Not NULLUNIQUE约束的组合使用PRIMARY KEYDEFAULTFOREIGN KEY一、约束是什么&#xff1f; 约束就是&#xff0c;在创建表的时候&#xff0c;对表设置一些规则&#xff0c;只有满足这些规则&#xff0c;才可以插入数据&am…

【微服务】Nacos通知客户端服务变更以及重试机制

&#x1f496;Spring家族源码解析及微服务系列 ✨【微服务】Nacos服务发现源码分析 ✨【微服务】SpringBoot监听器机制以及在Nacos中的应用 ✨【微服务】Nacos客户端微服务注册原理流程 ✨【微服务】SpringCloud中使用Ribbon实现负载均衡的原理 ✨【微服务】SpringBoot启动流程…