数据结构---详解单链表

一、单链表的概念及性质

1、链表的概念

  • 链表是一种物理存储结构上非连续、非顺序的储存结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
    在这里插入图片描述
    我们看上图,一个链表就很像一节节车厢一样,和顺序表不同的是,链表里的每节“车厢”都是独立申请下来的空间,我们称这一节节车厢为“结点”。我们看上图会发现结点的组成主要有两个部分:当前结点要保存的数据和保存下一个结点的地址(指针变量)。图中指针变量plist保存的是第一个结点的地址,所以我们称plist此时指向第一个结点。如果我们想让plist指向第二个又或者其他结点,只需要修改plist保存的地址内容即可。链表中每个结点都是独立申请的,我们需要通过指针变量来保存下一个结点的位置才能从当前结点找到下一个结点。

2、性质

  • 链式结构在逻辑上是连续的,在物理结构上不一定连续。
  • 结点一般是从堆上申请的。
  • 从堆上申请来的空间,是按照一定策略分配出来的,每次申请空间可能连续也可能不连续。

二、实现单项链表

1、准备工作

  • 创建三个文件(如下图所示)
    在这里插入图片描述
  1. 头文件SList.h:用来声明函数,定义链表
    2.源文件SList.c:用来实现函数
    3.测试文件test.c:测试工作(写完一个接口函数测试一下,避免后续的麻烦)

2、实现链表的基本接口

(1)创建一个链表

  • 在头文件中创建链表结点结构,和顺序表不用的是顺序表是创建一个数组arr和有效数据和容量大小,而链表这是储存一个数据和指向下一个数据的地址。
typedef int SLTDataType;

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

(2)链表的打印

//打印
void SLTPrint(SLTNode *phead) 
{
	//重新创建一个pcur指针是为了避免指针指向的改变
	//导致无法重新找到首节点
	SLTNode* pcur = phead;
	//链表遍历和数组有很大区别
	while (pcur != NULL)
	{
		printf("%d -> ", pcur->data);
		//打印完后要指向下一个地址
		pcur = pcur->next;
	}
	printf("NULL\n");
}

这里有一个点需要注意:pcur = pcur->next这里是将pcur的下一个值赋值给pcur这样就能打印出链表的下一个结点了。

(3)尾插

  • 在尾插之前还要说一个点,就是要创建结点的代码如下所示
    创建一个结点
  • 顺序表是扩容,这里是创建结点
//创建一个节点
SLTNode* SLTBuyNode(SLTDataType x)
{
	//给节点开辟一个空间
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	//如果不是NULL,就把x放在新的节点上
	node->data = x;
	node->next = NULL;

	return node;
}

这下面是尾插的代码:

//尾插
void SLTPushBack(SLTNode **pphead, SLTDataType x)
{
	SLTNode* newnode = SLTBuyNode(x);
	//判断是不是空链表
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//如果链表非空,找尾节点
		SLTNode* ptail = *pphead;
		while (ptail->next != NULL) //可以写成ptail->next
		{
			//遍历找到尾节点
			ptail = ptail->next;
		}
		//找到后跳出循环,将新创建的节点赋值过去
		ptail->next = newnode;
	}
}

(4)头插

//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

头插就相对简单,直接创建新的结点然后插入第一个结点,再把创建的结点作为头节点

(5)尾删

//尾删
void SLTPopBack(SLTNode** pphead)
{
	//分两种情况,只有一个节点的时候
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else {
		//第一个是传参的时候不能为空
	   //第二个人是链表不能为空
		assert(pphead && *pphead);
		//创建两个指针遍历
		SLTNode* ptail = *pphead;
		SLTNode* prec = NULL;
		while (ptail->next)
		{
			prec = ptail;
			ptail = ptail->next;
		}
		prec->next = NULL;
		free(ptail);
		ptail = NULL;
	}
}

(6)头删

//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead && *pphead);

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

这里注意释放掉删除的结点,记得将下一个结点置为头节点

(7)查找

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没找到
	return NULL;
}

循环遍历链表,找到就返回,找不到就返回NULL

(8) 在指定之前位置插入数据

//在指定位置之前插入数据
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && pos);

	if (pos == *pphead)
	{
		//头插
		SLTPushFront(pphead, x);
	}
	else {
		//创建一个节点
		SLTNode* newnode = SLTBuyNode(x);
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			//next不是pos的时候继续往下遍历
			prev = prev->next;
		}
		//prev->next是pos的时候
		newnode->next = pos;
		prev->next = newnode;
	}
}

(9) 在指定位置之后插入数据

