OJ——栈和队列

一、有效的括号

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

思路:

        使用栈来解题,将之前写的栈拷贝过来使用。创建一个栈,遍历s(s是字符串,可以直接使用s来遍历)。*s如果是左括号那就入栈,是右括号就与栈顶的元素进行判断,类型相同就删除栈顶元素表示这组闭合正确,不同销毁栈返回false。

bool isValid(char* s) {
    Stack pc;
    StackInit(&pc);
    while(*s)
    {
        if(*s!='(' && *s!='[' && *s!='{')
        {
            if(*s==')' && StackTop(&pc)!='('||
            *s==']' && StackTop(&pc)!='['||
            *s=='}' && StackTop(&pc)!='{')
            {
                StackDestroy(&pc);
                return false;
            }
            StackPop(&pc);
        }
        else
       { 
            StackPush(&pc,*s);
       }
       s++;
    }
}

这里要注意当*s为右括号,而栈里没有元素时,它将不可能完成闭合。

if(*s!='(' && *s!='[' && *s!='{')
{
    if(StackEmpty(&pc))
    {               
        StackDestroy(&pc);
        return false;
    }
    if(*s==')' && StackTop(&pc)!='('||
    *s==']' && StackTop(&pc)!='['||
    *s=='}' && StackTop(&pc)!='{')
    {
        StackDestroy(&pc);
        return false;
    }
    StackPop(&pc);
}

遍历结束后要先判断栈是否为空,不为空那有左括号还没有完成闭合,返回false,为空,s内所有括号闭合成功,返回true。

bool isValid(char* s) {
    Stack pc;
    StackInit(&pc);
    while(*s)
    {
        if(*s!='(' && *s!='[' && *s!='{')
        {
            if(StackEmpty(&pc))
            {               
                StackDestroy(&pc);
                return false;
            }
            if(*s==')' && StackTop(&pc)!='('||
            *s==']' && StackTop(&pc)!='['||
            *s=='}' && StackTop(&pc)!='{')
            {
                StackDestroy(&pc);
                return false;
            }
            StackPop(&pc);
        }
        else
       { 
            StackPush(&pc,*s);
       }
       s++;
    }
    if(!StackEmpty(&pc))
    {
        StackDestroy(&pc);
        return false;
    }
    StackDestroy(&pc);
    return true;
}

二、用队列实现栈

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

定义

使用两个队列实现

typedef struct {
    Queue p1;
    Queue p2;
} MyStack;

初始化

开辟栈的空间后,主要是对两个队列进行初始化

MyStack* myStackCreate() {
    MyStack* tmp=(MyStack*)malloc(sizeof(MyStack));
    QueueInit(&tmp->p1);
    QueueInit(&tmp->p2);
    return tmp;
}

插入

我们的两个队列是要保持至少其中一个为空的(在删除时会说明理由),队列和栈都是对顶进行的插入,所以只需要对有数据的队列进行插入就可以,都为空那插入哪个队列都可以。

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

删除

栈删除是对栈顶的数据进行删除,队列只能删除队头的数据,一个是线性表的头(队列),一个是尾(栈)。一个队列自然无法做到删除,但两个队列就可以做到,在前面提到过两个队列要保持其中一个为空,删除时将有数据的队列(noempty)依次插入到空队列(empty)中,直到noempty剩余一个或0个元素(栈可能为空)时,再将剩余的那个元素删除。这个过程中是将noempty队尾的数据留到了队列中进行删除,empty成了存放数据的队列。注:empty可能是p1或p2,noempty也是。

int myStackPop(MyStack* obj) {
    //拿到空队列和有数据的队列
    Queue* noempty=&obj->p1;
    Queue* empty=&obj->p2;
    if(QueueEmpty(&obj->p1))
    {
        noempty=&obj->p2;
        empty=&obj->p1;
    }

    while(QueueSize(noempty)>1)
    {
        QueuePush(empty,QueueFront(noempty));
        QueuePop(noempty);
    }
    int tmp = QueueFront(noempty);
    QueuePop(noempty);
    return tmp;
}

例:栈内的元素是{1,2,3,4,5,6},此时noempty也是相同的值,除队尾的数据外全部插入到empty中,并删除。

