DS:栈和队列的相互实现

                                                创作不易,感谢友友们三连!!

一、前言

        栈和队列的相互实现是用两个栈去实现队列或者是用两个队列去实现栈,这样其实是把问题复杂化的,实际中没有什么应用价值,但是通过他们的相互实现可以让我们更加深入地理解栈和队列的特点,而我也是用两道相关的OJ题来分析这个过程!

二、用两个队列实现栈

力扣:队列实现栈

2.1 思路

2.2 代码实现

2.2.1 队列的代码

我们先把队列的实现声明放这

Queue.h

#include<stdbool.h>
#include<assert.h>
typedef int QDatatype;//方便后面修改存储数据的数据类型
typedef struct QueueNode//队列结点的数据结构
{QDatatype data;//存储数据struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;//指向队头,用于出队(头删)QNode* ptail;//指向队尾,用于入队(尾插)int size;//记录有效元素个数
}Queue;//创建一个队列相关结构体
void QueueInit(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 QueueDestory(Queue* pq);//队列的销毁

2.2.2 结构体的创建

封装一个myStack结构体管理两个队列

typedef struct MyStack
{Queue q1;Queue q2;
} MyStack;
//用两个队列实现栈,构建一个结构体去管理这两个队列

2.2.3 初始化栈

初始化栈,相当于先构建栈的结构体变量,然后再初始化他的两个队列成员

MyStack* myStackCreate() //返回一个MySack类型的指针
{MyStack* obj = (MyStack*)malloc(sizeof(MyStack));if (obj == NULL){perror("malloc fail");exit(1);}
//如果开辟成功,对栈初始化相当于对栈结构体中的两个队列初始化QueueInit(&obj->q1);QueueInit(&obj->q2);return obj;
}

2.2.4 模拟压栈

按照我们之前的思路,压栈直接找到不为空的队列尾插就行

void myStackPush(MyStack* obj, int x)
//压栈前必须在不为空的队列中压栈
{if (!QueueEmpty(&obj->q1))//如果q1不为空压q1QueuePush(&obj->q1, x);else//如果q1为空,则压q2QueuePush(&obj->q2, x);
}

2.2.5 模拟出栈并返回栈顶元素

按照之前的思路,出栈就是先找到非空的队列,移除到空的队列上,只留下最后一个元素,再头删

int myStackPop(MyStack* obj)
//出栈,必须找到不为空的队列,然后将该队列的元素倒倒另一个队列中,知道只剩最后一个元素的时候,就删除
{//要找到不为空的队列进行操作,所以先假设一个为空一个不为空,如果给个条件判断Queue* Emptyq = &obj->q1;Queue* NonEmptyq = &obj->q2;if (!QueueEmpty(&obj->q1))//错了的话就认个错然后换回来{NonEmptyq = &obj->q1;Emptyq = &obj->q2;}//开始导数据while (QueueSize(NonEmptyq) > 1){//将队列头的元素倒进去另一个队列,在出栈QueuePush(Emptyq, QueueFront(NonEmptyq));//压栈QueuePop(NonEmptyq);//倒入一个就将非空队列出队列一个}//倒完后只剩一个数据了,先记录返回值再删除int top = QueueFront(NonEmptyq);QueuePop(NonEmptyq);return top;
}

2.2.6 获取栈顶元素

找到不为空的队列,然后获取队列尾的元素,就是获取栈顶元素

int myStackTop(MyStack* obj)
//找到不为空的队列,然后获取队列尾的元素,相当于获取栈顶元素
{if (!QueueEmpty(&obj->q1))//如果q1不为空取q1队尾return QueueBack(&obj->q1);else//如果q2不为空取q2队尾return QueueBack(&obj->q2);
}

2.2.7 判断栈是否为空

判断栈是否为空,本质上就是判断两个队列是否为空

bool myStackEmpty(MyStack* obj) //栈为空当且仅当两个队列都为空
{return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

2.2.8 释放栈

要先释放掉栈的队列成员的空间,然后再释放栈的空间,并置空

void myStackFree(MyStack* obj) 
{//释放obj前一定也要将q1和q2的空间释放了QueueDestory(&obj->q1);QueueDestory(&obj->q2);free(obj);//obj = NULL;没用,一级指针接受指针相当于值传递//要手动在main函数中置空
}

值得注意的是,这边给obj置空是没有的,要想改变obj必须用二级指针,所以我们最后要释放的话要在程序的最后自己手动释放。 

2.3 全部代码

2.3.1 queue.h

#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDatatype;//方便后面修改存储数据的数据类型
typedef struct QueueNode//队列结点的数据结构
{QDatatype data;//存储数据struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* phead;//指向队头,用于出队(头删)QNode* ptail;//指向队尾,用于入队(尾插)int size;//记录有效元素个数
}Queue;//创建一个队列相关结构体
void QueueInit(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 QueueDestory(Queue* pq);//队列的销毁

2.3.2 queue.c

#include"Queue.h"void QueueInit(Queue* pq)
{assert(pq);//判断传的是不是空指针pq->phead = pq->ptail = NULL;pq->size = 0;//因为队列不像栈一样,有一个top表示栈顶元素的下标//所以如果我们想知道这个队列的有效数据个数,就必须遍历队列//由于其先进先出的特性,我们默认只能访问到头元素和尾元素//所以必须访问一个头元素,就出队列一次,这样才能实现遍历//但是这样的代价太大了,为了方便,我们直接用size
}
void QueuePush(Queue* pq, QDatatype x)
{assert(pq);//入队必须从队尾入!QNode* newnode = (QNode*)malloc(sizeof(QNode));//创建一个新节点if (newnode == NULL)//如果新节点申请失败,退出程序{perror("malloc fail");}//新节点创建成功,给新节点初始化一下newnode->data = x;newnode->next = NULL;//开始入队//如果直接尾插的话,由于会用到ptail->next,所以得考虑队列为空的情况if (pq->ptail == NULL)//如果为空,直接把让新节点成为phead和ptail{//按道理来说,如果ptail为空,phead也应该为空// 但是有可能会因为我们的误操作使得phead不为空,这个时候一般是我们写错的问题//所以使用assert来判断一下,有问题的话会及时返回错误信息assert(pq->phead == 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));//队列中的出队列相当于链表的头删//如果直接头删,那么如果队列只有一个有效数据的话,那么我们将phead的空间释放掉,但是没有将ptail给置空//这样会导致ptail成为一个野指针,所以我们需要考虑只有一个节点多个节点的情况if (pq->phead->next == NULL)//一个节点的情况,直接将这个节点释放并置空即可{free(pq->phead);pq->phead = pq->ptail = NULL;//置空,防止野指针}else//多个节点的情况,直接头删{QNode* next = pq->phead->next;//临时指针记住下一个节点free(pq->phead);pq->phead = next;//让下一个节点成为新的头}pq->size--;
}QDatatype QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));//队列如果为空,则不可能找得到队列头元素//队列不为空的时候,直接返回phead指向的数据return pq->phead->data;
}QDatatype QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));//队列如果为空,则不可能找得到队尾元素//队列不为空的时候,直接返回ptail指向的数据return pq->ptail->data;
}int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}bool QueueEmpty(Queue* pq)//链表为空的情况,可以根据容量,也可以根据ptail==NULL&&phead==NULL
{assert(pq);return pq->phead == NULL && pq->ptail == NULL;
}void QueueDestory(Queue* pq)
{assert(pq);//判断传的是不是空指针//要逐个节点释放QNode* pcur = pq->phead;while (pcur){QNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}

2.3.3 mystack.h

#include"Queue.h"typedef struct MyStack
{Queue q1;Queue q2;
} MyStack;
//用两个队列实现栈,构建一个结构体去管理这两个队列MyStack* myStackCreate();//初始化栈
void myStackPush(MyStack* obj, int x);//压栈
int myStackPop(MyStack* obj);//出栈
int myStackTop(MyStack* obj);//获取栈顶元素
bool myStackEmpty(MyStack* obj);//判断栈是否为空
void myStackFree(MyStack* obj);//释放栈

2.3.4 mystack.c

#include"MyStack.h"MyStack* myStackCreate() //返回一个MySack类型的指针
{MyStack* obj = (MyStack*)malloc(sizeof(MyStack));if (obj == NULL){perror("malloc fail");exit(1);}
//如果开辟成功,对栈初始化相当于对栈结构体中的两个队列初始化QueueInit(&obj->q1);QueueInit(&obj->q2);return obj;
}void myStackPush(MyStack* obj, int x)
//压栈前必须在不为空的队列中压栈
{if (!QueueEmpty(&obj->q1))//如果q1不为空压q1QueuePush(&obj->q1, x);else//如果q1为空,则压q2QueuePush(&obj->q2, x);
}int myStackPop(MyStack* obj)
//出栈,必须找到不为空的队列,然后将该队列的元素倒倒另一个队列中,知道只剩最后一个元素的时候,就删除
{//要找到不为空的队列进行操作,所以先假设一个为空一个不为空,如果给个条件判断Queue* Emptyq = &obj->q1;Queue* NonEmptyq = &obj->q2;if (!QueueEmpty(&obj->q1))//错了的话就认个错然后换回来{NonEmptyq = &obj->q1;Emptyq = &obj->q2;}//开始导数据while (QueueSize(NonEmptyq) > 1){//将队列头的元素倒进去另一个队列,在出栈QueuePush(Emptyq, QueueFront(NonEmptyq));//压栈QueuePop(NonEmptyq);//倒入一个就将非空队列出队列一个}//倒完后只剩一个数据了,先记录返回值再删除int top = QueueFront(NonEmptyq);QueuePop(NonEmptyq);return top;
}int myStackTop(MyStack* obj)
//找到不为空的队列,然后获取队列尾的元素,相当于获取栈顶元素
{if (!QueueEmpty(&obj->q1))//如果q1不为空取q1队尾return QueueBack(&obj->q1);else//如果q2不为空取q2队尾return QueueBack(&obj->q2);
}bool myStackEmpty(MyStack* obj) //栈为空当且仅当两个队列都为空
{return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}void myStackFree(MyStack* obj) 
{//释放obj前一定也要将q1和q2的空间释放了QueueDestory(&obj->q1);QueueDestory(&obj->q2);free(obj);//obj = NULL;没用,一级指针接受指针相当于值传递//要手动在main函数中置空

三、用两个栈实现队列

力扣:用栈实现队列

3.1 思路

3.2 代码实现

3.2.1 栈的代码

这里先把栈的声明放这里

stack.h

#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>typedef int STDataType;
//支持动态增长的栈
typedef struct Stack
{STDataType* a;int top;//栈顶int capacity;//栈容量
}Stack;void StackInit(Stack* ps);//初始化栈
void StackPush(Stack* ps, STDataType x);//入栈
void StackPop(Stack* ps);//出栈
STDataType StackTop(Stack* ps);//获取栈顶元素
int StackSize(Stack* ps);//获取栈中有效元素个数
bool StackEmpty(Stack* ps);//检测栈是否为空,为空返回true
void StackDestory(Stack* ps);//销毁栈

3.2.2 结构体的创建

 根据前面的思路,一个栈专门用来入栈,一个栈专门用来出栈

typedef struct  MyQueue
{Stack Pushst;//专门用来入栈Stack Popst;//专门用来出栈
} MyQueue;MyQueue* myQueueCreate();//初始化队列
void myQueuePush(MyQueue* obj, int x);//模拟入队
int myQueuePop(MyQueue* obj);//模拟出队,并返回出去的头元素
int myQueuePeek(MyQueue* obj);//获取队列头元素并返回
bool myQueueEmpty(MyQueue* obj);//判断队列是否为空
void myQueueFree(MyQueue* obj);//销毁队列

3.2.3 初始化队列并返回节点

 初始化队列本质上就是对两个栈进行初始化

MyQueue* myQueueCreate() 
{MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));if (obj == NULL){perror("malloc fail");exit(1);}//对队列初始化其实就是对里面的两个栈初始化StackInit(&obj->Pushst);StackInit(&obj->Popst);return obj;
}

