栈与队列(OJ)

有效的括号(力扣):

→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←

那么我们来看一下这道OJ题的具体要求叭!

栈与队列(OJ)_第1张图片

如果我还没学习栈的时候,我可能基本思路就是去数左右括号的总个数了,这样肯定做不出题目的。当然今时不同往日了,我已经学会了栈这种数据结构,我们可以通过栈去实现这些括号的闭合匹配了

栈与队列(OJ)_第2张图片

分析:

我们来分析一下如何运用栈:

由于第一个括号必须是左括号,所以我们就让左括号入栈,等遇到右括号,再进行出栈操作,进行匹配即可

我们再来分析一下括号不匹配的相关情况:

  1. 多出来了若干个左括号,无法完全匹配

  1. 不会多出多余的括号,但是左右括号不匹配

  1. 多出来了若干个右括号,无法完全匹配

那么以上三种情况,如何在代码中体现呢?

  1. 遍历一遍字符串,如果栈到最后还存在数据,那么说明有若干个左括号没有相对应的右括号进行匹配

  1. 遍历字符串的过程中,存在右括号与栈顶的左括号不匹配的状况

  1. 遍历字符串的过程中,遇到右括号时,我们的栈为空。

那么通过以上的分析,我们就很容易去实现我们括号的匹配了。

代码的步步实现:

由于我们用C语言去写这道题,我们就需要手搓一个栈。

上一篇文章我们已经手搓过一个栈了,那么我们就传送一下叭!

→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←

相信我们都拿到了手搓的栈了,那么我们就开始进行下一步操作!

我们先创建出一个栈,并对它进行初始化。

stack stack;
StackInit(&stack);

我们去遍历整个字符串,遇到左括号,就入栈

if(*(s + i) == '(' 
        || *(s + i) == '[' 
        || *(s + i) == '{')
            StackPush(&stack, *(s + i));

遇到右括号的时候就需要考虑一下了,如果我们的栈为空,那么不用说,肯定时匹配不上了,直接返回false,如果我们的栈不为空,我们就需要从栈顶出栈顶的一个左括号来和这个右括号进行匹配,如果匹配成功了就继续循环,如果匹配不上,我们就直接返回false

else
        {
            if(StackEmpty(&stack))
            {
                StackDestroy(&stack);
                return false;
            }
            char tmp = StackTop(&stack);
            StackPop(&stack);
            if((tmp == '(' && *(s + i) == ')') 
            || (tmp == '[' && *(s + i) == ']')
            || (tmp == '{' && *(s + i) == '}'))
            {
                continue;
            }
            else
            {
                StackDestroy(&stack);
                return false;
            }
        }

那么就到了最后一步了,我们遍历完整个字符串,发现过程中没有发现任何的不匹配问题,也就是我们上面分析的三种情况的二、三种情况,那么我们还需要判断我们分析的第一种情况,第一种情况只需要检查一下我们的栈是否为空即可,如果我们的栈不是空的,那么就完美了,我们直接返回true,如果不是,我们就返回false

  if(StackEmpty(&stack))
    {
        StackDestroy(&stack);
        return true;
    }
    StackDestroy(&stack);
    return false;

我们可能有同学也看出来了,每次返回值的时候,我们都会对栈进行销毁,这是个好习惯,有效的防止了我们的内存泄漏。

那么我们来看一下完整的代码叭:

bool isValid(char * s)
{
    stack stack;
    StackInit(&stack);

    for(int i = 0; *(s + i) != '\0'; i++)
    {
        if(*(s + i) == '(' 
        || *(s + i) == '[' 
        || *(s + i) == '{')
            StackPush(&stack, *(s + i));
        else
        {
            if(StackEmpty(&stack))
            {
                StackDestroy(&stack);
                return false;
            }
            char tmp = StackTop(&stack);
            StackPop(&stack);
            if((tmp == '(' && *(s + i) == ')') 
            || (tmp == '[' && *(s + i) == ']')
            || (tmp == '{' && *(s + i) == '}'))
            {
                continue;
            }
            else
            {
                StackDestroy(&stack);
                return false;
            }
        }
    }
    if(StackEmpty(&stack))
    {
        StackDestroy(&stack);
        return true;
    }
    StackDestroy(&stack);
    return false;
}

