【栈和队列】常见面试题

news2024/9/27 15:24:28

文章目录

  • 1.[有效的括号](https://leetcode.cn/problems/valid-parentheses/description/)
    • 1.1 题目要求
    • 1.2 利用栈解决
  • 2. [用队列实现栈](https://leetcode.cn/problems/implement-stack-using-queues/description/)
    • 2.1 题目要求
    • 2.2 用队列实现栈
  • 3.[用栈实现队列](https://leetcode.cn/problems/implement-queue-using-stacks/description/)
    • 3.1 题目要求
    • 3.2 用栈实现队列
  • 4.[设计循环队列](https://leetcode.cn/problems/design-circular-queue/description/)
    • 4.1 题目要求
    • 4.2 设计循环队列

1.有效的括号

1.1 题目要求

判断所给字符串的括号是否有效。
有效的条件:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

1.2 利用栈解决

遍历给定字符串时,当我们遇到左括号时,把它入栈。目的是为了期望在后续的遍历中有一个相同类型的右括号与其匹配。
当我们遍历时遇到的是右括号,此时有3种情况:
1.栈为空,无法匹配返回false。
2.栈不为空,但不是与之匹配的左括号,返回false。
3.栈不为空,是与之匹配的左括号,继续遍历。
在2,3情况下,为了简化书写,我们可以先把栈顶元素出栈,用一个临时变量保存。
如果我们在遍历时都顺利通过,也不能代表这个字符串就是有效的。
比如这种情况:( ( ( ) )
遍历完后,栈中还会剩下(.
为此最后我们还需要判断栈是否为空,不为空返回false,为空返回true。
注意记得销毁栈

typedef struct stack
{
    char* a;
    int size;//写错了,代表top的意思
    int capacity;
}stack;

void init(stack* ps)
{
    assert(ps);
    ps->a = (char*)malloc(sizeof(char)*4);
    ps->size = 0;
    ps->capacity = 4;
}

bool empty(stack* ps)
{
    return ps->size == 0;
}

void push(stack* ps,char x)
{
    assert(ps);
    if(ps->size == ps->capacity)
    {
        char* tmp = (char*)realloc(ps->a,sizeof(char)*ps->capacity*2);
        if(tmp == NULL)
        {
            perror("realloc");
            exit(-1);
        }
        ps->a = tmp;
        ps->capacity *= 2;
    }
    ps->a[ps->size] = x;
    ps->size+=1;
}
void pop(stack* ps)
{
    assert(ps);
    assert(ps->size>0);
    ps->size -= 1;
}

char Top(stack* ps)
{
    assert(ps);
    assert(ps->size>0);
    return ps->a[ps->size-1];
}
void destory(stack* ps)
{
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->size = 0;
    ps->capacity = 0;
}
bool isValid(char* s) {
    stack st;
    init(&st);
    while(*s)
    {
        if(*s == '('||*s == '['||*s == '{')
            push(&st,*s);
        else if(*s == ')'||*s == ']'||*s == '}')
        {
            if(empty(&st))
            {
                destory(&st);
                return false;
            }
            char top = Top(&st);
            pop(&st);
            if(*s == ')'&&top!='('||*s == ']'&&top!='['||*s == '}'&&top!='{')
            {
                destory(&st);
                return false;
            }
        }
        s+=1;
    }
    if(!empty(&st))
    {
        destory(&st);
        return false;
    }
    destory(&st);
    return true;
}

2. 用队列实现栈

2.1 题目要求

使用两个队列实现一个后入先出(LIFO)的栈

2.2 用队列实现栈

如何用两个队列来实现栈呢?我们知道队列是先进先出的。
先画两个队列出来。
两个队列

此时插入了3个数据,为了让这两个队列实现后进先出的效果,我们得充分利用队列的特性。显然如果是栈,出数据是先出3的,但是队列的话是先出1。为了让队列先出3,我们就要把1和2先移动到另一个空队列中。
操作队列
如此一来,就可以拿到3了。
那么用两个队列实现栈的主要逻辑就出来了。
首先是插入逻辑:有两种情况
1.两个队列都为NULL,随便插入一个队列
2.其中一个队列不为NULL,那么就把数据插入到不为NULL的队列。
然后就是删除逻辑:
在删除时,我们需要把有数据的一方队列的数据转移到没有数据的队列中,直到只剩下一个元素,这就是栈顶元素,用一个临时遍历存储完数据后就可以删除这个栈顶元素了。
再然后的返回栈顶元素:
因为不用删除的缘故,队列又提供返回队尾元素的功能,所以直接返回有数据的那个队尾元素即可。
最后都是一些简单的接口,相信大家没问题的。

typedef struct node
{
    int data;
    struct node* next;
}node;

typedef struct queue
{
    node* head;
    node* tail;
}queue;

void init(queue* q)
{
    assert(q);
    q->head = q->tail = NULL;
}

void push(queue* q,int x)
{
    assert(q);
    node* newnode = (node*)malloc(sizeof(node));
    if(newnode == NULL)
    {
        perror("malloc");
        exit(-1);
    }
    newnode->data = x;
    newnode->next = NULL;
    if(q->head == NULL)
    {
        q->head = q->tail = newnode;
    }
    else
    {
        q->tail->next = newnode;
        q->tail = newnode;
    }
}

void pop(queue* q)
{
    assert(q);
    assert(q->head);
    if(q->head == q->tail)
    {
        free(q->head);
        q->head = q->tail = NULL;
    }
    else
    {
        node* next = q->head->next;
        free(q->head);
        q->head = next;
    }
}

int size(queue* q)
{
    assert(q);
    node* cur = q->head;
    int count = 0;
    while(cur)
    {
        count+=1;
        cur = cur->next;
    }
    return count;
}

bool empty(queue* q)
{
    assert(q);
    return q->head == NULL;
}

int top(queue* q)
{
    assert(q);
    assert(q->head);
    return q->head->data;
}

int back(queue* q)
{
    assert(q);
    assert(q->head);
    return q->tail->data;
}
void destory(queue* q)
{
    assert(q);
    node* cur = q->head;
    while(cur)
    {
        node* next = cur->next;
        free(cur);
        cur = next;
    }
    q->head = q->tail = NULL;
}

typedef struct {
    queue q1;
    queue q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* s = (MyStack*)malloc(sizeof(MyStack));
    init(&s->q1);
    init(&s->q2);
    return s;
}

void myStackPush(MyStack* s, int x) {
   if(!empty(&s->q1))
   {
        push(&s->q1,x);
   }
   else
   {
        push(&s->q2,x);
   }
}

int myStackPop(MyStack* s) {
    queue* em = &s->q1;
    queue* noem = &s->q2;
    if(!empty(em))
    {
        em = &s->q2;
        noem = &s->q1;
    }
    while(size(noem)>1)
    {
        push(em,top(noem));
        pop(noem);
    }
    int t = top(noem);
    pop(noem);
    return t;
}

int myStackTop(MyStack* s) {
    queue* em = &s->q1;
    queue* noem = &s->q2;
    if(!empty(em))
    {
        em = &s->q2;
        noem = &s->q1;
    }
    return back(noem);
}

bool myStackEmpty(MyStack* s) {
    return empty(&s->q1)&&empty(&s->q2);
}

void myStackFree(MyStack* s) {
    destory(&s->q1);
    destory(&s->q2);
    free(s);
}

3.用栈实现队列

3.1 题目要求

请使用两个栈实现先入先出队列

3.2 用栈实现队列

要用两个后进先出的栈实现先进先出的队列。先看图
两个栈

为了实现队列,我们要把栈设计为一个进进数据栈,一个出数据栈。
当我们需要删除数据时,因为队列先进先出的特性,要出的数据为1,可是栈顶元素是3,所以我们需要把进数据栈的数据全部移动到出数据栈中,移动完后如图:
操作栈

这样的话,程序的主体逻辑都出来了。
开始我们入数据的时候,全部都存储到pusht栈中。
然后当我们想要删除数据/读取队首元素时:会有两种情况
1.popt栈为空,那么我们就把pusht栈中的元素全部转移到popt栈。
2.popt栈不为空,直接取popt的栈顶元素就可以了。
为了简化代码,我们可以复用函数。先实现读取队首元素的功能(peek)再在删除队列元素时复用读取对手元素的功能。详见代码
最后都是写简单功能,大家看代码。

typedef struct stack
{
    int* a;
    int size;//写错了,代表top的意思
    int capacity;
}stack;

void init(stack* ps)
{
    assert(ps);
    ps->a = (int*)malloc(sizeof(int)*4);
    ps->size = 0;
    ps->capacity = 4;
}

bool empty(stack* ps)
{
    return ps->size == 0;
}

void push(stack* ps,int x)
{
    assert(ps);
    if(ps->size == ps->capacity)
    {
        int* tmp = (int*)realloc(ps->a,sizeof(int)*ps->capacity*2);
        if(tmp == NULL)
        {
            perror("realloc");
            exit(-1);
        }
        ps->a = tmp;
        ps->capacity *= 2;
    }
    ps->a[ps->size] = x;
    ps->size+=1;
}
void pop(stack* ps)
{
    assert(ps);
    assert(ps->size>0);
    ps->size -= 1;
}
int Top(stack* ps)
{
    assert(ps);
    assert(ps->size>0);
    return ps->a[ps->size-1];
}
void destory(stack* ps)
{
    assert(ps);
    free(ps->a);
    ps->a = NULL;
    ps->size = 0;
    ps->capacity = 0;
}
int size(stack* ps)
{
    assert(ps);
    return ps->size;
}
typedef struct {
    stack pusht;
    stack popt;
} MyQueue;

MyQueue* myQueueCreate() {
    MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
    init(&q->pusht);
    init(&q->popt);
    return q;
}

void myQueuePush(MyQueue* q, int x) {
   push(&q->pusht,x);
}

int myQueuePeek(MyQueue* q) {
    if(empty(&q->popt))
    {
        while(!empty(&q->pusht))
        {
            push(&q->popt,Top(&q->pusht));
            pop(&q->pusht);
        }
    }
    return Top(&q->popt);
}

int myQueuePop(MyQueue* q) {
    int top = myQueuePeek(q);
    pop(&q->popt);
    return top;
}

bool myQueueEmpty(MyQueue* q) {
    return empty(&q->pusht)&&empty(&q->popt);
}
void myQueueFree(MyQueue* q) {
    destory(&q->pusht);
    destory(&q->popt);
    free(q);
    q = NULL;
}

4.设计循环队列

4.1 题目要求

设计一个大小为k的循环队列

4.2 设计循环队列

简单科普循环队列
这是一个循环队列
循环队列

下面展示循环队列为空和为满时情形
tail位置是不存储数据的,代表意思是队尾元素的下一个。
循环队列的各个情形

科普完成后,下面就是正式的题目讲解
在初始化时,我们需要开k+1个空间,因为数组中的tial位是不存储数据的。
先写判断循环队列是否为空和为满的功能,写了两个函数书写其他函数时就比较轻松了。
正如上面所说,tail = head为空
为满的话,考虑到循环因素,我们需要利用取模操作。
为满就一种情况,head在tail前面,但是因为数组不像上面画的那样是一个环,所以为满就有了两种情况:
1.tial在head前面,多种情况
2.tail在head后面,在后面就一种情况,tail为k,head为0
(tail+1)%(k+1) = head就是判断条件。
后面的插入删除都没什么难点了,唯一要注意的就是当head和tial到达k时要注意下一步的操作。
还有返回队尾数据的,tail是指向队尾数据的下一个元素的!

typedef struct {
    int* a;
    int head;
    int tail;
    int k;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    q->a = (int*)malloc(sizeof(int)*(k+1));
    q->head = q->tail = 0;
    q->k = k;
    return q;
}
//函数声明
bool myCircularQueueIsEmpty(MyCircularQueue* q);
bool myCircularQueueIsFull(MyCircularQueue* q);


bool myCircularQueueEnQueue(MyCircularQueue* q, int value) {
    if(myCircularQueueIsFull(q))
        return false;
    q->a[q->tail] = value;
    if(q->tail == q->k)
        q->tail = 0;
    else
        q->tail+=1;
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* q) {
    if(myCircularQueueIsEmpty(q))
        return false;
    if(q->head == q->k)
        q->head = 0;
    else
        q->head+=1;
    return true;
}

int myCircularQueueFront(MyCircularQueue* q) {
    if(myCircularQueueIsEmpty(q))
        return -1;
    return q->a[q->head];
}

int myCircularQueueRear(MyCircularQueue* q) {
    if(myCircularQueueIsEmpty(q))
        return -1;
    if(q->tail == 0)
        return q->a[q->k];
    else
        return q->a[q->tail-1];
}

bool myCircularQueueIsEmpty(MyCircularQueue* q) {
    if(q->head == q->tail)
        return true;
    return false;
}

bool myCircularQueueIsFull(MyCircularQueue* q) {
    if((q->tail+1)%(q->k+1) == q->head)
        return true;
    return false;

}

void myCircularQueueFree(MyCircularQueue* q) {
    free(q);
    q = NULL;
}

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

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

相关文章

cesium gltf模型

cesium 支持的三维模型格式包括GLTF和GLB(二进制glTF文件)。 本文通过使用Entity图元的modelGraphics对象来加载gltf模型,简单对gltf模型的加载进行了封装。通过设置模型的欧拉角,可以计算模型的朝向。 1 3D数学中模型旋转的方式…

数据定义和操作

一、数据定义语言和操作语言 1. 分析需求设计数据库表 根据“优乐网”系统需求: 此系统满足以下需求: 系统支持生成商品的入库和出库。入库之后的商品可以在平台显示 所有用户都可以浏览系统的商品信息,只有注册用户才能订购团购商品和服…

机械学习—零基础学习日志(python编程)

零基础为了学人工智能,正在艰苦的学习 昨天给高等数学的学习按下暂停键,现在开始学习python编程。 我学习的思路是直接去阿里云的AI学习课堂里面学习。 整体感觉,阿里云的AI课堂还是有一些乱,早期课程和新出内容没有更新和归档…

进阶学习------linux运维读写执行权限

进阶学习------linux运维读写执行权限 在UNIX和类UNIX操作系统中,文件权限是通过一组特定的数字来表示的,这些数字分为三组,分别对应于用户(文件所有者)、组和其他用户的权限。每组权限由三个二进制位表示,…

如何进行硬件调试?

硬件调试是硬件系统设计、开发和制造过程中不可或缺的一环,旨在对可能出现的问题进行分析和解决。以下是进行硬件调试的一般步骤和方法: 一、准备阶段 熟悉设计文档:在开始调试之前,需要详细阅读和理解硬件系统的设计文档、原理图…

【探索数据结构与算法】——深入了解双向链表(图文详解)

目录 一、双向链表的基本概念 ​​​ 二、双向链表的结构 三、双向链表的基本操作实现方法 1.双向链表的初始化 2.双向链表的头插 3.双向链表的尾插 6.查找节点 7.在指定位置之前插入节点 8.删除指定位置节点 9.打印链表数据 10.双向链表销毁 四、完整代码实现 …

html+css+js网页设计 星享咖啡6个页面(带js) ui还原度90%

htmlcssjs网页设计 星享咖啡6个页面(带js) ui还原度90% 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等…

一个拳打Claude3.5 Artifacts 脚踢软件外包公司的国产AI神器

Claude3.5的Artifacts功能想必大家都有所耳闻吧。 不了解的小伙伴也没关系,看下面这个视频。 更详细的介绍可以看之前我写的文章 关于Claude3.5-Sonnet引以为傲的功能,在半年前就被某国产平台无情碾压的那档事!_claude 3.5 sonnet 国内能用…

20240808在飞凌OK3588-C开发板上使用HDMI OUT接口的SONY索尼8530机芯的YUV模式录像

20240808在飞凌OK3588-C开发板上使用HDMI OUT接口的SONY索尼8530机芯的YUV模式录像 2024/8/8 15:26 v4l2-ctl --list-devices v4l2-ctl --list-formats-ext -d /dev/video8 v4l2-ctl -V -d /dev/video8 根据规格书《FCB-CR8550_8530_TM_20190730.pdf》,很容易知道8…

【秋招突围】2024届校招-拼多多笔试题-第一套

🍭 大家好这里是 大厂笔试突围,一起备战秋招笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🌻 听说本周PDD的笔…

elasticSearch和ik分词插件安装和使用

elasticSearch 特点:分布式搜索和分析引擎,可以用http以json的方式进行数据索引。 由来:ES全称Elastic Stack(ELK Stack),是由三个产品elasticSearch,logstack(数据收集&#xff0…

【新手必备】5分钟学会Transformer算法的核心要点

Transformer 是近年来在自然语言处理(NLP)领域取得显著成果的一种深度学习模型,最初由 Vaswani et al. 在 2017 年提出。 与传统的序列模型(如 RNN 和 LSTM)相比,Transformer 的主要优势在于其能够更好地处…

零基础5分钟上手亚马逊云科技AWS核心云架构知识-用S3桶托管静态网页

简介: 小李哥从今天开始将开启全新亚马逊云科技AWS云计算知识学习系列,适用于任何无云计算或者亚马逊云科技技术背景的开发者,让大家0基础5分钟通过这篇文章就能完全学会亚马逊云科技一个经典的服务开发架构。 我将每天介绍一个基于亚马逊云…

go之protobuf和grpc

一、Protobuf Protobuf是接口规范的描述语言,可以通过工具生成代码,将结构化数据序列化。 二、grpc gRPC 是 Google 公司基于 Protobuf 开发的跨语言的开源 RPC 框架。 三、使用教程 3.1 student.proto syntax "proto3"; import "go…

软件测试---接口测试

一、接口及接口测试概念 (1)接口的类型 (2)接口测试的概念 (3)接口测试的原理 (4)接口测试的特点 (5)接口测试的实现方式 二、HTTP协议 (1&#…

【人工智能】AI时代程序员----是缔造AI程序员,还是AI缔造程序员?

是缔造AI程序员,还是AI缔造程序员? 前言 随着AIGC(如ChatGPT、MidJourney、Claude等)大语言模型的涌现,AI辅助编程工具日益普及,程序员的工作方式正在发生深刻变革。 ​ ChatGPT ​ Midjourney ​ Claude …

【免费测试】人脸身份证比对接口如何用Java对接?(一)

一、什么是人脸身份证比对? 人脸身份证比对又称人证比对,实人比对,人像比对,输入姓名、身份证号码和头像照片,与公安库身份证头像进行权威比对,返回分值作为判断依据。 二、人脸身份证比对接口适用哪些场…

Processing之函数

一.函数基础 函数是processing程序中最基本的结构。经常使用的函数有draw()、line()、size()等。计算机每次运行程序的一行。当一个函数运行时,计算机跳到函数定义的位置,运行到哪里的代码,然后在跳回它离开的位置。 1.1掷骰子 案例代码如图…

离线安装windows应用商店中的应用

以windows notepad为例 1.百度windows应用商店,notepad 进入https://apps.microsoft.com/detail/9msmlrh6lzf3?hlen-aq&glAQ 是一个名为windows notepad的应用,复制地址栏里面detail后面的字符9msmlrh6lzf3 有很多notepad类似的应用,wi…

2024年新能源汽车充电桩建设驶入快车道

2024年新能源汽车市场大爆发:渗透率飙升,保有量创新高,充电桩建设驶入快车道 随着2024年新能源汽车市场的持续繁荣,一场前所未有的绿色革命正在全球范围内加速推进。这一年,新能源汽车的渗透率不仅实现了质的飞跃&…