【C语言】数据结构——无头单链表实例探究

news2024/12/23 15:24:20

💗个人主页💗
⭐个人专栏——数据结构学习⭐
💫点击关注🤩一起学习C语言💯💫

目录

  • 导读:
  • 1. 单链表
    • 1.1 什么是单链表
    • 1.2 优缺点
  • 2. 实现单链表基本功能
    • 2.1 定义结构体
    • 2.2 单链表打印
    • 2.3 销毁单链表
    • 2.4 动态申请一个结点
    • 2.5 单链表尾插
    • 2.6 单链表尾删
    • 2.7 单链表头插
    • 2.8 单链表头删
    • 2.9 单链表查找
    • 2.10 单链表任意插入
    • 2.11 单链表任意删除
  • 3. 代码整理
    • 3.1 SList.h声明函数
    • 3.2 SList.c定义函数
    • 3.3 study.c调用
  • 4. 博主有话说

导读:

在前面我们已经学习了顺序表,今天我们来学习链表的单链表,也是无头的单链表,这需要对一级指针和二级指针有充分的了解。

1. 单链表

1.1 什么是单链表

单链表是一种常见的数据结构,由一系列节点依次连接形成。
每个节点包含两部分信息:数据信息指向下一个节点的指针
单链表的第一个节点称为头节点,最后一个节点没有下一个节点,其指针指向空。
类似于火车,火车头连接后一个车厢,再由后面的车厢依次连接
在这里插入图片描述

图1.1

1.2 优缺点

单链表的优点:

  1. 动态性:单链表的长度可以动态增长,不需要预先指定长度;
  2. 内存利用率高:链表中每个节点只需要存储下一个节点的地址,不需要像数组那样存储固定大小的位置,因此可以更加灵活地利用内存;
  3. 插入和删除操作方便:由于只需要改变链表节点中的指针,可以很方便地在链表中插入和删除节点。

单链表的缺点:

  1. 随机访问困难:由于必须从头节点开始遍历整个链表才能访问任意位置上的节点,因此随机访问效率较低;
  2. 存储空间浪费:由于链表节点中需要保存指向下一个节点的指针,因此需要额外的存储空间;
  3. 不支持反向遍历:由于链表节点只存储了指向下一个节点的指针,因此无法反向遍历链表。

2. 实现单链表基本功能

我们需要创建两个 C文件: study.c 和 SList.c,以及一个 头文件: SList.h。
头文件来声明函数,一个C文件来定义函数,另外一个C文件来用于主函数main()进行测试。

2.1 定义结构体

typedef是类型定义的意思。typedef struct 是为了使用这个结构体方便。

若struct SeqList {}这样来定义结构体的话。在申请SeqList 的变量时,需要这样写,struct SList n;
若用typedef,可以这样写,typedef struct SList{}SL; 。在申请变量时就可以这样写,SL n;
区别就在于使用时,是否可以省去struct这个关键字。

SList.h声明函数

//给int类型起一个别名——SLNDataType
typedef int SLNDataType;
typedef struct SListNode
{
	SLNDataType val;
	struct SListNode* next;
}SLNode;

2.2 单链表打印

SeqList.h声明函数

// 单链表打印
void SLTPrint(SLNode* phead);

SList.c定义函数

