队列的实现(附含两道经典例题)

news2024/11/23 10:52:28

🍉文章主页:阿博历练记
📖文章专栏:数据结构与算法
🚍代码仓库:阿博编程日记
🍥欢迎关注:欢迎友友们点赞收藏+关注哦🌹

在这里插入图片描述

文章目录

    • 🌾前言
    • 🎬队列
      • 🔍1.队列的结构框架
      • 🔍2.队列的初始化
      • 👑为什么初始化不使用二级指针
      • 🔍3.队列的释放
      • 🔍4.队列的插入数据
      • 🔍5.队列的删除数据
      • 🔍6.队列取队头数据
      • 🔍7.队列取队尾数据
      • 🔍8.返回队列数据的个数
      • 🔍9.判断队列是否为空
      • 🚀Queue.h代码
      • 🛸Queue.c代码
      • 🛳Test.c代码
      • 🧋代码效果展示
      • 1.🖋题目描述
      • 💡逻辑分析
      • 🎥代码实现
      • 2.🖋题目描述
      • 💡逻辑分析
      • 🎥代码实现

🌾前言

友友们,上期阿博给大家介绍了栈的实现,今天阿博给大家介绍一种新的数据结构:队列.
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)的性质。
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
在这里插入图片描述
队列也可以使用数组链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低.
在这里插入图片描述
在这里插入图片描述

🎬队列

🔍1.队列的结构框架

typedef  int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType  data;
}QNode;
typedef struct  Queue
{
	QNode* phead;
	QNode* ptail;
	int  size;
}Queue;

⛳⛳友友们注意,这两个结构体不能合并到一起,因为它们所代表的意义不一样,第一个结构体是每一个结点的结构,第二个结构体代表的是这个队列的结构,它表示的是队列整体.

🔍2.队列的初始化

void  QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

👑为什么初始化不使用二级指针

⛳⛳这里有可能友友们会有疑问,我们初始化不是要改变phead指针和ptail指针,它们两个都是结构体指针,我们要改变它们,为什么不用二级指针呢?这里友友们注意了,phead指针和ptail指针又在一个新的结构体Queue里面放着,它们就属于这个结构体里面的成员,我们要改变它,只需要传这个新结构体的地址就可以访问并改变它们了.
这里阿博给友友们总结几种不用二级指针的方法:
⭐1.我们在函数外部定义一个同类型的指针,通过返回值的方式接收,这本质上是一个值拷贝(赋值)
⭐2.带哨兵位的头结点,它的本质是改变结构体里面的next指针,next指针属于结构体的成员,所以我们只需要传结构体的指针就可以访问到它了.
⭐3.把结构体指针重新放在一个结构体里面,这样它就属于这个结构体的成员了,我们只需要传这个结构体的地址就可以改变结构体指针了.

🔍3.队列的释放

1.保存下一结点的地址迭代释放

void  QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

2.保存当前结点的地址迭代释放

void  QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
	    QNode* del = cur;
		cur = cur->next;
		free(del);
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

🚩🚩友友们,这里要注意两个点:⭐1.如果保存当前结点的地址的话,我们就需要先让cur=cur->next往后迭代,然后在释放保留的那个地址,如果先释放的话,那么cur=cur->next这一步就会报错,此时cur已经被释放了,我们还在使用,它就是一个野指针.⭐2.如果保留下一结点地址的话,我们就需要先释放当前结点,在让cur=next往后进行迭代,如果我们先往后迭代的话,此时cur=next已经指向下一结点了,我们在把它释放,这样就会导致上一个结点没有释放和下次再使用cur就是野指针.🌈🌈

🔍4.队列的插入数据

