上一篇写的是栈这一篇分享队列实现及其与队列相关OJ题
文章目录
1、队列概念
队列同栈一样也是一种特殊的数据结构,遵循
先进先出的原则,例如:想象在独木桥上走着的人,先上去的人定是先从独木桥上下来,为啥说是特殊呢?因为它只允许在对尾插入数据(简称入队,然后在对头删除数据(简称出队),只允许在这两端进行插入和删除操作
而基于它的特性选择链表实现还是数组实现更好呢?
当然选链表实现比较好,因为数组在头删除时需要移动大量的数据,时间复杂度为O(N),而用链表头删时间复杂度为O(1),那么有人会说那链表的尾插时间复杂度不也是O(N)吗,因为每次都要找尾节点
其实可以创建两个指针,一个指向对头,一个指向队尾,这样就不用每次都找尾了,时间复杂度也是O(1)。由于是多个数据,选择用结构体将其封装起来,而我们在链表每次访问长度的时候时间复杂度都为O(N),因此再在这个队列结构体中定义一个size表示队列大小
队列结构定义
typedef int QDataType;
typedef struct QNode
{
struct QNode* next;
QDataType data;
}QNode;
队列中的元素是每个节点,而每个节点又有多个数据存放下一个节点地址的next,存放每个节点数据的data,相当于队列里面它的成员又是一个结构体然后还有表示队列大小的size
typedef struct Queue
{
//Queue结构体中成员又包含结构体
QNode* head;//头指针指向头节点
QNode* tail;//尾指针指向尾节点
int size;//队列中节点的个数
}Queue;
队列实现
队列初始化
void QInit(Queue* pq)
{
// 传过来的时候就怕传一个空地址过来,那么队列都没有还怎么对其操作
assert(pq);
pq->head = pq->tail = NULL;//
pq->size = 0;
}
队列销毁
void QDestroy(Queue* pq)
{
assert(pq);
assert(pq->head!=NULL);//对都为空那么就不用再释放了
while (pq->head)
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
入队
void QPush(Queue* pq, QDataType x)
{
assert(pq);
//先创建一个队列里面元素的节点,
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail\n");
return;
}
newnode->data = x;
newnode->next = NULL;
//如果队列为空时,那么就直接将节点插入进来即可
if (pq->head == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = pq->tail->next;
}
//入队之后队列长度就要增一
pq->size++;
}
出队
void QPop(Queue* pq)
{
assert(pq);
assert(pq->head != NULL);//队列不空才出队
//队列中只有一个元素的时候,这时也就是对头指针和队尾指针指向同一个节点
if (pq->tail == pq->head)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
//找到头节点的下一个节点
else
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
//出队之后队列长度会减一
pq->size--;
}
判空
bool QEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;//直接返回对头指针,对头指针为空的话队列即为空
}
取对头元素
QDataType QFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
取队尾元素
QDataType QTail(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->tail->data;
}
获取队列长度
int QSize(Queue* pq)
{
assert(pq);
assert(pq->head);
//由于size直接获得队列大小,因此直接返回size即可
return pq->size;
}
Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;
typedef struct QNode
{
struct QNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
//Queue结构体中成员又包含结构体
QNode* head;//头指针
QNode* tail;
int size;//队列中节点的个数
}Queue;
//初始化
void QInit(Queue* pq);
//销毁队列
void QDestroy(Queue* pq);
//入队
void QPush(Queue* pq, QDataType x);
//出队
void QPop(Queue* pq);
//判空
bool QEmpty(Queue* pq);
//获取对头
QDataType QFront(Queue* pq);
//队大小
int QSize(Queue* pq);
//返回队尾元素
QDataType QTail(Queue* pq);
queue.c
void QInit(Queue* pq)
{
// 传过来的时候就怕传一个空地址过来,那么队列都没有还怎么对其操作
assert(pq);
pq->head = pq->tail = NULL;//
pq->size = 0;
}
void QDestroy(Queue* pq)
{
assert(pq);
//assert(pq->head!=NULL);//对头都为空那么就不用再释放了
while (pq->head)
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QPush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail\n");
return;
}
newnode->data = x;
newnode->next = NULL;
if (pq->head == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = pq->tail->next;
}
pq->size++;
}
void QPop(Queue* pq)
{
assert(pq);
assert(pq->head != NULL);
if (pq->tail == pq->head)
{
free(pq->head);
pq->head = NULL;
}
else
{
QNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
pq->size--;
}
bool QEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
QDataType QFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
int QSize(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->size;
}
QDataType QTail(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->tail->data;
}
test.c
#include"Queue.h"
int main()
{
Queue pq;
QInit(&pq);
QPush(&pq, 1);
QPush(&pq, 2);
QPush(&pq, 3);
QPush(&pq, 4);
/*while (!QEmpty(&pq))
{
printf("%d ", QFront(&pq));
QPop(&pq);
}*/
printf("%d ", QTail(&pq));
printf("%d ", QSize(&pq));
QDestroy(&pq);
return 0;
}
1、用队列实现栈

由于队列是先进先出,栈是先进后出
具体思想那么要用队列来实现栈就要保证最先进栈的要最后出栈,可是队列又是先进先出,要保证队列最后一个先出去的话,那么让那个不为空的队列其队尾为止前面的元素依次倒入另外一个队列中,然后取出这个数据那个那么就用两个队列才能完成

而队列就是我上面写的咯
QDataType QTail(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->tail->data;
}
//栈里有两个队列
typedef struct {
Queue q1;
Queue q2;
} MyStack;
//创建这个栈是要返回栈的地址的
//那么就malloc一个栈出来
//如果不向内存动态申请一个栈而是直接创建一个栈,那么创建的栈就是一个临时变量,它出了这个函数空间就会自动
MyStack* myStackCreate() {
MyStack*st = (MyStack*)malloc(sizeof(MyStack));
if(st == NULL)
{
perror("malloc fail\n");
return NULL;
}
else
{
//创建栈之后需要将栈结构体成员中的连个队列也初始化而这个队列又是一个结构体,且要改变队列内容就需传结构体地址传过去
//st是栈的结构体指针村的是栈的地址,对->访问它的成员,得到队列q1,q2,由于是用队列实现栈所以操作其实都是队列实现的,相当于操作的是队列,然后&st->q1就是传队列地址
QInit(&st->q1);
QInit(&st->q2);
return st;
}
}
//入栈是往队列不为空的那个里面入,
//最开始两个队列都是空就随便入一个,但是后面再入栈时就要往不为空的那个队列里面入数据。
void myStackPush(MyStack* obj, int x) {
if(!QEmpty(&obj->q1))
{
QPush(&obj->q1,x);
}
else
{
QPush(&obj->q2,x);
}
}
//栈是一个后进先出的特殊数据结构,且每次要出数据只能从栈顶出
//而队列是一种先进先出的特殊数据结构,且出数据只能在对头出,而我们要用对列来实现这个栈,我们只有将不为空的 那个队列中队尾元素之前的元素倒入那个为空的队列中,然后将原来那个不为空队列的队尾元素保存下来,最后将其出掉,然后如果再次调用出栈这个函数接口,那么也是一样的操作
//这里出队不是将那些元素丢掉,而是将这些出队元素入到原本为空的那个队列中
//
int myStackPop(MyStack* obj) {
Queue*empty = &obj->q1;//这里先默认q1为空队列
Queue*nonempty = &obj->q2;
if(!QEmpty(&obj->q1))//然后再判断队列q1是否为空,队列q1如果不为空,那么队列q2就是空咯
{
empty = &obj->q2;
nonempty = &obj->q1;
}
//将不为空的队列nonempty中对头元素先入到原来为空的队列empty中去,然后再将对头元素出掉,出到只剩下一个元素为止
while(QSize(nonempty)>1)
{
//将要出的数据反手就入到empty空的队列中去
QPush(empty,QFront(nonempty));
//然后再出数据
QPop(nonempty);
}
int top = QFront(nonempty);
QPop(nonempty);
return top;
}
//返回栈顶元素,其实就是返回不为空队列nonempty这个队尾数据
int myStackTop(MyStack* obj) {
Queue*empty = &obj->q1;
Queue*nonempty = &obj->q2;
if(!QEmpty(&obj->q1))
{
empty = &obj->q2;
nonempty = &obj->q1;
}
return QTail(nonempty);
}
//判断栈是否为空就是判断两个队列中是否还有元素,其中一个还有都不为空
bool myStackEmpty(MyStack* obj) {
return QEmpty(&obj->q1) &&QEmpty(&obj->q2);
}
//销毁栈不嫩一下子就将栈释放了,因为如果一下子就将栈释放了的话,你是释放了你这个栈里面的成员q1,q2,但是人家q1,q2里安他自己还有数据啊,这样造成了内存泄漏不得行
//而是一个先销毁队列中的数据然后再释放栈
void myStackFree(MyStack* obj) {
QDestroy(&obj->q1);
QDestroy(&obj->q2);
free(obj);
}
2、leetcode用栈实现队列

思想:用两个栈实现,一个栈进行入队操作,另一个栈进行出队操作 出队操作:当出队的栈不为空是,直接进行出栈操作,如果为空,需要把入队的栈元素全部导入到出队的栈,然后再进行出栈操作

//由于栈最先进去的数据要最后才能获得 //假设1,2,3,4是按顺序依次入队那么它的出队顺序也是1,2,3,4
//要用栈来实现队列那么就要让原来的数据以逆序4,3
,2,1的方式存储到栈中然后每次出栈顶的数据就可以实现队列的先进先出,而一个栈明显行不通,这时让一个栈用来实现入队,栈push用来模拟入队,那么进栈也就是1,2,3,4,将从push栈依次得到的数据倒入pop栈中,pop栈中进栈就是4,3,2,1那么从栈pop中拿到的数据也就是同进队时的顺序一样的1,2,3,4,在push栈向pop栈中倒数据时要保证pop栈为空方可倒不然如果pop栈里还有元素4,这时又从push栈中倒数据5,那么这个4就要在5后面出队,这就不符合对的先进先出了;
代码实现
typedef int STDataType;
typedef struct STNode
{
STDataType* a;
int top;
int capacity;
}ST;
void StackInit(ST* st)
{
assert(st);
st->a = (STDataType*)malloc(sizeof(STDataType)*4);
if (st->a == NULL)
{
printf("malloc fail\n");
exit(-1);
}
st->capacity = 4;
st->top = 0;
}
//销毁
void StackDestory(ST* st)
{
assert(st);
free(st->a);
st->a = NULL;
st->top = st->capacity = 0;
}
//进栈
void StackPush(ST* st, STDataType x)
{
assert(st);
if (st->top == st->capacity)
{
STDataType* tmp = (STDataType*)realloc(st->a,st->capacity*2*(sizeof(STDataType) ));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
else
{
st->a = tmp;
st->capacity *= 2;
}
}
st->a[st->top] = x;
st->top++;
}
//出栈
void StackPop(ST* st)
{
assert(st);
assert(st->top > 0);
st->top--;
}
//判空
bool StackEmpty(ST* st)
{
assert(st);
return st->top == 0;
}
//获取栈顶元素
STDataType StackTop(ST* st)
{
assert(st);
assert(st->top > 0);
return st->a[st->top-1];
}
//获取栈的大小
int StackSize(ST* st)
{
assert(st);
return st->top;
}
//栈的结构体定义
//先定义两个栈出来
//和用对列实现栈类似
typedef struct {
//push栈用来模拟入队
//pop栈用来模拟出队
ST push;
ST pop;
} MyQueue;
//这里也和用队列实现栈类似
MyQueue* myQueueCreate() {
MyQueue*pq = (MyQueue*)malloc(sizeof(MyQueue));
if(pq == NULL)
{
perror("malloc fail");
return NULL;
}
StackInit(&pq->push);
StackInit(&pq->pop);
return pq;
}
//队列是先进先出的特殊数据结构
//栈是先进后出,并且在栈顶插入和删除
//直接往push中入数据,不用管push是否为空
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->push,x);
}
//导入数据时注意pop栈为空才倒
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->pop))
{
while(!StackEmpty(&obj->push))
{
StackPush(&obj->pop,StackTop(&obj->push));
StackPop(&obj->push);
}
}
int front = StackTop(&obj->pop);
StackPop(&obj->pop);
return front;
}
//每次返回pop栈顶元素就是对头元素
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->pop))
{
while(!StackEmpty(&obj->push))
{
StackPush(&obj->pop,StackTop(&obj->push));
StackPop(&obj->push);
}
}
return StackTop(&obj->pop);
}
//两个栈为空队列才为空
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->push) && StackEmpty(&obj->pop);
}
//先销毁栈再释放队列
void myQueueFree(MyQueue* obj) {
StackDestory(&obj->pop);
StackDestory(&obj->push);
free(obj);
}
由于这时用C语言实现的需要自己手写一个栈。
队列是一种特殊的数据结构,在后面学习二叉树还会用到,因此还是要将其掌握熟练
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que
我有一个涉及多台机器、消息队列和事务的问题。因此,例如用户点击网页,点击将消息发送到另一台机器,该机器将付款添加到用户的帐户。每秒可能有数千次点击。事务的所有方面都应该是容错的。我以前从未遇到过这样的事情,但一些阅读表明这是一个众所周知的问题。所以我的问题。我假设安全的方法是使用两阶段提交,但协议(protocol)是阻塞的,所以我不会获得所需的性能,我是否正确?我通常写Ruby,但似乎Redis之类的数据库和Rescue、RabbitMQ等消息队列系统对我的帮助不大——即使我实现某种两阶段提交,如果Redis崩溃,数据也会丢失,因为它本质上只是内存。所有这些让我开始关注erlang和
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem
通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复
在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定