//打印结构体
void SLTPrint(SLNode* phead)
{
	SLNode* cur = phead;//指向头节点
	while (cur != NULL)
	{
		printf("%d-> ", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}

2.3 销毁单链表

动态开辟的空间用完之后都需要释放,以防后面出现问题。
SList.h声明函数

//单链表销毁
void SLTDestroy(SLNode** pphead);

SList.c定义函数

//单链表销毁
void SLTDestroy(SLNode** pphead)
{
	assert(pphead);
	SLNode* cur = *pphead;
	SLNode* prev = NULL;
	while (cur != NULL)
	{
		prev = cur->next;
		free(cur);
		cur = prev;
	}
	*pphead = NULL;
}

2.4 动态申请一个结点

无论在链表头部、尾部还是任意位置插入一个节点,都需要开辟一个节点,每个插入函数里都要写开辟节点的函数会重复,为了方便,我们单独定义一个函数用来开辟新节点,每次只需调用即可。
SList.c定义函数

SLNode* CreateNode(SLNDataType x)
{
	//让指针newnode指向malloc开辟的新空间
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)//开辟失败则返回错误信息
	{
		perror("malloc fail");
		exit(-1);
	}
	//对结构体成员解引用,改变其值
	newnode->val = x;
	//让next指向空
	newnode->next = NULL;
	return newnode;
}

2.5 单链表尾插

思路:
新建一个节点,让链表最后一个节点的next指向新节点
在这里插入图片描述

SList.h声明函数

// 单链表尾插
void SLTPushBack(SLNode** pphead, SLNDataType x);

SList.c定义函数
如果这个链表中没有任何节点,只需让头部指针plist直接指向newnode。
需要注意的一点是,plist是一级指针,我们想改变plist,就要用二级指针来接收plist的地址,这样才能改变plist的指向

// 单链表尾插
void SLTPushBack(SLNode** pphead, SLNDataType x)
{
	assert(pphead);

	SLNode* newnode = CreateNode(x);
	//如果开头为空,则直接指向CreateNode()函数开辟的空间,完成尾插
	if (*pphead == NULL)
	{
		*pphead = newnode;
		//改变外部结构体指针Node*,要用Node**
	}
	else
	{
		//找尾
		SLNode* tail = *pphead;
		//如果结构体成员next指向的不是空指针
		while (tail->next != NULL)
		{
			//让tail指向下一个节点
			tail = tail->next;
		}
		//再让tail节点的next指向新开辟的空间,完成尾插
		tail->next = newnode;
	}
}

在这里插入图片描述

study.c调用

//测试尾插和尾删
void TestSLT1()
{
	SLNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);

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

int main()
{
	TestSLT1();
	return 0;
}

在这里插入图片描述

2.6 单链表尾删

找到倒数第二个节点,让其next指向NULL,用free()释放那个最后一个节点。
如果只有一个节点,直接释放头节点即可。
在这里插入图片描述

SList.h声明函数

// 单链表尾删
void SLTPopBack(SLNode** pphead);

SList.c定义函数

// 单链表尾删
void SLTPopBack(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	//只有一个节点时
	if ((*pphead)->next == NULL)
	{
		//直接释放
		free(*pphead);
		*pphead = NULL;
	}
	//多个节点
	else
	{
		//tail指向开头
		SLNode* tail = *pphead;
		//再定义一个空指针
		SLNode* prev = NULL;
		//next成员指向的下一节点不为空
		while (tail->next != NULL)
		{
			//让prev指向tail所指向的空间
			prev = tail;
			//tail指向下一个节点
			tail = tail->next;
		}
		//循环结束,tail指向的为空,释放空间
		free(tail);
		//再让prev指向的结构体内的next成员指向NULL,完成尾删
		prev->next = NULL;
	}
}

在这里插入图片描述

study.c调用

//测试尾插和尾删
void TestSLT1()
{
	SLNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);

	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTDestroy(&plist);
	SLTPrint(plist);
}
int main()
{
	TestSLT1();
	return 0;
}

在这里插入图片描述

2.7 单链表头插

让头节点plist指向新开辟的节点,再让新开辟节点的next指向之前的第一个节点。
在这里插入图片描述

SList.h声明函数

//单链表头插
void SLTPushFront(SLNode** pphead, SLNDataType x);

SList.c定义函数

// 单链表头插
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
	assert(pphead);
	//让* newnode指向CreateNode()函数开辟的新空间
	SLNode* newnode = CreateNode(x);
	//让新开辟的节点内的next成员指向链表开头的节点
	newnode->next = *pphead;
	//再重新让之前的头节点指向新开辟的节点,完成头插
	*pphead = newnode;
}

在这里插入图片描述

study.c调用

//测试头插和头删
void TestSLT2()
{
	SLNode* plist = NULL;
	SLTPushFront(&plist, 10);
	SLTPushFront(&plist, 20);
	SLTPushFront(&plist, 30);
	SLTPushFront(&plist, 40);
	SLTPrint(plist);

	SLTDestroy(&plist);
}
int main()
{
	TestSLT2();
	return 0;
}

在这里插入图片描述

2.8 单链表头删

让plist指向第二个节点,释放第一个节点。
在这里插入图片描述

SList.h声明函数

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

SList.c定义函数

// 单链表头删
void SListPopFront(SLNode** pphead)
{
	assert(*pphead);
	//tail指向开头
	SLNode* tail = *pphead;
	//让头节点指针指向下一个节点
	*pphead = (*pphead)->next;
	//把第一个节点空间释放,完成头删
	free(tail);
	tail = NULL;
}

study.c调用