void  QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->phead == NULL)
	{
		assert(pq->ptail == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

⛳⛳友友们注意,就算这里是首次插入数据,我们也不需要二级指针因为phead和ptail指针都在结构体里面放着,所以我们传这个结构体的指针就可以改变它们.

🔍5.队列的删除数据

错误案例

void  QueuePop(Queue* pq)
{
        assert(pq);
        QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
		pq->size--;
}	

在这里插入图片描述
代码纠正

void  QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//1个结点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead =pq->ptail= NULL;     //不能对同一动态开辟出来的空间进行多次free释放,这里我们释放完pq->phead之后,pq->ptail也已经被释放了,所以我们主要的目的就是把pq->phead和pq->ptail都置空
	}
	//多个结点
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

⛳⛳友友们注意,pq->phead和pq->ptail指向相同的结点,free(pq->phead)之后就已经把这块内存空间释放了,此时我们就不能再free(pq->ptail)了,因为动态开辟出来的空间不能进行多次free释放.

🔍6.队列取队头数据

QDataType  QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return  pq->phead->data;
}

这里我们需要断言队列不能为空,如果为空,pq->phead就是空指针,这时pq->phead->data就是对空指针的解引用,程序就会报错.

🔍7.队列取队尾数据

QDataType  QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return  pq->ptail->data;
}

🔍8.返回队列数据的个数

int   QueueSize(Queue* pq)
{
	assert(pq);
	return  pq->size;
}

🔍9.判断队列是否为空

bool  QueueEmpty(Queue* pq)
{
	assert(pq);
	return  pq->phead == NULL
		&&  pq->ptail == NULL;
}

🚀Queue.h代码

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

typedef  int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType  data;
}QNode;
typedef struct  Queue
{
	QNode* phead;
	QNode* ptail;
	int  size;
}Queue;
void  QueueInit(Queue*pq);
void  QueueDestroy(Queue* pq);
void  QueuePush(Queue* pq, QDataType x);
void  QueuePop(Queue* pq);
QDataType  QueueFront(Queue* pq);
QDataType  QueueBack(Queue* pq);
int   QueueSize(Queue* pq);
bool  QueueEmpty(Queue* pq);

🛸Queue.c代码

#define  _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
void  QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
void  QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
		/*QNode* del = cur;
		cur = cur->next;
		free(del);*/
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
void  QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->phead == NULL)
	{
		assert(pq->ptail == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
void  QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//1个结点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail=NULL;     //不能对同一动态开辟出来的空间进行多次free释放,这里我们释放完pq->phead之后,pq->ptail也已经被释放了,所以我们主要的目的就是把pq->phead和pq->ptail都置空
	}
	//多个结点
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}
QDataType  QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return  pq->phead->data;
}
QDataType  QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return  pq->ptail->data;
}
int   QueueSize(Queue* pq)
{
	assert(pq);
	return  pq->size;
}
bool  QueueEmpty(Queue* pq)
{
	assert(pq);
	return  pq->phead == NULL
		&&  pq->ptail == NULL;
}

🛳Test.c代码

#define  _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
#include<stdio.h>
TestQueue()
{
	Queue  q;
	QueueInit(&q);

	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestroy(&q);
	return  0;
}
int main()
{
	TestQueue();
	return  0;
}

🧋代码效果展示

在这里插入图片描述

1.🖋题目描述

在这里插入图片描述

💡逻辑分析

在这里插入图片描述

友友们,通过这里也可以看出我们的入栈顺序是1,2,3,我们的出栈顺序也是1,2,3.

🎥代码实现

typedef  int  STDataType;
typedef struct  Stack
{
	STDataType* a;
	int top;                    //top指向栈顶的位置
	int capacity;
}ST;

void  STInit(ST* pst);
void  STDestroy(ST* pst);
void  STPush(ST* pst,STDataType x);
STDataType  STTop(ST* pst);
void  STPop(ST* pst);
bool  STEmpty(ST* pst);
int   STSize(ST* pst);

void  STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->top = 0;    //如果我们初始化为0,top就指向栈顶元素的下一个位置,初始化为-1,top就是指向栈顶元素.
	pst->capacity = 0;
}
void  STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->capacity = pst->top = 0;
}
void  STPush(ST* pst, STDataType x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
		int newcapacity= pst->capacity==0 ? 4 : pst->capacity * 2 ;
		STDataType* tmp = (STDataType*)realloc(pst->a,newcapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	} 
	pst->a[pst->top] = x;
	pst->top++;
}
STDataType  STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	return pst->a[pst->top - 1];
}
bool  STEmpty(ST* pst)
{
	assert(pst);
	return  pst->top == 0;
}
void  STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));
	pst->top--;
}
int   STSize(ST* pst)
{
	assert(pst);
	return   pst->top;
}

