Lesson2无头单向非循环链表(中)

news2024/9/23 12:35:02

1.链表

1.1链表的概念及结构

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

 1.2链表的分类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向或者双向

2. 带头或者不带头

3. 循环或者非循环

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

2.链表的实现

#pragma once

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//写入访问权限冲突 解决方法:#include <stdlib.h>

//无头+单向+非循环链表增删查改实现
typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;//数据域--存储数据
	struct SListNode* next;//指针域--存放下一个节点的地址
}SLTNode;

// 动态申请一个节点
SLTNode* BuyListNode(SLTDateType x);

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

// 单链表尾插
void SListPushBack(SLTNode** pphead, SLTDateType x);

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

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

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

// 单链表查找
SLTNode* SListFind(SLTNode* phead, SLTDateType x);

//pos位置之前插入
void SListInsert(SLTNode**pphead, SLTNode* pos, SLTDateType x);

// 在pos的后面插入,这个更适合,也更简单
void SListInsertAfter(SLTNode* pos, SLTDateType x);

//单链表在pos位置上删除
void SListErase(SLTNode *pphead, SLTNode* pos);

//单链表在pos后面删除
void SListEraseAfter(SLTNode* pos)

//单链表的释放
void SListDestory(SLTNode** pphead);



2.1动态申请一个节点

SLTNode* BuyListNode(SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

2.2单链表打印

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

2.3单链表尾插

void SListPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = BuyListNode(x);
	//判断是否为空结点
	if (*pphead == NULL)
	{
		*pphead = newnode;//==*plist=newnode
	}
	else
	{
		//找到尾结点
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

2.4单链表的头插

void SListPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = BuyListNode(x);
	//不用判空
	newnode->next = *pphead;
	*pphead = newnode;

}

2.5单链表的尾删

void SListPopBack(SLTNode** pphead)
{
	assert(pphead);
	//判断是否空链表--非法访问
	//a.温柔地方式
	if (*pphead == NULL)
	{
		return;
	}
	//b.暴力地方式
	///assert(*pphead != NULL);

	//1.一个节点
	//2.两个以上节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);//释放pphead指向的
		*pphead = NULL;
	}
	else
	{
	//方法一:
	SLTNode* tail = *pphead;
	SLTNode* prev = NULL;
	while (tail->next/*tail->next != NULL*/)
	{
		prev = tail;
		tail = tail->next;
	}
	free(tail);
	tail = NULL;
	prev->next = NULL;//一个节点非法访问

	//方法二:
	//SLTNode* tail = *pphead;
	//while (tail->next->next)//一个节点非法访问
	//{
	//	tail = tail->next;
	//}
	//free(tail->next);
	//tail->next = NULL;

	}
}

2.6单链表头删

void SListPopFront(SLTNode** pphead)
{
	assert(pphead);
	//判断是否空链表--非法访问
	//a.温柔地方式
	if (*pphead == NULL)
	{
		return;
	}
	//b.暴力地方式
	///assert(*pphead != NULL);

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

}

 2.7单链表查找

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

2.8pos位置之前插入

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);
	SLTNode* newnode = BuyListNode(x);
	if (*pphead == pos)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		//找到pos的前一个位置
		SLTNode* posPrev = *pphead;//pos为第一个节点时,posPrev找不到
		while (posPrev->next != pos)
		{
			posPrev->next = newnode;
			newnode->next = pos;
		}
	}
}

2.9在pos的后面插入,这个更适合,也更简单

void SListInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);
	//不改变*plist的指向,不用二级指针
	//一级指针传参:可以修改它的内容 但不可以修改它的指向
	//*pos指向一个节点
	//可以修改它的内容data和next
	//*plist里面是空的 没有data和next 无内容 
    //所以是通过二级指针来改变他指向的内容
	//除非为*plist开辟空间 *plist成为一个空节点 成为一个哨兵位 就不需要二级指针 
    //直接通过next指向
	assert(pos);
	SLTNode* newnode = BuyListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

2.10单链表在pos位置上删除

void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		*pphead = pos->next;
		free(pos);
		pos = NULL;
		//SListPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while(prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;//置空与不置空都可以
		//pos不置空也无所谓,毕竟在函数中是形参,形参的改变不会影响实参,处于好习惯,还是置空比较好
	}
}

2.11 单链表在pos后面删除

void SListEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

2.12单链表的释放

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

	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	*pphead = NULL;
}

3.代码

SList.h