那么我们来进行下一道OJ题

用栈实现队列(力扣):

→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←

老样子,我们还是先来看一下我们题目的要求叭!

栈与队列(OJ)_第3张图片

分析:

我们栈的数据是先进后出,后进先出,而我们队列是先进先出,后进后出。

假设我们的栈里面一次入栈了1-7的数字,我们需要一样按顺序去出栈,那么我们第一个数字就必须输出1,那么我们就必须如下图,先把1以上的其他数字入另一个栈。

栈与队列(OJ)_第4张图片

那么我们就可以得到第一个数,之后我们可以发现,另一个栈所呈现数据的顺序就是我们需要的顺序,那么我们就遍历出栈即可。

代码的步步实现:

我们还是需要手搓一个栈,我相信很多小伙伴都不想往上翻或退出去再找一次,那么,上任意门

→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←

因为我们需要创建两个栈,所以我是不是可以用一个结构体来表示我的两个栈?

typedef struct {
    int stackInTop, stackOutTop;
    int stackIn[100], stackOut[100];
} MyQueue;

该结构体存放了我们输出以及输入的两个栈,还有两个栈的栈顶。

当然,我们的栈还是需要初始化的

MyQueue* myQueueCreate() 
{
    MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));
    if(queue == NULL)
    {
        perror("malloc fail");
        return;
    }
    queue->stackInTop = 0;
    queue->stackOutTop = 0;
    return queue;
}

一定要记得把两个栈顶置为0.

void myQueuePush(MyQueue* obj, int x) 
{
    obj->stackIn[(obj->stackInTop)++] = x;
}

先把元素压入第一个栈,然后让我们的栈顶+1

出栈!!!

int myQueuePop(MyQueue* obj) {
    int stackInTop = obj->stackInTop;
    int stackOutTop = obj->stackOutTop;
    //若输出栈为空
    if(stackOutTop == 0) {
        while(stackInTop > 0) {
            obj->stackOut[stackOutTop++] = obj->stackIn[--stackInTop];
        }
    }
    int top = obj->stackOut[--stackOutTop];
    while(stackOutTop > 0) {
        obj->stackIn[stackInTop++] = obj->stackOut[--stackOutTop];
    }
    obj->stackInTop = stackInTop;
    obj->stackOutTop = stackOutTop;
    return top;
}

如果输出栈为空,而且第一个栈有元素,我们就需要将第一个栈的元素倒腾到第二个栈当中,

还要把栈顶元素进行保存。

返回栈底元素

int myQueuePeek(MyQueue* obj) {
    return obj->stackIn[0];
}

判断队列是否为空

bool myQueueEmpty(MyQueue* obj) {
    return obj->stackInTop == 0 && obj->stackOutTop == 0;
}

只需要判断两个栈顶是否为0就ok啦

将栈顶指针置为0

void myQueueFree(MyQueue* obj) {
    obj->stackInTop = 0;
    obj->stackOutTop = 0;
}

那么我们来看一下完整的代码叭:

typedef struct {
    int stackInTop, stackOutTop;
    int stackIn[100], stackOut[100];
} MyQueue;

MyQueue* myQueueCreate() 
{
    MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue));
    if(queue == NULL)
    {
        perror("malloc fail");
        return;
    }
    queue->stackInTop = 0;
    queue->stackOutTop = 0;
    return queue;
}

void myQueuePush(MyQueue* obj, int x) 
{
    obj->stackIn[(obj->stackInTop)++] = x;
}

