力扣(leetcode)经典题目分享第3期——栈和队列

栈和队列

  • 一. 选择题
    • 1.1 进出栈顺序
    • 1.2 循环队列
    • 1.3 队列的基本运算
    • 1.4 循环队列的有效长度
  • 二. OJ练习题
    • 2.1 括号匹配问题
    • 2.2 用队列实现栈
    • 2.3 用栈实现队列
    • 2.4 循环队列
  • 总结:

一. 选择题

1.1 进出栈顺序

若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A 1,4,3,2
B 2,3,4,1
C 3,1,4,2
D 3,4,2,1

答案:C
解析:出栈的要求是先出栈顶元素。C选项是先出的3,那么此时栈中还有1和2,如果第二个要出栈的话,只能出栈顶的2,而他出的1,所以C选项肯定是错的。

1.2 循环队列

循环队列的存储空间为 Q(1:100) ,初始状态为 front=rear=100 。经过一系列正常的入队与退队操作
后, front=rear=99 ,则循环队列中的元素个数为( )
A 1
B 2
C 99
D 0或者100
答案:D
解析:由于循环队列的性质,当front==rear时,不能判断队列为空还是为满,所以D正确。

1.3 队列的基本运算

4.以下( )不是队列的基本运算?
A 从队尾插入一个新元素
B 从队列中删除第i个元素
C 判断一个队列是否为空
D 读取队头元素的值
答案:B

1.4 循环队列的有效长度

5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为?(假设
队头不存放数据)
A (rear - front + N) % N + 1
B (rear - front + N) % N
C ear - front) % (N + 1)
D (rear - front + N) % (N - 1)
答案:B

二. OJ练习题

需要注意的点:本篇博客的代码中用到的数据结构可以从我上一期博客中找到。链接。

2.1 括号匹配问题

题目介绍:
给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。

OJ链接
思路分析:
使用一个栈来存储字符串中的左括号,如果遇到字符串中的右括号,则出栈匹配,一旦匹配失败则返回false,否则就一直匹配,直到将数组元素全部匹配完成,返回true。
代码实现:

bool isValid(char * s)
{
    Stack st;
    StackInit(&st);
    while(*s)
    {
        if((*s=='(')||(*s=='[')||(*s=='{'))
        {
            StackPush(&st,*s);
            s++;
        }
        else
        {
            if(StackEmpty(&st)==true)//数组只有有括号时的处理
                      return false;
            STDataType tmp=StackTop(&st);
            StackPop(&st);
            if((*s==')'&&tmp!='(')
                  ||(*s==']'&&tmp!='[')
                      ||(*s=='}'&&tmp!='{'))
                      return false;
            else
            {
                s++;
            }
        }
    }
    bool ret=StackEmpty(&st);//数组只有左括号时的处理
    StackDestroy(&st);
    return ret;
}

2.2 用队列实现栈

题目介绍:请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。 OJ链接。
思路分析:队列先进先出,栈是后进先出。主要解决出栈时的问题,本题需要使用两个队列才能实现栈,当出栈时,将一个队列中的元素转移到另一个空队列中,原队列只保留队尾元素,此时该元素就是我们要出栈的元素。之后每次出栈都以同样的方法倒元素即可。
图解:
力扣(leetcode)经典题目分享第3期——栈和队列_第1张图片
此时需要出栈顶元素5,我们将队列1的元素倒到队列2去,其中队列1只留一个元素
力扣(leetcode)经典题目分享第3期——栈和队列_第2张图片
这时队列1就只剩5这个元素了,对其操作即可。
代码如下:

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


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

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

int myStackPop(MyStack* obj) 
{
    if(!QueueEmpty(&obj->q1))
    {
        while(QueueSize(&obj->q1)>1)
        {
            QueuePush(&obj->q2,QueueFront(&obj->q1));
            QueuePop(&obj->q1);
        }
        int tmp=QueueFront(&obj->q1);
        QueuePop(&obj->q1);
        return tmp;
    }
    else
    {
        while(QueueSize(&obj->q2)>1)
        {
            QueuePush(&obj->q1,QueueFront(&obj->q2));
            QueuePop(&obj->q2);
        }
        int tmp=QueueFront(&obj->q2);
        QueuePop(&obj->q2);
        return tmp;
    }
}

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

bool myStackEmpty(MyStack* obj) 
{
    return (QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2));
}

void myStackFree(MyStack* obj)
{
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

2.3 用栈实现队列

题目介绍:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty)。OJ链接。
思路分析:同样是使用两个栈来实现队列,需要注意队列是先进先出,其中一个栈用来入队(pushst),一个栈专门用来出队(popst)。当出队时,如果popst为空,就将pushst中的元素倒到popst中,再在popst里出栈。

代码如下:

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


MyQueue* myQueueCreate()
{
    MyQueue* tmp=(MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&tmp->popst);
    StackInit(&tmp->pushst);
    return tmp;
}

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

int myQueuePeek(MyQueue* obj)
{
    if(StackEmpty(&obj->popst))
    {
        while(StackSize(&obj->pushst)>0)
        {
            StackPush(&obj->popst,StackTop(&obj->pushst));
            StackPop(&obj->pushst);
        }
        return StackTop(&obj->popst);
    }
    return StackTop(&obj->popst);
}
int myQueuePop(MyQueue* obj) 
{
    int top=myQueuePeek(obj);
    StackPop(&obj->popst);
    return top;
}


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

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

2.4 循环队列

题目介绍:
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
力扣(leetcode)经典题目分享第3期——栈和队列_第3张图片
OJ链接
思路分析:
这里我们并不是使用链表来实现循环队列,而是使用数组来实现它。原因是因为单链表实现循环列表的话,不好处理队尾元素。所以我们就使用数组来实现循环队列。该部分较难,我将一些需要注意的地方写了出来,大家自己写的时候应该加以注意,后面也会有代码可以对照。
需要注意的是:
假设队头尾front,队尾为rear,队列容量为k,那么:

  1. 开空间时,需要多开一个,以解决判空和判满问题。
  2. 判断队列是否满了:(rear+1)%(k+1)==front
  3. 每次push后,rear需要%=k+1
  4. 每次pop后,front需要%=k+1
    代码如下:
typedef struct 
{
    int* a;
    int head;
    int tail;
    int k;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->head=obj->tail=0;
    obj->k=k;
    return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    assert(obj);
    return obj->head==obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    assert(obj);  
    int tail= (obj->tail+1)%(obj->k+1);
    return tail==obj->head;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    assert(obj);
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->a[obj->tail]=value;
    obj->tail++;
    obj->tail %= (obj->k + 1);
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    assert(obj);
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    obj->head++;
    obj->head%=(obj->k+1);
    return true;
}

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

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



void myCircularQueueFree(MyCircularQueue* obj) 
{
    assert(obj);
    free(obj->a);
    free(obj);
}

总结:

本篇博客主要介绍了一些常见的选择题和OJ题,主要考察的还是队列和栈的性质。关于循环队列的部分呢,难度较大,没有特殊要求的话,可以跳过。

力扣(leetcode)经典题目分享第3期——栈和队列_第4张图片

你可能感兴趣的:(C语言刷题,leetcode,算法,c语言,数据结构,学习)