typedef struct {
  ST  pushst;
  ST  popst;
} MyQueue;


MyQueue* myQueueCreate() {
   MyQueue*obj=(MyQueue*)malloc(sizeof(MyQueue));
   if(obj==NULL)
   {
       perror("malloc fail");
       return;
   }
   STInit(&obj->pushst);
   STInit(&obj->popst);
   return   obj;
}

void myQueuePush(MyQueue* obj, int x) {
   STPush(&obj->pushst,x);
}

int myQueuePop(MyQueue* obj) {
   int  front=myQueuePeek(obj);
   STPop(&obj->popst);
   return  front;
}

int myQueuePeek(MyQueue* obj) {
  if(STEmpty(&obj->popst))
  {
      while(!STEmpty(&obj->pushst))
      {
      STPush(&obj->popst,STTop(&obj->pushst));
      STPop(&obj->pushst);
      }
  }
  return  STTop(&obj->popst);
}

bool myQueueEmpty(MyQueue* obj) {
return  (STEmpty(&obj->pushst))
        &&(STEmpty(&obj->popst));
}

void myQueueFree(MyQueue* obj) {
   STDestroy(&obj->popst);
   STDestroy(&obj->pushst);
   free(obj);
}

2.🖋题目描述

在这里插入图片描述

💡逻辑分析

在这里插入图片描述

🎥代码实现

typedef  int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType  data;
}QNode;
typedef struct  Queue
{
	QNode* phead;
	QNode* ptail;
	int  size;
}Queue;
void  QueueInit(Queue*pq);
void  QueueDestroy(Queue* pq);
void  QueuePush(Queue* pq, QDataType x);
void  QueuePop(Queue* pq);
QDataType  QueueFront(Queue* pq);
QDataType  QueueBack(Queue* pq);
int   QueueSize(Queue* pq);
bool  QueueEmpty(Queue* pq);
void  QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
void  QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
		/*QNode* del = cur;
		cur = cur->next;
		free(del);*/
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
void  QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->phead == NULL)
	{
		assert(pq->ptail == NULL);
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
void  QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	//1个结点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead =pq->ptail= NULL;     //不能对同一动态开辟出来的空间进行多次free释放,这里我们释放完pq->phead之后,pq->ptail也已经被释放了,所以我们主要的目的就是把pq->phead和pq->ptail都置空
	}
	//多个结点
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}
QDataType  QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return  pq->phead->data;
}
QDataType  QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return  pq->ptail->data;
}
int   QueueSize(Queue* pq)
{
	assert(pq);
	return  pq->size;
}
bool  QueueEmpty(Queue* pq)
{
	assert(pq);
	return  pq->phead == NULL
		&&  pq->ptail == NULL;
}


typedef struct {
   Queue  p;
   Queue  q;
} MyStack;


MyStack* myStackCreate() {
  MyStack*obj=(MyStack*)malloc(sizeof(MyStack));
  if(obj==NULL)
  {
      perror("malloc fail");
      return;
  }
  QueueInit(&obj->p);
  QueueInit(&obj->q);
   return  obj;
}

void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty(&obj->q))
    {
        QueuePush(&obj->q,x);
    }
    else
    {
        QueuePush(&obj->p,x);
    }
}

int myStackPop(MyStack* obj) {
      Queue* NoFull=&obj->p;
      Queue*  Full=&obj->q;
      if(QueueEmpty(&obj->p))
      {
          Full=&obj->p;
          NoFull=&obj->q;
      }
      while(QueueSize(NoFull)>1)
      {
          QueuePush(Full,QueueFront(NoFull));
          QueuePop(NoFull);
      }
      int top=QueueBack(NoFull);
      QueuePop(NoFull);
      return  top;
}

int myStackTop(MyStack* obj) {
   if(!QueueEmpty(&obj->p))
   {
       return  QueueBack(&obj->p);
   }
   else
   {
        return  QueueBack(&obj->q);
   }
}