//测试头插和头删
void TestSLT2()
{
	SLNode* plist = NULL;
	SLTPushFront(&plist, 10);
	SLTPushFront(&plist, 20);
	SLTPushFront(&plist, 30);
	SLTPushFront(&plist, 40);
	SLTPrint(plist);

	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);

	SLTDestroy(&plist);
}
int main()
{
	TestSLT2();
	return 0;
}

在这里插入图片描述

2.9 单链表查找

想要查找链表里的val里是否存入有一个值,遍历链表,查看每个节点的val值,找到则返回该节点的地址,找不到返回-1,具体的作用我们到后面应用。
在这里插入图片描述

SList.h声明函数

// 单链表查找
SLNode* SListFind(SLNode* pphead, SLNDataType x);

SList.c定义函数

SLNode* SListFind(SLNode* phead, SLNDataType x)
{
	SLNode* cur = phead;
	while (cur)
	{
		if (cur->val == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

2.10 单链表任意插入

单链表的插入不止是头插和尾插,可以在任意位置插入。
比如我们在链表中一个数值前插入节点,就可以利用单链表查找来找到这个数,返回其节点的位置,然后在该位置插入节点。
在这里插入图片描述

如果pos位置刚好在第一个节点,就是头插,直接调用之前的头插函数即可

SList.h声明函数

void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);

SList.c定义函数

void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);
	
	//单节点
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	//多节点
	else
	{
		SLNode* tail = *pphead;
		while (tail->next != pos)
		{
			tail = tail->next;
		}
		SLNode* newnode = CreateNode(x);
		tail->next = newnode;
		newnode->next = pos;
	}
}

在这里插入图片描述

study.c调用

void TestSLT3()
{
	SLNode* plist = NULL;
	SLTPushBack(&plist, 10);
	SLTPushBack(&plist, 20);
	SLTPushBack(&plist, 30);
	SLTPushBack(&plist, 40);
	SLTPrint(plist);

	SLNode* pos = SListFind(plist, 30);

	if (pos != NULL)
	{
		SLTInsert(&plist, pos, 3);
		SLTPrint(plist);
	}
	SLTDestroy(&plist);
}
int main()
{
	TestSLT3();
	return 0;
}

在这里插入图片描述

2.11 单链表任意删除

和任意插入差不多,如果pos位置在头部就是头删,直接调用即可。
SList.h声明函数

//单链表任意位置删除
void SLTErase(SLNode** pphead, SLNode* pos);

SList.c定义函数

//单链表任意位置删除
void SLTErase(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);

	SLNode* tail = *pphead;
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		while (tail->next != pos)
		{
			tail = tail->next;
		}
		tail->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

study.c调用

//单链表任意位置插入和删除
void TestSLT3()
{
	SLNode* plist = NULL;
	SLTPushBack(&plist, 10);
	SLTPushBack(&plist, 20);
	SLTPushBack(&plist, 30);
	SLTPushBack(&plist, 40);
	SLTPrint(plist);

	SLNode* pos = SListFind(plist, 30);
	if (pos != NULL)
	{
		SLTErase(&plist, pos);
	}
	SLTPrint(plist);
	SLTDestroy(&plist);
}

int main()
{
	TestSLT3();
	return 0;
}

在这里插入图片描述

3. 代码整理

3.1 SList.h声明函数

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

// 动态申请一个结点
typedef int SLNDataType;
typedef struct SListNode
{
	SLNDataType val;
	struct SListNode* next;
}SLNode;

// 单链表打印
void SLTPrint(SLNode* phead);
//单链表销毁
void SLTDestroy(SLNode** pphead);
// 单链表尾插
void SLTPushBack(SLNode** pphead, SLNDataType x);
//单链表头插
void SLTPushFront(SLNode** pphead, SLNDataType x);
// 单链表尾删
void SLTPopBack(SLNode** pphead);
// 单链表头删
void SLTPopFront(SLNode** pphead);
// 单链表查找
SLNode* SListFind(SLNode* pphead, SLNDataType x);

// 单链表任意位置插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);
//单链表任意位置删除
void SLTErase(SLNode** pphead, SLNode* pos);


void SLTInsertAfter(SLNode* pos, SLNDataType x);
void SLTEraseAfter(SLNode* pos);

3.2 SList.c定义函数

#include "SList.h"

//打印结构体
void SLTPrint(SLNode* phead)
{
	SLNode* cur = phead;//指向头节点
	while (cur != NULL)
	{
		printf("%d-> ", cur->val);
		cur = cur->next;
	}
	printf("NULL\n");
}