//在指定位置之后插入数据
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

(10)删除pos结点

//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && pos);

	if (pos == *pphead)
	{
		//要删除的节点就是头节点时
		SLTPopFront(pphead);
	}
	else {
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

(11)删除pos之后的结点

//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

(12)链表的销毁

  • 有借有还,再借不难
//销毁链表
void SListDestroy(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

三、链表的代码总览

1、SList.h

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

typedef int SLTDataType;

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

//打印
void SLTPrint(SLTNode *phead);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x);
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);
//查找
SLTNode* SLTFind(SLTNode* phead,SLTDataType x);
//在指定位置之前插入数据
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//在指定位置之后插入数据
void SLInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SListDestroy(SLTNode** pphead);

2、SList.c

#define  _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

//创建一个节点
SLTNode* SLTBuyNode(SLTDataType x)
{
	//给节点开辟一个空间
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	//如果不是NULL,就把x放在新的节点上
	node->data = x;
	node->next = NULL;

	return node;
}
//打印
void SLTPrint(SLTNode *phead) 
{
	//重新创建一个pcur指针是为了避免指针指向的改变
	//导致无法重新找到首节点
	SLTNode* pcur = phead;
	//链表遍历和数组有很大区别
	while (pcur != NULL)
	{
		printf("%d -> ", pcur->data);
		//打印完后要指向下一个地址
		pcur = pcur->next;
	}
	printf("NULL\n");
}
//尾插
void SLTPushBack(SLTNode **pphead, SLTDataType x)
{
	SLTNode* newnode = SLTBuyNode(x);
	//判断是不是空链表
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//如果链表非空,找尾节点
		SLTNode* ptail = *pphead;
		while (ptail->next != NULL) //可以写成ptail->next
		{
			//遍历找到尾节点
			ptail = ptail->next;
		}
		//找到后跳出循环,将新创建的节点赋值过去
		ptail->next = newnode;
	}
}
//头插
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}
//尾删
void SLTPopBack(SLTNode** pphead)
{
	//分两种情况,只有一个节点的时候
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else {
		//第一个是传参的时候不能为空
	   //第二个人是链表不能为空
		assert(pphead && *pphead);
		//创建两个指针遍历
		SLTNode* ptail = *pphead;
		SLTNode* prec = NULL;
		while (ptail->next)
		{
			prec = ptail;
			ptail = ptail->next;
		}
		prec->next = NULL;
		free(ptail);
		ptail = NULL;
	}
}
//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead && *pphead);

	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	//没找到
	return NULL;
}
//在指定位置之前插入数据
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && pos);

	if (pos == *pphead)
	{
		//头插
		SLTPushFront(pphead, x);
	}
	else {
		//创建一个节点
		SLTNode* newnode = SLTBuyNode(x);
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			//next不是pos的时候继续往下遍历
			prev = prev->next;
		}
		//prev->next是pos的时候
		newnode->next = pos;
		prev->next = newnode;
	}
}
//在指定位置之后插入数据
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//删除pos结点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && pos);

	if (pos == *pphead)
	{
		//要删除的节点就是头节点时
		SLTPopFront(pphead);
	}
	else {
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}
//销毁链表
void SListDestroy(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

3、test.c

#define  _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"

void test1()
{
	//手动构建一个链表
	//创建空间
	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
	//初始化
	node1->data = 1;
	node2->data = 2;
	node3->data = 3;
	node4->data = 4;
	//连接节点
	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = NULL;
	//打印
	SLTNode* plist = node1;
	SLTPrint(plist);
}
//尾插测试
void test2()
{
	SLTNode* plist = NULL;
	//这里是取指针的地址,所以要二级指针来接收
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPushBack(&plist, 5);
	//打印
	SLTPrint(plist);
}
//尾删测试
void test3()
{
	//先插入数据
	SLTNode* plist = NULL;
	
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPushBack(&plist, 5);
	//打印
	SLTPrint(&plist);
	//尾删
	SLTPopBack(&plist);
	//打印
	SLTPrint(plist);
}
//头插测试
void test4()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPushFront(&plist, 5);
	//打印
	SLTPrint(plist);
}
//头删测试
void test5()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPushFront(&plist, 5);
	//打印
	SLTPrint(plist);
	//头删
	SLTPopFront(&plist);
	//打印
	SLTPrint(plist);
}

//查找测试
int test6()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPushFront(&plist, 5);
	//查找
	SLTNode* ret = SLTFind(plist, 6);
	if (ret == NULL)
	{
		printf("未找到!");
	}
	else {
		printf("找到了!");
	}
}