#pragma once

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//写入访问权限冲突 解决方法:#include <stdlib.h>

//无头+单向+非循环链表增删查改实现
typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SLTNode;

// 动态申请一个节点
SLTNode* BuyListNode(SLTDateType x);

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

// 单链表尾插
void SListPushBack(SLTNode** pphead, SLTDateType x);

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

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

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

// 单链表查找
SLTNode* SListFind(SLTNode* phead, SLTDateType x);

//pos位置之前插入
void SListInsert(SLTNode**pphead, SLTNode* pos, SLTDateType x);

// 在pos的后面插入,这个更适合,也更简单
void SListInsertAfter(SLTNode* pos, SLTDateType x);

//单链表在pos位置上删除
void SListErase(SLTNode *ppphead, SLTNode* pos);

//单链表在pos后面删除
void SListEraseAfter(SLTNode* pos)

//单链表的释放
void SListDestory(SLTNode** pphead);



 SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SList.h"

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

SLTNode* BuyListNode(SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		printf("malloc fail]\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

void SListPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = BuyListNode(x);
	//判断是否为空结点
	if (*pphead == NULL)
	{
		*pphead = newnode;//==*plist=newnode
	}
	else
	{
		//找到尾结点
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

void SListPopBack(SLTNode** pphead)
{
	assert(pphead);
	//判断是否空链表--非法访问
	//a.温柔地方式
	if (*pphead == NULL)
	{
		return;
	}
	//b.暴力地方式
	///assert(*pphead != NULL);

	//1.一个节点
	//2.两个以上节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);//释放pphead指向的
		*pphead = NULL;
	}
	else
	{
	//方法一:
	SLTNode* tail = *pphead;
	SLTNode* prev = NULL;
	while (tail->next/*tail->next != NULL*/)
	{
		prev = tail;
		tail = tail->next;
	}
	free(tail);
	tail = NULL;
	prev->next = NULL;//一个节点非法访问

	//方法二:
	//SLTNode* tail = *pphead;
	//while (tail->next->next)//一个节点非法访问
	//{
	//	tail = tail->next;
	//}
	//free(tail->next);
	//tail->next = NULL;

	}
}

void SListPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = BuyListNode(x);
	//不用判空
	newnode->next = *pphead;
	*pphead = newnode;

}

void SListPopFront(SLTNode** pphead)
{
	assert(pphead);
	//判断是否空链表--非法访问
	//a.温柔地方式
	if (*pphead == NULL)
	{
		return;
	}
	//b.暴力地方式
	///assert(*pphead != NULL);

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

}

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

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);
	SLTNode* newnode = BuyListNode(x);
	if (*pphead == pos)
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		//找到pos的前一个位置
		SLTNode* posPrev = *pphead;//pos为第一个节点时,posPrev找不到
		while (posPrev->next != pos)
		{
			posPrev->next = newnode;
			newnode->next = pos;
		}
	}
}
// 在pos的后面插入,这个更适合,也更简单
void SListInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);
	//不改变*plist的指向,不用二级指针
	//一级指针传参:可以修改它的内容 但不可以修改它的指向
	//*pos指向一个节点
	//可以修改它的内容data和next
	//*plist里面是空的 没有data和next 无内容 所以是通过二级指针来改变他指向的内容
	//除非为*plist开辟空间 *plist成为一个空节点 成为一个哨兵位 就不需要二级指针 直接通过next指向
	assert(pos);
	SLTNode* newnode = BuyListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SListErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		*pphead = pos->next;
		free(pos);
		pos = NULL;
		//SListPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while(prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;//置空与不置空都可以
		//pos不置空也无所谓,毕竟在函数中是形参,形参的改变不会影响实参,处于好习惯,还是置空比较好
	}
}

void SListEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

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

	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}

	*pphead = NULL;
}

Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"SList.h"