//单链表销毁
void SLTDestroy(SLNode** pphead)
{
	assert(pphead);
	SLNode* cur = *pphead;
	SLNode* prev = NULL;
	while (cur != NULL)
	{
		prev = cur->next;
		free(cur);
		cur = prev;
	}
	*pphead = NULL;
}

SLNode* CreateNode(SLNDataType x)
{
	//让指针newnode指向malloc开辟的新空间
	SLNode* newnode = (SLNode*)malloc(sizeof(SLNode));
	if (newnode == NULL)//开辟失败则返回错误信息
	{
		perror("malloc fail");
		exit(-1);
	}
	//对结构体成员解引用,改变其值
	newnode->val = x;
	//让next指向空
	newnode->next = NULL;
	return newnode;
}
// 单链表尾插
void SLTPushBack(SLNode** pphead, SLNDataType x)
{
	assert(pphead);

	SLNode* newnode = CreateNode(x);
	//如果开头为空,则直接指向CreateNode()函数开辟的空间,完成尾插
	if (*pphead == NULL)
	{
		*pphead = newnode;
		//改变外部结构体指针Node*,要用Node**
	}
	else
	{
		//找尾
		SLNode* tail = *pphead;
		//如果结构体成员next指向的不是空指针
		while (tail->next != NULL)
		{
			//让tail指向下一个节点
			tail = tail->next;
		}
		//再让tail节点的next指向新开辟的空间,完成尾插
		tail->next = newnode;
	}
}

// 单链表尾删
void SLTPopBack(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	//只有一个节点时
	if ((*pphead)->next == NULL)
	{
		//直接释放
		free(*pphead);
		*pphead = NULL;
	}
	//多个节点
	else
	{
		//tail指向开头
		SLNode* tail = *pphead;
		//再定义一个空指针
		SLNode* prev = NULL;
		//next成员指向的下一节点不为空
		while (tail->next != NULL)
		{
			//让prev指向tail所指向的空间
			prev = tail;
			//tail指向下一个节点
			tail = tail->next;
		}
		//循环结束,tail指向的为空,释放空间
		free(tail);
		//再让prev指向的结构体内的next成员指向NULL,完成尾删
		prev->next = NULL;
	}

}

// 单链表头插
void SLTPushFront(SLNode** pphead, SLNDataType x)
{
	assert(pphead);

	//让* newnode指向CreateNode()函数开辟的新空间
	SLNode* newnode = CreateNode(x);
	//让新开辟的节点内的next成员指向链表开头的节点
	newnode->next = *pphead;
	//再重新让之前的头节点指向新开辟的节点,完成头插
	*pphead = newnode;
}


// 单链表头删
void SLTPopFront(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	//tail指向开头
	SLNode* tail = *pphead;
	//让头节点指针指向下一个节点
	*pphead = (*pphead)->next;
	//把第一个节点空间释放,完成头删
	free(tail);
	tail = NULL;
}

// 单链表查找
SLNode* SListFind(SLNode* phead, SLNDataType x)
{
	SLNode* cur = phead;
	while (cur)
	{
		if (cur->val == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

// 单链表任意位置插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);
	//单节点
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	//多节点
	else
	{
		SLNode* tail = *pphead;
		while (tail->next != pos)
		{
			tail = tail->next;
		}
		SLNode* newnode = CreateNode(x);
		tail->next = newnode;
		newnode->next = pos;
	}
}

//单链表任意位置删除
void SLTErase(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(pos);
	assert(*pphead);

	SLNode* tail = *pphead;
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		while (tail->next != pos)
		{
			tail = tail->next;
		}
		tail->next = pos->next;
		free(pos);
		pos = NULL;
	}
}


void SLTInsertAfter(SLNode* pos, SLNDataType x)
{
	assert(pos);
	SLNode* newnode = CreateNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
void SLTEraseAfter(SLNode* pos)
{
	assert(pos);
	assert(pos->next);

	SLNode* tmp = pos->next;
	pos->next = pos->next->next;
	free(tmp);
	tmp = NULL;
}

3.3 study.c调用

#define _CRT_SECURE_NO_WARNINGS 
#include "SList.h"
//每个节点的地址没有关联,是随机的,东一个,西一个

//想要改变int*,传的就要是int**

//测试尾插和尾删
void TestSLT1()
{
	SLNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);

	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTDestroy(&plist);
	SLTPrint(plist);

}
//测试头插和头删
void TestSLT2()
{
	SLNode* plist = NULL;
	SLTPushFront(&plist, 10);
	SLTPushFront(&plist, 20);
	SLTPushFront(&plist, 30);
	SLTPushFront(&plist, 40);
	SLTPrint(plist);

	SLTPopFront(&plist);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);

	SLTDestroy(&plist);
	
}