3.2.4 模拟入队

入队就很简单了,直接往第一个栈里面入栈就行

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

3.2.5 获取队列头的元素并返回

队列头的元素要在第二个栈去获取,但在获取前还得确保第二个栈有元素,如果没有,就得先把第一个栈倒过去

int myQueuePeek(MyQueue* obj)
//获取队列头元素有两种情况
//1、一种是popst不为空,直接返回其top下标的元素就行了
//2、一种是popst为空,这时候需要将pushst中的数据全部倒过去,再重复情况1
{if (StackEmpty(&obj->Popst)){while (!StackEmpty(&obj->Pushst)){//挪数据就是一边给popst入栈,一边给pushst出栈StackPush(&obj->Popst, StackTop(&obj->Pushst));StackPop(&obj->Pushst);}}return StackTop(&obj->Popst);
}

3.2.6 模拟出队,并返回出去的头元素

出队也要确保第二个栈不为空,但其实我们有了上面这个函数,直接调用一次就相当于是把我们把这个操作给完成了,所以我们直接接受上面那个函数的返回值然后直接pop就行

int myQueuePop(MyQueue* obj)
{
//跟myQueuePeek一样有两种情况,但是我们调用了这个函数相当于是帮我们判断过了,此时直接删就可以了int top = myQueuePeek(obj);StackPop(&obj->Popst);return top;
}

3.2.7 判断队列是否为空

队列是否为空,本质上就是两个栈是否为空

bool myQueueEmpty(MyQueue* obj) 
{return StackEmpty(&obj->Popst) && StackEmpty(&obj->Pushst);
}

3.2.8 销毁队列 

队列销毁,本质上就是两个栈的销毁,但是要在最后把队列的结构体的释放掉

void myQueueFree(MyQueue* obj) 
{StackDestory(&obj->Popst);StackDestory(&obj->Pushst);free(obj);//没用obj = NULL;
}

注意的是这边接受obj的是一级指针,相当于值传递,这里置空没什么意义,要在外面手动置空 

3.3 全部代码

3.3.1 stack.h

#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>typedef int STDataType;
//支持动态增长的栈
typedef struct Stack
{STDataType* a;int top;//栈顶int capacity;//栈容量
}Stack;void StackInit(Stack* ps);//初始化栈
void StackPush(Stack* ps, STDataType x);//入栈
void StackPop(Stack* ps);//出栈
STDataType StackTop(Stack* ps);//获取栈顶元素
int StackSize(Stack* ps);//获取栈中有效元素个数
bool StackEmpty(Stack* ps);//检测栈是否为空,为空返回true
void StackDestory(Stack* ps);//销毁栈

3.3.2 stack.c

#include"Stack.h"void StackInit(Stack* ps)
{ps->a = NULL;ps->top = ps->capacity = 0;
}void StackPush(Stack* ps, STDataType x)
{assert(ps);//判断是否需要扩容if (ps->top == ps->capacity){int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* temp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapacity);if (temp == NULL){perror("realloc fail");exit(1);}ps->a = temp;ps->capacity = newcapacity;}//压栈ps->a[ps->top++] = x;
}void StackPop(Stack* ps)
{assert(ps);//如果栈为空,则没有删除的必要assert(!StackEmpty(ps));ps->top--;
}STDataType StackTop(Stack* ps)
{assert(ps);//如果栈为空,不可能有栈顶元素assert(!StackEmpty(ps));return ps->a[ps->top - 1];
}int StackSize(Stack* ps)
{assert(ps);return ps->top;
}bool StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;
}void StackDestory(Stack* ps)
{free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}

3.3.3 myqueue.h

#include"Stack.h"typedef struct  MyQueue
{Stack Pushst;//专门用来入栈Stack Popst;//专门用来出栈
} MyQueue;MyQueue* myQueueCreate();//初始化队列并返回节点
void myQueuePush(MyQueue* obj, int x);//模拟入队
int myQueuePop(MyQueue* obj);//模拟出队,并返回出去的头元素
int myQueuePeek(MyQueue* obj);//获取队列头元素并返回
bool myQueueEmpty(MyQueue* obj);//判断队列是否为空
void myQueueFree(MyQueue* obj);//销毁队列

3.3.4 myqueue.c

#include"MyQueue.h"MyQueue* myQueueCreate() 
{MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));if (obj == NULL){perror("malloc fail");exit(1);}//对队列初始化其实就是对里面的两个栈初始化StackInit(&obj->Pushst);StackInit(&obj->Popst);return obj;
}void myQueuePush(MyQueue* obj, int x)
{StackPush(&obj->Pushst, x);
}int myQueuePop(MyQueue* obj)
{
//跟myQueuePeek一样有两种情况,但是我们调用了这个函数相当于是帮我们判断过了,此时直接删就可以了int top = myQueuePeek(obj);StackPop(&obj->Popst);return top;
}int myQueuePeek(MyQueue* obj)
//获取队列头元素有两种情况
//1、一种是popst不为空,直接返回其top下标的元素就行了
//2、一种是popst为空,这时候需要将pushst中的数据全部倒过去,再重复情况1
{if (StackEmpty(&obj->Popst)){while (!StackEmpty(&obj->Pushst)){//挪数据就是一边给popst入栈,一边给pushst出栈StackPush(&obj->Popst, StackTop(&obj->Pushst));StackPop(&obj->Pushst);}}return StackTop(&obj->Popst);
}bool myQueueEmpty(MyQueue* obj) 
{return StackEmpty(&obj->Popst) && StackEmpty(&obj->Pushst);
}void myQueueFree(MyQueue* obj) 
{StackDestory(&obj->Popst);StackDestory(&obj->Pushst);free(obj);//没用obj = NULL;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PyTorch使用Tricks:Dropout,R-Dropout和Multi-Sample Dropout等 !!

文章目录 1、为什么使用Dropout&#xff1f; 2、Dropout的拓展1&#xff1a;R-Dropout 3、Dropout的拓展2&#xff1a;Multi-Sample Dropout 4、Dropout的拓展3&#xff1a;DropConnect 5、Dropout的拓展4&#xff1a;Standout 6、Dropout的拓展5&#xff1a;Gaussian Dropout …

Jest和Mocha对比:两者之间有哪些区别?

什么是单元测试&#xff1f; 所谓单元测试&#xff0c;是对软件中单个功能组件进行测试的一种软件测试方式&#xff0c;其目的是确保代码中的每一个基本单元都能正常运行。因此&#xff0c;开发人员在应用程序开发的整个过程&#xff08;即代码编写过程&#xff09;中都需要进行…

Avalonia学习(二十四)-系统界面

目前项目式练习&#xff0c;界面内容偏多&#xff0c;所以不给大家贴代码了&#xff0c;可以留言交流。此次为大家展示的是物联项目的例子&#xff0c;仅仅是学习&#xff0c;我把一些重点列举一下。 界面无边框 以前的样例主要是通过实现控件来完成的&#xff0c;前面已经有窗…

美团外卖商超药店商品销量

外卖药店商品月销量 外卖商超商品月销量

学习总结19

# 奶牛的耳语 ## 题目描述 在你的养牛场&#xff0c;所有的奶牛都养在一排呈直线的牛栏中。一共有 n 头奶牛&#xff0c;其中第 i 头牛在直线上所处的位置可以用一个整数坐标 pi(0< pi < 10^8) 来表示。在无聊的日子里&#xff0c;奶牛们常常在自己的牛栏里与其它奶牛交…

术业有专攻!三防加固平板助力工业起飞

在日常使用中的商业电脑比较追求时效性&#xff0c;以市场定位做标准&#xff0c;内部元件只需满足一般要求就行&#xff0c;使用寿命比较短。而三防平板电脑是主要运用在复杂、恶劣的环境下所以在需求方面较高,需要保证产品在恶劣条件下正常使用&#xff0c;满足行业领域的需求…

Jakarta Bean Validation

Validation 官网 https://beanvalidation.org/ 常见注解 Bean Validation中定义的注解&#xff1a; 注解详细信息Null被注释的元素必须为 nullNotNull被注释的元素必须不为 nullAssertTrue被注释的元素必须为 trueAssertFalse被注释的元素必须为 falseMin(value)被注释的元素…

【linux】体系结构和os管理

冯诺依曼体系结构 输入单元&#xff1a;包括键盘, 鼠标&#xff0c;扫描仪, 写板等 中央处理器(CPU)&#xff1a;含有运算器和控制器等 输出单元&#xff1a;显示器&#xff0c;打印机等 这里的存储器指的是内存 三者是相互连接的&#xff0c;设备之间会进行数据的来回拷贝&am…

【springboot+vue项目(十五)】基于Oauth2的SSO单点登录(二)vue-element-admin框架改造整合Oauth2.0

Vue-element-admin 是一个基于 Vue.js 和 Element UI 的后台管理系统框架&#xff0c;提供了丰富的组件和功能&#xff0c;可以帮助开发者快速搭建现代化的后台管理系统。 一、基本知识 &#xff08;一&#xff09;Vue-element-admin 的主要文件和目录 vue-element-admin/ |…

人工智能_普通服务器CPU_安装清华开源人工智能AI大模型ChatGlm-6B_001---人工智能工作笔记0096

使用centos安装,注意安装之前,保证系统可以联网,然后执行yum update 先去更新一下系统,可以省掉很多麻烦 20240219_150031 这里我们使用centos系统吧,使用习惯了. ChatGlm首先需要一台个人计算机,或者服务器, 要的算力,训练最多,微调次之,推理需要算力最少 其实很多都支持C…

stable diffusion webui学习总结(2):技巧汇总

一、脸部修复&#xff1a;解决在低分辨率下&#xff0c;脸部生成异常的问题 勾选ADetailer&#xff0c;会在生成图片后&#xff0c;用更高的分辨率&#xff0c;对于脸部重新生成一遍 二、高清放大&#xff1a;低分辨率照片提升到高分辨率&#xff0c;并丰富内容细节 1、先通过…

【LeetCode】树的BFS(层序遍历)精选6题

目录 1. N 叉树的层序遍历&#xff08;中等&#xff09; 2. 二叉树的锯齿形层序遍历&#xff08;中等&#xff09; 3. 二叉树的最大宽度&#xff08;中等&#xff09; 4. 在每个树行中找最大值&#xff08;中等&#xff09; 5. 找树左下角的值&#xff08;中等&#xff09…

票务系统平台架构设计与实现

票务系统是一个复杂的平台&#xff0c;涉及到用户购票、票务管理、支付结算等多方面功能。本文将介绍票务系统平台的架构设计原则、技术选型以及实现过程&#xff0c;帮助读者了解如何构建一个高效、稳定的票务系统。 正文&#xff1a; 1. 系统设计原则 在设计票务系统平台时…

【计算机网络】网络基础

初识网络 一、网络发展二、认识协议三、认识网络协议1. 协议分层2. OSI 七层模型3. TCP/IP五层模型4. OS和网络协议栈 四、网络传输基本流程1. TCP/IP 协议通讯过程2. 以太网通信&#xff08;1&#xff09;以太网通信原理&#xff08;2&#xff09;数据碰撞 3. 数据跨网络传输 …

计算机网络概论和数据通信基础

文章目录 计算机网络概论从物理构成上看&#xff0c;计算机网络包括硬件、软件和协议三大部分计算机网络的功能组成计算机网络的分类网络体系结构分层与体系结构接口、协议和服务数据传送单位OSI模型TCP/IP模型 数据通信基础数字信号调制为模拟信号正交振幅调制QAM 模拟数据编码…

STM32Cubemx TB6612直流电机驱动

一、TB6612FNG TB6612是一个支持双电机的驱动模块&#xff0c;支持PWM调速。PWMA、AIN1、AIN2 为一组控制引脚&#xff0c;PWMA 为 PWM 速度控制引脚&#xff0c;AIN1、AIN2 为方向控制引脚&#xff1b;PWMB、BIN1、BIN2 为一组控制引脚&#xff0c;PWMB 为 PWM 速度控制引脚&…

例40:滚动条的使用

建立一个EXE工程&#xff0c;在窗体上放一个文本框和一个水平滚动条。设置滚动条的最大最小属性分别为&#xff1a;100和1。如图36。 图36 为滚动条的change事件输入代码&#xff1a; Sub Form1_HScroll1_Change(hWndForm As hWnd, hWndControl As hWnd, hNewPos As Long, n…

【高效开发工具系列】PyCharm使用

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

更改WordPress作者存档链接author和用户名插件Change Author Link Structure

WordPress作者存档链接默认情况为/author/Administrator&#xff08;用户名&#xff09;&#xff0c;为了防止用户名泄露&#xff0c;我们可以将其改为/author/1&#xff08;用户ID&#xff09;&#xff0c;具体操作可参考『如何将WordPress作者存档链接中的用户名改为昵称或ID…

【二十四】【C++】多态

多态的基本概念 多态是一种允许使用相同的接口来访问不同的底层形式&#xff08;类型&#xff09;的对象的能力。C中的多态主要通过以下两种方式实现&#xff1a; 编译时多态&#xff08;静态多态&#xff09;&#xff1a;通过函数重载和运算符重载实现。 运行时多态&#x…