void test()
{
	SLTNode* plist = NULL;
	//*plist指向的是NULL
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 1);
	SListPrint(plist);

	SListPushFront(&plist, 2);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 2);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPrint(plist);

	SLTNode* pos = SListFind(plist, 2);
	int i = 1;
	while (pos)
	{
		printf("第%d个pos节点:%->%d\n", i++, pos, pos->data);
		pos = SListFind(pos->next, 2);
	}
	//修改值 2-20
	pos = SListFind(plist, 2);
	if (pos)
	{
		pos->data = 20;
	}
	SListPrint(plist);
	pos = SListFind(plist, 20);
	if (pos)
	{
		pos->data = 30;
	}
	SListPrint(plist);
	
	pos = SListFind(plist, 3);
	if (pos)
	{
		SListInsert(&plist, pos, 30);
	}
	SListPrint(plist);

	pos = SListFind(plist, 30);
	if (pos)
	{
		SListInsert(&plist, pos, 300);
	}
	SListPrint(plist);

	pos = SListFind(plist, 300);
	if (pos)
	{
		SListInsertAfter(pos, 3000);
	}
	SListPrint(plist);
	pos = SListFind(plist, 3000);
	if (pos)
	{
		SListErase(&plist, pos);
	}
	SListPrint(plist);

	pos = SListFind(plist, 300);
	if (pos)
	{
		SListEraseAfter(pos);
	}
	SListPrint(plist);
	SListDestory(&plist);

}	
int main()

{
	test();
	return 0;
}

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

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

相关文章

【SAP Hana】X档案:SAP HANA 数据库基础知识

SAP HANA 数据库基础知识1、基本规则&#xff08;1&#xff09;注释&#xff08;2&#xff09;标识符&#xff08;3&#xff09;引号&#xff08;4&#xff09;保留字2、数据类型&#xff08;1&#xff09;日期时间类型&#xff08;2&#xff09;数字类型&#xff08;3&#xf…

SNMP简单网络管理协议

随着网络的规模越来越庞大&#xff0c;网络中的设备种类繁多&#xff0c;如何对越来越复杂的网络进行有效的管理&#xff0c;从而提供高质量的网络服务&#xff0c;已成为网络管理所面临的巨大挑战。网络的管理和运维手段多样&#xff0c;下面将对几种常见的网管与运维手段展开…

[leetcode.29]两数相除,位运算虽好,不要满眼是她

题目如下 不允许用乘除法&#xff0c;但是我们可以用加减法和位运算。。。不过这里不要用位运算&#xff0c;比如说你要是想用补码交替除法&#xff0c;你根本无法获得移动几位&#xff08;移动31位&#xff1f;太鬼畜了吧&#xff09; 所以说单纯的除法部分&#xff0c;我们可…

测试开发 | 实战演练基于加密接口测试测试用例设计

image1080594 76.4 KB 如果接口测试仅仅只是掌握一些requests或者其他一些功能强大的库的用法&#xff0c;是远远不够的&#xff0c;还需要具有根据公司的业务以及需求去定制化一个接口自动化测试框架能力。所以在这个部分&#xff0c;会主要介绍接口测试用例分析以及通用的流程…

从零开始 verilog 以太网交换机(二)MAC接收控制器的设计与实现

从零开始 verilog 以太网交换机&#xff08;二&#xff09;MAC接收控制器的设计与实现 &#x1f508;声明&#xff1a; &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN主页 &#x1f9e8; 从零开始 verilog 以太网交换机系列专栏&#xff1a;点击这里 &#x1f511;未经作者允…

plt绘制点线图 点(marker)过密的解决办法

设置 markevery10 plt.plot(x, y, markero, markevery10) import matplotlib.pyplot as plt import numpy as npxnp.arange(0,2*np.pi,0.01) ynp.sin(x)fig, ax plt.subplots(constrained_layoutTrue)plt.title(markevery10)ax.plot(x, y, markero, markevery50, mfcr,mecr)…

(小甲鱼python)函数笔记合集四 函数(IV)总结 函数中参数的作用域 局部作用域 全局作用域 global语句 嵌套函数 nonlocal语句等详解

一、基础复习 函数的基本用法 创建和调用函数 函数的形参与实参等等函数的几种参数 位置参数、关键字参数、默认参数等函数的收集参数*args **args 解包参数详解 二、函数中参数的作用域 作用域&#xff1a;一个变量可以被访问的范围&#xff0c;一个变量的作用域总是由它在代…

jQuery学习-01jQuery下载安装

1 jQuery的介绍 jQuery就是js函数库&#xff0c;里面有大量的js函数库&#xff0c;使用这些函数操作dom对象&#xff0c;做事件&#xff0c;动画&#xff0c;ajax处理&#xff1b; 地址&#xff1a;https://jquery.com/ 2下载 地址&#xff1a;https://jquery.com/download/…

Apache Solr 9.1-(一)初体验单机模式运行