最后在将6删除,就实现了栈的删除。

获取栈顶元素

有数据队列的队尾就是栈的栈顶。

int myStackTop(MyStack* obj) {
    if(QueueEmpty(&obj->p1))
        return QueueBack(&obj->p2);
    return QueueBack(&obj->p1);
}

是否为空

两个队列都为空栈才为空。

bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->p1)&&QueueEmpty(&obj->p2);
}

销毁

void myStackFree(MyStack* obj) {
    QueueInit(&obj->p1);
    QueueInit(&obj->p2);
    free(obj);
}

三、用栈实现队列

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

定义

使用两个栈实现

typedef struct {
    Stack q1;
    Stack q2;
} MyQueue;

初始化

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

插入

只需要对q1进行插入,q2用来删除。

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

删除

如果现在队列内有数据{1,2,3,4,5,6},这些数据都是放在q1内的,此时将q1的数据放到q2中,q2数据的顺序为{6,5,4,3,2,1},删出q2的栈顶1就是原先队列的队头,再依次向后删这组数据就达到了先入先出的效果。后续插入时数据还是先放入q1中,在删除数据时只有当q2为空,再将q1内的数据导入到q2中,否则先入先出的效果会达不到。

int myQueuePop(MyQueue* obj) {
    if(StackEmpty(&obj->q2))
    {
        swap(&obj->q1,&obj->q2);
    }
    STDataType tmp=StackTop(&obj->q2);
    StackPop(&obj->q2);
    return tmp;
}

获取队头数据

q2栈顶的元素才是队列的队头元素,在返回q2栈顶元素前要保证q2内有数据。

int myQueuePeek(MyQueue* obj) {
    if(StackEmpty(&obj->q2))
    {
        swap(&obj->q1,&obj->q2);
    }
    return StackTop(&obj->q2);
}

是否为空

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->q1)&&StackEmpty(&obj->q2);
}

销毁

void myQueueFree(MyQueue* obj) {
    StackDestroy(&obj->q1);
    StackDestroy(&obj->q2);
    free(obj);
}

四、设计循环队列

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

定义:

s:循环队列是固定的大小,使用连续的空间来储存数据最好。

front/rear:在使用两个变量来指向队头下标和队尾的下一块数据的下标。

k:队列开辟了几个元素。

typedef int QDataType;

typedef struct {
    QDataType* s;
    int front;
    int rear;
    int k;
} MyCircularQueue;

初始化:

开辟储存数据的空间时,开辟的大小要比最多存储的元素多一个,这样在判断是否为空和满时不会有冲突。

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* tmp=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    tmp->s=(QDataType*)malloc(sizeof(QDataType)*(k+1));
    tmp->front=tmp->rear=0;
    tmp->k=k+1;
    return tmp;
}

插入:

插入前判断队列是否为满,为满返回false,开辟空间时多开辟了一个,rear也是代表要插入元素的下标,当rear+1与front相等时空间就为满(rear可能是s的最后一个下标,要%k回到头去)。插入后rear要+1更换位置也要%k。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if((obj->rear+1)%obj->k==obj->front)
    {
        return false;
    }
    obj->s[obj->rear]=value;
    obj->rear=(obj->rear+1)%obj->k;
    return true;
}

删除:

删除判断是否为空,rear和front相等时就为空(如果只开辟一块空间就难以进行判断)。front直接+1就可以,逻辑上只有front到rear间的空间是有效的。

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

获取队头数据:

int myCircularQueueFront(MyCircularQueue* obj) {
    if(obj->rear==obj->front)
        return -1;
    return obj->s[obj->front];
}

获取队尾数据:

rear-1的位置是队尾的下标,但rear-1可能为-1,rear-1+k就是数组的最后一个下标,如果rear不是0这时就会发生越界,需要%k来控制。

int myCircularQueueRear(MyCircularQueue* obj) {
    if(obj->rear==obj->front)
        return -1;
    return obj->s[(obj->rear-1+obj->k)%obj->k];
}

判断是否为空和为满:

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front==obj->rear;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%obj->k==obj->front;
}

销毁:

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->s);
    free(obj);
    obj=NULL;
}

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值