//单链表任意位置插入和删除
void TestSLT3()
{
	SLNode* plist = NULL;
	SLTPushBack(&plist, 10);
	SLTPushBack(&plist, 20);
	SLTPushBack(&plist, 30);
	SLTPushBack(&plist, 40);
	SLTPrint(plist);

	SLNode* pos = SListFind(plist, 30);

	/*if (pos != NULL)
	{
		SLTInsert(&plist, pos, 3);
		SLTPrint(plist);
	}
	SLTDestroy(&plist);*/


	if (pos != NULL)
	{
		SLTErase(&plist, pos);
	}
	SLTPrint(plist);
	SLTDestroy(&plist);
}
int main()
{
	//TestSLT1();
	//TestSLT2();
	TestSLT3();
	return 0;
}

4. 博主有话说

有关无头单链表的内容就分享到这里,更多有关内容关注博主,有问题可以留言和博主讨论。

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

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

相关文章

【有源码】基于uniapp的农场管理小程序springboot基于微信小程序的农场检测系统(源码 调试 lw 开题报告ppt)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

10 路由协议:西出网关无故人,敢问路在何方

1.网络包出了网关之后&#xff0c;就有了一种漂泊的悲凉感 2.之前的场景是比较简单的场景&#xff0c;但是在实际生产环境下&#xff0c;出了网关&#xff0c;会面临着很多路由器&#xff0c;有很多条道路可以选。 3、如何配置路由&#xff1f; 路由表的设计 1.路由器就是一…

高校教务系统登录页面JS分析——西安外国语大学教务系统

高校教务系统密码加密逻辑及JS逆向 本文将介绍高校教务系统的密码加密逻辑以及使用JavaScript进行逆向分析的过程。通过本文&#xff0c;你将了解到密码加密的基本概念、常用加密算法以及如何通过逆向分析来破解密码。 本文仅供交流学习&#xff0c;勿用于非法用途。 一、密码加…

学习笔记:CANOE模拟LIN主节点和实际从节点进行通信测试

先写点感想&#xff0c;在LIN开发阶段&#xff0c;我一般用图莫斯USB工具来进行模拟主机节点发送数据。后来公司买了CANOE工具就边学习边搭建了LIN的测试工程&#xff0c;网上的资料真的很少&#xff0c;主要是靠自己一点点摸索前进&#xff0c;总算入门。几个月后的今天&#…

网工内推 | 售后工程师,IP认证优先,最高15薪,年底有分红

01 威发系统&#xff08;中国&#xff09;有限公司 招聘岗位&#xff1a;售后工程师 职责描述&#xff1a; 1、负责各种规模的项目售后安装、调试和维护工作&#xff1b; 2、解决工程和维护中的一般技术问题&#xff0c;支持、协助处理其他相关的技术问题&#xff1b; 3、与…

如何像优秀测试人员那样思考?

优秀测试和普通测试之间的差别在于测试人员如何思考&#xff1a;测试人员的测试设计选择&#xff0c;解释所观察到的能力&#xff0c;以及非常令人信服地分析描述这些现象的能力。 然而&#xff0c;在实际工作中&#xff0c;我们更多的看到了测试人员在电脑前点点点&#xff0…

文件批量改名:轻松批量重命名快手素材文件,提高工作效率

文件名太长&#xff0c;文件太多有什么办法可以一键改名呢&#xff1f; 在日常繁琐的工作中&#xff0c;我们经常需要整理大量的文件&#xff0c;而为了更高效地管理和快速查找&#xff0c;对文件进行重命名显得尤为关键。然而&#xff0c;传统的手动逐个重命名方式不仅耗时&a…

算法学习打卡day41|栈和队列:栈和队列相互实现、括号匹配、逆波兰表达式、滑动窗口最大值问题、求前 K 个高频元素

栈和队列相互实现 力扣题目链接&#xff1a;用栈实现队列、用队列实现栈 题目描述&#xff1a; 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(…

2 任务2: 使用趋动云GPU进行猫狗识别实践