Apache Solr 9.1-&#xff08;一&#xff09;初体验单机模式运行 Solr是一个基于Apache Lucene的搜索服务器&#xff0c;Apache Lucene是开源的、基于Java的信息检索库&#xff0c;Solr能为用户提供无论在任何时候都可以根据用户的查询请求返回结果&#xff0c;它被设计为一个强…

day12-继承

1. 继承 1.1 继承的实现&#xff08;掌握&#xff09; 继承的概念 继承是面向对象三大特征之一&#xff0c;可以使得子类具有父类的属性和方法&#xff0c;还可以在子类中重新定义&#xff0c;以及追加属性和方法 实现继承的格式 继承通过extends实现格式&#xff1a;class 子…

【算法题】1318. 或运算的最小翻转次数

插&#xff1a; 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你三个正整数 a、b 和 c。 你可…

Webpack:HTML Webpack Plugin插件

HTML Webpack Plugin插件&#xff0c;在Webpack构建的前端项目中&#xff0c;用于简化index.html文件的创建&#xff0c;以免除项目打包之后手动创建/拷贝index.html到打包目录下的繁琐步骤。以下&#xff0c;从一个已构建好的Vue项目中的一个现象谈起&#xff0c;逐步深入了解…

vs code中的platformIO插件,完成Arduino的程序编写,导入,安装开发板管理库

准备工作 vs code已经安装好&#xff0c;扩展插件plateformIO也安装好。&#xff08;下图是platformIO安装方式&#xff09; platformIO界面功能介绍和简单使用 新建Arduino项目 选择正确的开发板型号&#xff0c;和自己习惯的编译框架。打开后有一个.ini的配置文件&#x…

MySQL表的增删改查(初级)

MySQL数据库最核心的内容就是增删改查&#xff08;即CURD&#xff09;,看了这篇初级增删改查的博客之后可以解决以后工作中百分之80-90的内容&#xff0c;这部分的知识并不是很难&#xff0c;但是需要一定的熟练程度&#xff1b;C&#xff1a;create--新增U&#xff1a;update-…

设计模式_行为型模式 -《策略模式》

设计模式_行为型模式 -《策略模式》 笔记整理自 黑马程序员Java设计模式详解&#xff0c; 23种Java设计模式&#xff08;图解框架源码分析实战&#xff09; 概述 先看下面的图片&#xff0c;我们去旅游选择出行模式有很多种&#xff0c;可以骑自行车、可以坐汽车、可以坐火车、…

Nginx应用场景

Nginx应用场景 Nginx配置文件说明 Nginx 的配置文件位置 1、文件位置 安装目录\conf\nginx.conf 安装目录\nginx.conf 2、两个文件是一样的 3、使用 /usr/local/nginx/sbin/nginx 启动 Nginx &#xff0c;默认用的是 安装目录 \nginx.conf 配置文件 4、作用&#xff1a;完…

三、命令行工具cmder的安装

1、cmder安装 1.1、cmder简介 cmder是一个增强型命令工具&#xff0c;不仅可以使用Windows下的所有命令&#xff0c;并且还可以使用Linux和shell命令。 1.2、cmder下载 (1)cmder的官方网站提供的下载地址实在是太慢了基本是下载不下来&#xff0c;建议到清华大学的镜像站去…

SELECT必知必会_引擎,PROCEDURE,事务处理

书接上文&#xff0c;之前说了Mysql的SELECT部分&#xff0c;本片文章会重点介绍关于MySql的其他一些知识&#xff0c;也会是MySql必知必会的最后一篇。 首先&#xff0c;是Mysql中的增删改操作&#xff0c;对于测试岗来说&#xff0c;这部分知识相对来说不是那么重要&#xf…

案例分享 | AI助力肯尼亚“Sheng”语研究

你听说过一种叫做“Sheng”的语言吗&#xff1f;这是一种斯瓦希里语-英语俚语&#xff0c;主要使用者为肯尼亚内罗毕等城市地区的青年。近年来&#xff0c;随着“Sheng”的使用量不断增加&#xff0c;一家非盈利组织正在帮助更新该地区的社区信息资源&#xff0c;随时根据词汇中…

【6s965-fall2022】深度学习的效率指标

两个核心指标是计算和内存(Computation and Memory)。需要考虑的三个维度是存储、延迟和能耗(Storage, Latency, and Energy)。 延迟 Latency Latency max(Toperation,Tmemory)max(T_{operation}, T_{memory})max(Toperation​,Tmemory​) 能耗 Energy 内存访问比计算更消耗…