bool myStackEmpty(MyStack* obj) {
return   QueueEmpty(&obj->p)
         &&QueueEmpty(&obj->q);
}

void myStackFree(MyStack* obj) {
   QueueDestroy(&obj->p);
   QueueDestroy(&obj->q);
   free(obj);
}

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

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

相关文章

探索Windows CMD命令的经典应用:实用技巧大揭秘,值得收藏

文章目录 导语&#xff1a;第一部分&#xff1a;CMD命令基础1. ipconfig&#xff1a;获取网络配置信息2. ping&#xff1a;测试网络连接3. dir&#xff1a;查看目录内容4. telnet&#xff1a;远程登录和测试网络服务 第二部分&#xff1a;进程管理5. tasklist&#xff1a;查看正…

10-Docker发布微服务

文章目录 搭建SpringBoot项目发布微服务项目到Docker容器 搭建SpringBoot项目 搭建一个简单的SpringBoot项目&#xff1a; 创建maven工程&#xff0c;pom为&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://m…

【Java多线程案例】单例模式

本期讲解单例模式的饿汉模式与懒汉模式&#xff0c;以及如何解决懒汉模式造成线程的不安全问题。 目录 什么是单例模式&#xff1f; 1. 饿汉模式 2. 懒汉模式 2.1 懒汉模式单线程版 2.2 懒汉模式多线程版 3. 解决懒汉模式不安全问题 3.1 保证原子性 3.2 防止指令重排序…

MAC移动硬盘无法识别解决方案(超详细图文教程)

目录 步骤1&#xff1a;先找到外接移动硬盘&#xff0c;在 Mac “终端”执行命令 步骤2&#xff1a;加载移动硬盘&#xff0c;可以在终端执行以下两个命令之一 步骤3&#xff1a;终止后台自动执行的“磁盘修复进程” 步骤4&#xff1a;手动执行磁盘修复命令 步骤5&#xf…

lwIP更新记01:全局互斥锁替代消息机制

从 lwIP-2.0.0 开始&#xff0c;在 opt.h 中多了一个宏开关 LWIP_TCPIP_CORE_LOCKING&#xff0c;默认使能。这个宏用于启用 内核锁定 功能&#xff0c;使用 全局互斥锁 实现。在之前&#xff0c;lwIP 使用 消息机制 解决 lwIP 内核线程安全问题。消息机制易于实现&#xff0c;…

基于Linux安装Docker

Docker官网&#xff1a;Docker Docs: How to build, share, and run applications | Docker Documentation 学习任何技术&#xff0c;一定要参考相应的官网学习&#xff0c;一定要参考官网学习&#xff01;&#xff01;&#xff01; 目录 一、环境准备 1.1 配置源 1.1.1 下载…

Vue+uniapp桃源婚恋交友APP 安卓小程序 nodejs java python

小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 开发模式&#xff1a;混合开发本文先提出了开发基于小程序的桃源婚恋交友APP系统的背景意义&#xff0c;然后通过功能性和非功能性分析阐述本系统的需求&#xff0c;然后进行系统设计。技术实现部分选择Jav…

c++ 11标准模板(STL) std::set(九)