int myQueuePop(MyQueue* obj) {
    int stackInTop = obj->stackInTop;
    int stackOutTop = obj->stackOutTop;
    //若输出栈为空
    if(stackOutTop == 0) {
        while(stackInTop > 0) {
            obj->stackOut[stackOutTop++] = obj->stackIn[--stackInTop];
        }
    }
    int top = obj->stackOut[--stackOutTop];
    while(stackOutTop > 0) {
        obj->stackIn[stackInTop++] = obj->stackOut[--stackOutTop];
    }
    obj->stackInTop = stackInTop;
    obj->stackOutTop = stackOutTop;
    return top;
}

int myQueuePeek(MyQueue* obj) {
    return obj->stackIn[0];
}

bool myQueueEmpty(MyQueue* obj) {
    return obj->stackInTop == 0 && obj->stackOutTop == 0;
}

void myQueueFree(MyQueue* obj) {
    obj->stackInTop = 0;
    obj->stackOutTop = 0;
}

用队列实现栈(力扣):

→→→→→→→→→→→→→→→→→哆啦A梦的任意门←←←←←←←←←←←←←←←←←←←

还是先来看一下题目的要求叭!

栈与队列(OJ)_第5张图片

分析:

还是先从我们两种数据结构的性质进行分析,栈是先进后出,后进先出,而队列是先进先出,后进后出。

根据以上性质,还有上一道用栈实现队列的思路,我们也大概能分析出来。

还是一样的思路,一个队列用于输出我们的元素,一个元素用来备份我们的元素。

来看下面的一组图:

栈与队列(OJ)_第6张图片
栈与队列(OJ)_第7张图片

我们发现了,这个思路虽然和我们上面的那题虽然像,但是,好像并没有那么简单,我们刚做完的那道题,只需要倒腾一次,这元素的排列就符合我们的预期了,但是这道题就不一样,我们需要倒腾一次才能输出一次,但是这并不影响我们的做题。

那么就马上到我们的代码实现叭!

代码的步步实现:

当然,还是先把两个队列封装进一个结构体当中

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

我们栈的创建,因为这个栈的创建是通过初始化两个队列,所以我们可以看到对两个队列进行初始化

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

那么来到元素的压栈

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

我们也能分析的出,一个队列是用来备份我们的数据的,另一个用来对元素进行输出。

所以我们只对一个有数据的队列进行压栈。

到出栈啦!

int myStackPop(MyStack* obj) 
{
    assert(obj);
    Queue* EmptyQueue = &obj->q1;
    Queue* NonEmptyQueue = &obj->q2;
    if(!QueueEmpty(EmptyQueue))
    {
        EmptyQueue = &obj->q2;
        NonEmptyQueue = &obj->q1;
    }
    while(QueueSize(NonEmptyQueue) > 1)
    {
        QueuePush(EmptyQueue, QueueTop(NonEmptyQueue));
        QueuePop(NonEmptyQueue);
    }
    int top = QueueTop(NonEmptyQueue);
    QueuePop(NonEmptyQueue);
    return top;
}

我们默认第一个队列是空的嘛,那么如果不是空的,那么另一个就是空的咯~

所以我们要找到那个空的队列,也就是上一次的输出队列,让它来当一次存储数据的队列,然后上一次用来存储数据的队列对最后一个元素进行输出即可!

获取我们的栈顶元素数据

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

当然,还是要先找到哪个不是空的队列

检查栈是否为空

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

如果栈是空的,那么两个队列都是空的。

将该栈进行销毁

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

养成编程的好习惯,先从释放内存做起,记得一定要先释放那两个队列,再去释放这个栈,如果先释放这个栈,我们再去释放那两个队列的时候是找不到那两个队列的

那么我们来看一下完整的代码叭:

typedef struct QNode//队列节点
{
    int data;
    struct QNode* next;
}QNode;
typedef struct Queue//队列
{
    QNode* front;
    QNode* tail;
}Queue;
//栈是否为空
bool myStackEmpty(MyStack* obj);
//初始化
void QueueInit(Queue* queue);
//队列是否为空
bool QueueEmpty(Queue* queue);
//进队
void QueuePush(Queue* queue, int x);
//出队
void QueuePop(Queue* queue);
//队列头部元素
int QueueTop(Queue* queue);
//队列尾部元素
int QueueBack(Queue* queue);
//队列有效元素个数
int QueueSize(Queue* queue);
//销毁队列
void QueueDestroy(Queue* queue);

void QueueInit(Queue* queue)//初始化
{
    assert(queue);
    queue->front = NULL;
    queue->tail = NULL;
}
//队列是否为空
bool QueueEmpty(Queue* queue)
{
    assert(queue);
    return queue->tail == NULL;
}
//进队
void QueuePush(Queue* queue, int x)
{
    assert(queue);
    QNode* newnode = (QNode*)malloc(sizeof(QNode));
    assert(newnode);
    newnode->data = x;
    newnode->next = NULL;
    if(queue->tail == NULL)
    {
        queue->front = queue->tail = newnode;
    }
    else
    {
        queue->tail->next = newnode;
        queue->tail = newnode;
    }
}
//出队
void QueuePop(Queue* queue)
{
    assert(queue);
    QNode* next = queue->front->next;
    free(queue->front);
    queue->front = next;
    if(queue->front == NULL)
    {
        queue->tail = NULL;
    }
}
//队列头部元素
int QueueTop(Queue* queue)
{
    assert(queue);
    assert(queue->front);
    return queue->front->data;
}
//队列尾部元素
int QueueBack(Queue* queue)
{
    assert(queue);
    assert(queue->tail);
    return queue->tail->data;
}
//队列有效元素个数
int QueueSize(Queue* queue)
{
    assert(queue);
    int size = 0;
    QNode* cur = queue->front;
    while(cur != NULL)
    {
        ++size;
        cur = cur->next;
    }
    return size;
}
//销毁队列
void QueueDestroy(Queue* queue)
{
    assert(queue);
    QNode* next = queue->front;
    while(next != NULL)
    {
        next = queue->front->next;
        free(queue->front);
        queue->front = next;
    }
    queue->tail = NULL;
}

typedef struct 
{
    Queue q1;
    Queue q2;
} MyStack;
//创建栈
MyStack* myStackCreate() 
{
    MyStack* mystack = (MyStack*)malloc(sizeof(MyStack));
    assert(mystack);
    QueueInit(&mystack->q1);
    QueueInit(&mystack->q2);
    return mystack;
}
//压栈
void myStackPush(MyStack* obj, int x) 
{
    assert(obj);
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1, x);
    }
    else
    {
        QueuePush(&obj->q2, x);
    }
}
//出栈
int myStackPop(MyStack* obj) 
{
    assert(obj);
    Queue* EmptyQueue = &obj->q1;
    Queue* NonEmptyQueue = &obj->q2;
    if(!QueueEmpty(EmptyQueue))
    {
        EmptyQueue = &obj->q2;
        NonEmptyQueue = &obj->q1;
    }
    while(QueueSize(NonEmptyQueue) > 1)
    {
        QueuePush(EmptyQueue, QueueTop(NonEmptyQueue));
        QueuePop(NonEmptyQueue);
    }
    int top = QueueTop(NonEmptyQueue);
    QueuePop(NonEmptyQueue);
    return top;
}
//栈顶元素
int myStackTop(MyStack* obj) 
{
    assert(obj);
    assert(!myStackEmpty(obj));
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}
//栈是否为空
bool myStackEmpty(MyStack* obj) 
{
    assert(obj);
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
//销毁栈
void myStackFree(MyStack* obj) 
{
    assert(obj);
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
    obj = NULL;
}

你可能感兴趣的:(数据结构与算法,C语言的学习,数据结构,c++,开发语言)