使用趋动云GPU进行猫狗识别实践 1 创建项目2 初始化开发环境3 调试代码4 提交离线任务5 结果集存储与下载 使用趋动云提供的免费GPU&#xff0c;进行猫狗识别实践。 虽然例程里面提供的是基于tensorflow的&#xff0c;但是你也可以使用pytorch的代码 使用这个平台的一个优点就是…

1-前端基本知识-CSS

1-前端基本知识-CSS 文章目录 1-前端基本知识-CSS总体概述什么是CSS&#xff1f;CSS引入方式行内式内嵌式连接式/外部样式表 CSS选择器元素选择器id选择器class选择器&#xff08;使用较广&#xff09; CSS浮动CSS定位静态定位&#xff1a;static绝对定位&#xff1a;absolute相…

“精准分割视频,误差降低至零——视频剪辑的新革命!”

在数字媒体的时代&#xff0c;视频剪辑已经成为我们日常生活和工作中不可或缺的一部分。无论是制作一部电影、剪辑一个纪录片&#xff0c;还是编辑一个家庭视频&#xff0c;我们都需要一个精准、高效的视频剪辑工具。今天&#xff0c;我们向您推荐一款全新的视频剪辑软件——精…

[NLP] 使用Llama.cpp和LangChain在CPU上使用大模型

一 准备工作 下面是构建这个应用程序时将使用的软件工具: 1.Llama-cpp-python 下载llama-cpp, llama-cpp-python [NLP] Llama2模型运行在Mac机器-CSDN博客 2、LangChain LangChain是一个提供了一组广泛的集成和数据连接器&#xff0c;允许我们链接和编排不同的模块。可以常…

关于卷积神经网络的步幅(stride)

认识步幅&#xff08;stride&#xff09; 卷积核从输入数组的最左上方开始&#xff0c;按从左往右、从上往下的顺序&#xff0c;依次在输入数组上滑动&#xff0c;我们将每次滑动的行数和列数称为步幅。 计算步幅 假设输入的形状n∗n&#xff0c;卷积核的形状为f∗f&#xff0…

域渗透06-协议(NTLM hash利用)

前言&#xff1a; 当我们获取到一台域内主机打算干什么&#xff0c;毫无疑问当然是拿域控&#xff0c;如果域控未发现漏洞应该怎么办&#xff0c;首先我们需要查看我们拿到主机的权限和在域中的组&#xff0c;如果本机权限够我们就需要利用工具抓取本机的hash&#xff0c;然后…

HCIE-CCE

1、创建集群 svc网络&#xff0c;10.247 pod网络&#xff0c;10.244 节点网络&#xff0c;192.168.66&#xff08;master和node一致&#xff09; 2、创建节点 上面集群选择了最新版本1.27&#xff0c;CCE从1.27版本开始不再支持docker容器引擎&#xff0c;仅支持containered&…

渗透实战靶机3wp

0x00 简介 目标IP&#xff1a;xxxx.95 测试IP&#xff1a;xxxx.96 测试环境&#xff1a;win10、kali等 测试时间&#xff1a;2021.7.23-2021.7.26 测试人员&#xff1a;ruanruan 0x01 信息收集 1、端口扫描 21&#xff0c;ftp&#xff0c;ProFTPD&#xff0c;1.3.3c22&a…

Oracle 安装及 Spring 使用 Oracle

参考内容&#xff1a; docker安装oracle数据库史上最全步骤&#xff08;带图文&#xff09; Mac下oracle数据库客户端 Docker安装Oracle docker能安装oracle吗 Batch script for add a auto-increased primary key for exist table with records Docker 安装 Oracle11g 注意&a…

基于单片机的甲醛检测器设计

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、系统硬件设计三、软件设计4.1 程序结构流程图原理图 四、结论五、 文章目录 概要 本文将要提…

餐饮软件开发价格受到需求的影响!

随着科技的进步和互联网的普及&#xff0c;餐饮行业也逐渐实现了数字化转型&#xff0c;越来越多的餐厅开始引入餐饮软件来提升服务质量、提高效率、拓展销售渠道等。 而对于许多初创餐厅或餐饮企业来说&#xff0c;开发一款适合自己的餐饮软件的价格一直是他们关注的焦点&…

NtripShare Caster高精度定位CORS服务软件

NtripShare CORS是NtripShare GNSS系列软件中最早的软件系统&#xff0c;也是NtripShare名称的起源。 所谓GNSS CORS服务系统一般构成&#xff1a; 1&#xff09;基准站网&#xff1a;由若干个分布合理的GNSS 基准站组成&#xff1b; 2&#xff09;数据传输系统&#xff1a;…