//在指定位置之前插入数据测试
void test7()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPushFront(&plist, 5);
	//查找
	SLTNode* find = SLTFind(plist, 5);
	SLInsert(&plist, find, 99);
	//打印
	SLTPrint(plist);
}
//在指定位置之后插入数据测试
void test8()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPushFront(&plist, 5);
	//查找
	SLTNode* find = SLTFind(plist, 1);
	SLInsertAfter(find, 88);
	//打印
	SLTPrint(plist);
}
//删除pos结点测试
void test9()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	SLTPushFront(&plist, 5);
	//查找
	SLTNode* find = SLTFind(plist, 5);
	SLTErase(&plist, find);
	//打印
	SLTPrint(plist);
}
//删除pos之后的结点
void test10()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	//查找
	SLTNode* find = SLTFind(plist, 1);
	SLTEraseAfter(find);
	//打印
	SLTPrint(plist);
}
//销毁链表测试
void test11()
{
	SLTNode* plist = NULL;

	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPushFront(&plist, 4);
	//打印
	SLTPrint(plist);
	//销毁
	SListDestroy(&plist);
	//打印
	SLTPrint(plist);
}

int main()
{
	//test1();
	//test2();
	//test3();
	//test4();
	//test5();
	//test6();
	//test7();
	//test8();
	//test9();
	//test10();
	test11();

	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Spring Boot的网上商品订单转手系统设计与实现,LW+源码+讲解

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装网上商品订单转手系统软件来发挥其高效地信息处理的作用&a…

金蝶云星空与聚水潭系统的数据无缝对接案例

金蝶云星空与聚水潭的其他出库单数据集成案例分享 在企业日常运营中&#xff0c;数据的高效流动和准确处理至关重要。本文将重点介绍如何通过轻易云数据集成平台&#xff0c;实现金蝶云星空系统中的其他出库单数据无缝对接到聚水潭系统。本次集成方案名为“金蝶-其他出库单——…

企业级大数据安全架构

安全架构 一、集群访问控制1.1 Kerberos认证机制1.2 Apache Knox 统一访问网关 二、资源授权管理2.1 Apache Ranger 数据授权与管理 三、服务安全保障3.1 LDAP 轻量目录访问协议 四、大数据安全架构 当谈到企业级大数据平台时&#xff0c;安全性是一个至关重要的方面。随着数据…

cv::intersectConvexConvex返回其中一个输入点集,两个点集不相交

问题&#xff1a;cv::intersectConvexConvex返回其中一个输入点集&#xff0c;但两个点集并不相交 版本&#xff1a;opencv 3.1.0 git上也有人反馈了intersectConvexConvex sometimes returning one of the input polygons in case of empty intersection #10044 是凸包嵌套判…

贪心算法day3(最长递增序列问题)

目录 1.最长递增三元子序列 2.最长连续递增序列 1.最长递增三元子序列 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;我们只需要设置两个数进行比较就好。设a为nums[0]&#xff0c;b 为一个无穷大的数&#xff0c;只要有比a小的数字就赋值…

SpringBoot助力的共享汽车业务优化系统

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

基于STM32的LCD1602显示Proteus仿真设计(仿真+程序+设计报告+讲解视频)

这里写目录标题 1.主要功能0. 资料清单&下载链接资料下载链接&#xff1a;2.仿真设计3. 程序设计4. 设计报告5. 框图 基于STM32的LCD1602显示Proteus仿真设计(仿真程序设计报告讲解视频&#xff09; 仿真图proteus 8.9 程序编译器&#xff1a;keil 5 编程语言&#xff1a…

ArcGIS/QGIS按掩膜提取或栅格裁剪后栅格数据的值为什么变了?

问题描述&#xff1a; 现有一栅格数据&#xff0c;使用ArcGIS或者QGIS按照矢量边界进行按掩膜提取或者栅格裁剪以后&#xff0c;其值的范围发生了变化&#xff0c;如下&#xff1a; 可以看到&#xff0c;不论是按掩膜提取还是进行栅格裁剪后&#xff0c;其值的范围均与原来栅…

CKA认证 | Day1 k8s核心概念与集群搭建

第一章 Kubernetes 核心概念 1、主流的容器集群管理系统 容器编排系统&#xff1a; KubernetesSwarmMesos Marathon 2、Kubernetes介绍 Kubernetes是Google在2014年开源的一个容器集群管理系统&#xff0c;Kubernetes简称K8s。 Kubernetes用于容器化应用程序的部署&#x…

Nat Med病理AI系列|基础模型Virchow在病理学中的应用·顶刊精析·24-11-09

小罗碎碎念 今天是Nature Medicine病理AI系列的最后一篇文章&#xff0c;标题为A foundation model for clinical-grade computational pathology and rare cancers detection。 这篇文章介绍了一个大型病理基础模型Virchow&#xff0c;它在计算病理学领域实现了对常见和罕见癌…

vue3 + element-plus 的 upload + axios + django 文件上传并保存

之前在网上搜了好多教程&#xff0c;一直没有找到合适自己的&#xff0c;要么只有前端部分没有后端&#xff0c;要么就是写的不是很明白。所以还得靠自己摸索出来后&#xff0c;来此记录一下整个过程。 其实就是不要用默认的 action&#xff0c;要手动实现上传方式 http-reque…

多模态数字人AI产品正在革新金融业,解密头部银行、证券公司都在用的AI工具

在人工智能迅猛发展的时代背景下&#xff0c;金融业正迎来一场深刻的变革。 多模态的人工智能&#xff0c;以其独特的魅力&#xff0c;正在重塑金融行业的格局&#xff0c;为金融服务带来前所未有的新想象。从今年以来行业对AI技术的探索与实践中&#xff0c;AIGC 3D数字人多模…

数据仓库还是数据集市?这俩怎么选?

数据仓库和数据集市作为支持决策分析的两种不同方式&#xff0c;根据各自的特点和优势&#xff0c;有不同的应用场景&#xff0c;今天就来探讨下数据集市和数据仓库该怎么选&#xff1f; 一、数据集市和数据仓库对比 1、数据集市与数据仓库的关系&#xff1a; 1&#xff09;数…

2024年数据分析5大趋势

在快速发展和创新的数据分析领域&#xff0c;2024 年有望成为突破性趋势的一年&#xff0c;这些趋势将重新定义企业从数据中提取洞察的方式。 下文将分析2024 年 5 大数据分析趋势&#xff0c;揭示将塑造数据驱动决策未来的工具和策略。 趋势一&#xff1a;人工智能落地将成为…

AI帮你记住所有密码,你敢把隐私交给它吗?

数字时代的密码管理挑战 在这个信息爆炸的数字时代&#xff0c;每个人都面临着前所未有的密码管理挑战。随着我们在网上进行越来越多的活动&#xff0c;从购物到社交&#xff0c;再到网上银行&#xff0c;所需的密码数量也随之激增。每个账户需要独特且复杂的组合&#xff0c;…

[Meachines] [Medium] MonitorsThree SQLI+Cacti-CMS-RCE+Duplicati权限提升

信息收集 IP AddressOpening Ports10.10.11.30TCP:22&#xff0c;80 $ nmap -p- 10.10.11.30 --min-rate 1000 -sC -sV -Pn PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) | …

springboot牛奶预定系统-计算机设计毕业源码70299

摘要 在当今社会&#xff0c;随着人们对健康和营养需求的不断增长&#xff0c;牛奶作为重要的营养食品备受青睐。然而&#xff0c;传统的牛奶预定方式存在着诸多不便和限制&#xff0c;如需要到实体店购买或电话预定等&#xff0c;导致消费者体验不佳。因此&#xff0c;开发一款…

Windows 局域网IP扫描工具:IPScaner 轻量免安装

IPScaner是一款258KB的工具&#xff0c;具备快捷修改IP、批量扫描、地址计算等功能&#xff0c;自动识别本机IP网段&#xff0c;快速查看IP使用情况&#xff0c;适用于监控维护、企业IT运维等场 软件功能介绍&#xff1a; 1&#xff09;快捷修改本地IP、IP批量扫描、IP地址计算…

基于java校园招聘管理系统的设计与实现

一、环境信息 开发语言&#xff1a;JAVA JDK版本&#xff1a;JDK8及以上 数据库&#xff1a;MySql5.6及以上 Maven版本&#xff1a;任意版本 操作系统&#xff1a;Windows、macOS 开发工具&#xff1a;Idea、Eclipse、MyEclipse 开发框架&#xff1a;SpringbootHTMLjQueryMysq…

后端Node学习项目-项目基础搭建

前言 各位好&#xff0c;我是前端SkyRain。最近为了响应公司号召&#xff0c;开始对后端知识的学习&#xff0c;作为纯粹小白&#xff0c;记录下每一步的操作流程。 项目仓库&#xff1a;https://gitee.com/sky-rain-drht/drht-node 因为写了文档&#xff0c;代码里注释不是很…