定义于头文件 <set> template< class Key, class Compare std::less<Key>, class Allocator std::allocator<Key> > class set;(1)namespace pmr { template <class Key, class Compare std::less<Key>> using se…

vue参照企业微信日程写一个小组件

今天公司要求做日程 这体的话 和企业微信的日程功能挺想的 也没有找到特别好的工具 就直接自己手敲了一个 先看效果 因为样式使用 sass 写的 所以 项目中要引入 sass 感兴趣的 可以把代码拿去二开一下 <template><section class "skeletonPositioning"…

Java线程概述 (一)线程介绍

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;&#x1fa85;什么是程序 、进程、线程&#xff1f;&#x1fa85;线程的生命周期&#x1fa85;多线程&#x1fa85;守护者线程&#x1fa85;线程并行与并发&#x1fa85;死锁&#x1f…

机器学习项目实战-能源利用率 Part-4(模型构建)

博主前期相关的博客可见下&#xff1a; 机器学习项目实战-能源利用率 Part-1&#xff08;数据清洗&#xff09; 机器学习项目实战-能源利用率 Part-2&#xff08;探索性数据分析&#xff09; 机器学习项目实战-能源利用率 Part-3&#xff08;特征工程与特征筛选&#xff09; 这…

AList挂载工具安装搭建使用教程,快速访问多个网盘的资源(保姆级图文)

目录 1. 下载AList2. 命令行启动快速启动小技巧 3. 用户登录4. 添加阿里云网盘帐号5. 添加百度云网盘资源总结 欢迎关注 『发现你走远了』 博客&#xff0c;持续更新中 欢迎关注 『发现你走远了』 博客&#xff0c;持续更新中 软件功能&#xff1a;将多个网盘的资源聚合在一起&…

本地部署 privateGPT

本地部署 privateGPT 1. 什么是 privateGPT2. Github 地址3. 创建虚拟环境4. 部署 privateGPT5. 配置 .env6. 下载模型7. 将文件放入 source_documents 目录中8. 摄取所有数据9. 向本地文档提问 1. 什么是 privateGPT 利用 GPT 的强大功能&#xff0c;私密地与您的文档交互&am…

Flutter 笔记 | Flutter 中的路由、包、资源、异常和调试

路由管理 Flutter中的路由通俗的讲就是页面跳转。在Flutter中通过Navigator组件管理路由导航。并提供了管理堆栈的方法。如&#xff1a;Navigator.push和Navigator.pop Flutter中给我们提供了两种配置路由跳转的方式&#xff1a;1、基本路由&#xff0c; 2、命名路由 普通路…

详解c++STL—函数对象

目录 1、函数对象 1.1、函数对象概念 1.2、函数对象的使用 2、谓词 2.1、谓词概念 2.2、一元谓词 2.3、二元谓词 3、内建函数对象 3.1、理解内建函数对象 3.2、算术仿函数 3.3、关系仿函数 3.4、逻辑仿函数 1、函数对象 1.1、函数对象概念 概念&#xff1a; 重载…

数据结构第三天 【二叉搜索树】

这道题真是写的我想吐了&#xff0c;主要是函数太多&#xff0c;排错太难了&#xff0c;搞了两个小时&#xff0c;基本就是在排错&#xff0c;排了一个小时&#xff0c;后面自己心态也有点崩溃了&#xff0c;其实不是一道很难的题&#xff0c;但是是一个非常麻烦的题目&#xf…

使用Serv-U搭建FTP服务器并公网访问

文章目录 1. 前言2. 本地FTP搭建2.1 Serv-U下载和安装2.2 Serv-U共享网页测试2.3 Cpolar下载和安装 3. 本地FTP发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 转载自内网穿透工具的文章&#xff1a;使用Serv-U搭建FTP服务器并公网访问【内网穿透】_ 1. 前言…

linux专题:GDB详细调试方法与实现

系列文章目录 例如&#xff1a;第一章 Linux-GDB 调试实验的使用 文章目录 目录 系列文章目录 文章目录 一、实验目的 二、实验现象 三、实验准备 四、Linux GDB调试实验流程 五、Linux GDB 调试器 总结 一、实验目的 掌握使用 gcc 分步编译 c 代码为可执行程序步骤以及 gc…

【数学建模】步长的选择(优化建模)

人们每天都在行走&#xff0c;排除以运动健身为目的的走路方式&#xff0c;而仅仅考虑距离固定&#xff0c;以节省体力为最终目的的行走&#xff0c;那么选择多大的步长才最省力&#xff1f; 人在走路时所做的功等于抬高人体重心所需的势能与两腿运动所需的动能之和。在给定速度…

又到520了,来画一朵抽搐的玫瑰花吧

文章目录 静态的玫瑰 敲了这么多年代码&#xff0c;每年都得画一些心啊花啊什么的&#xff0c;所以现在常规的已经有些倦怠了&#xff0c;至少也得来个三维图形才看着比较合理&#xff0c;而且光是三维的也没啥意思&#xff0c;最好再加上能动起来。 静态的玫瑰 网上有很多生…