栈与队列的oj题

前言

本次博客做题为主,直接开始

第一题

20. 有效的括号 - 力扣(LeetCode)

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

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

分析

首先它是一个括号匹配的问题,括号匹配就意味着一 一对应

那么谁先对应谁后对应呢?

比如 对于一个(来说它要找到 ‘)’  但是之后如果字符串下一个字符为[

那么[必须要先匹配之后才轮到(对吧

是不是刚好符合栈的定义,后进先出,比对完后面的在比对前面

那么,我们就用栈来存储数据

那么接下来就该讨论情况了

1 如果字符串的个数为奇数,那么它一定不匹配,这是不用质疑的,那么接下来的情况就都是

偶数个数了

2如果第一个元素就为右括号,那么它就一定不匹配,当然,这个第一个元素不精确

我们可以扩展一下,那就是栈为空时,接收的元素为有括号那么它就一定不匹配

3普通不匹配情况:普通不匹配  比如 用(匹配}  用 { 匹配 ]等等情况

4就是遇到两个朝右方向的字符 那么存储继续

接下来就具体模拟一下流程

我们让栈记录与括号方向相反的括号,比如字符串中 第一个是 '{' 那么在栈中存储'}'

这样在下一次的比对中可以直接判断他们是否相等,而不是判断是不是对应括号

如果相等,让栈直接pop,如果不等那就是不匹配,这样巧妙的解决了问题

当然,我们也可以记录相同方向的括号,只不过在比较是发生了变化

看代码吧

class Solution {
public:
    bool isValid(string s) {
        stack<char>st;
        for(int i=0;i<s.size();i++)
        {
            if(s[i]=='('||s[i]=='['||s[i]=='{')
            st.push(s[i]);
            else if(s[i]==')'&&!st.empty()&&st.top()=='(')
            st.pop();
            else if(s[i]==']'&&!st.empty()&&st.top()=='[')
            st.pop();
            else if(s[i]=='}'&&!st.empty()&&st.top()=='{')
            st.pop();
            else
            return false;
        }
        return st.empty();
    }
};

最后不要忘了,要返回st.empty()

因为会有没有进行过一次比较的情况

比如 ((  这种情况没有右括号

第二题

225. 用队列实现栈 - 力扣(LeetCode)

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 

分析

使用队列实现栈时,我们还是画图来分析吧

看图

OK看代码吧

typedef struct Queue {
	struct Queue_* head;
	struct Queue_* tail;
	int size;
}Que;
typedef struct Queue_ {
	struct Queue_* next;
	int val;
}Que_;
void InitQue(Que* que)
{
	que->size = 0;
	que->tail=que->head = NULL;
}
void PushQue(Que* que, int val)
{
	Que_* newnode = (Que_*)malloc(sizeof(Que_));
	newnode->val = val;
	newnode->next = NULL;
	if (que->head == NULL)
	{
		que->head = que->tail = newnode;
	}
	else
	{
		que->tail->next=newnode;
		que->tail = newnode;
	}
	que->size++;
}
void PopQue(Que* que)
{
	if (que->head == que->tail)
	{
		free(que->head);
		que->head = que->tail = NULL;
	}
	else
	{
		Que_* temp = que->head->next;
		free(que->head);
		que->head = temp;
	}
	que->size--;
}
bool EmptyQue(Que* que)
{
	return que->size == 0;
}
int FrontQue(Que* que)
{
    assert(que->head);
	return que->head->val;
}
int BackQue(Que* que)
{
    assert(que->head);
	return que->tail->val;
}
int SizeQue(Que* que)
{
	return que->size;
}
void DestroyQue(Que*que)
{
    free(que->head);
    que->size=0;
    que->head=que->tail=NULL;
}
typedef struct {
    Que que1;
    Que que2;
} MyStack;
MyStack* myStackCreate() {
    MyStack*st=(MyStack*)malloc(sizeof(MyStack));
    InitQue(&st->que1);
    InitQue(&st->que2);
    return st;
}
void myStackPush(MyStack* obj, int x) {
    if(!EmptyQue(&obj->que1))
    {
        PushQue(&obj->que1,x);
    }
    else
    {
        PushQue(&obj->que2,x);
    }
}
int myStackPop(MyStack* obj) {
    //采用假设法
    Que *a=&obj->que1;
    Que *b=&obj->que2;
    if(EmptyQue(&obj->que1))
    {
     a=&obj->que2;
     b=&obj->que1;
    }
    while(SizeQue(a)>1)
    {
        PushQue(b,a->head->val);
        PopQue(a);
    }
    int ret=FrontQue(a);
    PopQue(a);
    return ret;
}
int myStackTop(MyStack* obj) {

    Que *a=&obj->que1;
    Que *b=&obj->que2;
    if(EmptyQue(&obj->que1))
    {
     a=&obj->que2;
     b=&obj->que1;
    }
    return BackQue(a);
}
bool myStackEmpty(MyStack* obj) {
    
    return EmptyQue(&obj->que1)&&EmptyQue(&obj->que2);
}
void myStackFree(MyStack* obj) {
    DestroyQue(&obj->que1);
    DestroyQue(&obj->que2);
    free(obj);
}

第三题

232. 用栈实现队列 - 力扣(LeetCode)

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

这个题目与上个题目差不多

分析部分还是看图快一点

 看代码

typedef int DataType;
typedef struct Stack {
	DataType* a;
	int top;
	int capacity;
}ST;
void InitStack(ST* st)
{
	assert(st);
	st->a = NULL;
	st->capacity = 0;
	st->top = 0;
}
DataType TopStack(ST* st)
{
	assert(st);
	assert(st->top > 0);
	return st->a[st->top - 1];
}
void PushStack(ST* st, DataType x)
{
	assert(st);
	if (st->a == NULL)
	{
		st->a=(DataType*)malloc(sizeof(DataType)*4);
		st->capacity = 4;
		st->a[st->top++] = x;
	}
	else
	{
		if (st->capacity == st->top)
		{
			DataType* temp = (DataType*)realloc(st->a, sizeof(DataType) * 2 * st->capacity);
			if (temp != NULL)
			{
				st->a =temp;
				st->capacity *= 2;
			}
		}
		st->a[st->top++] = x;
	}
}
void PopStack(ST* st)
{
	assert(st);
	assert(st->top > 0);
	st->top--;
}
int SizeStack(ST* st)
{
	return st->top;
}
DataType BottomStack(ST* st)
{
	assert(st);
	assert(st->top > 0);
	return st->a[0];
}
void DestroyStack(ST* st)
{
	free(st->a);
	st->a = NULL;
	st->capacity = 0;
	st->top = 0;
}
bool EmptyStack(ST* st)
{
	return st->top == 0;
}
typedef struct {
    ST st1;
    ST st2;
} MyQueue;
MyQueue* myQueueCreate() {
    MyQueue*que=(MyQueue*)malloc(sizeof(MyQueue));
    InitStack(&que->st1);
    InitStack(&que->st2);
    return que;
}
void myQueuePush(MyQueue* obj, int x) {
    PushStack(&obj->st1,x);
}
int myQueuePop(MyQueue* obj) {
    int ret=myQueuePeek(obj);
    PopStack(&obj->st2);
    return ret;
}
int myQueuePeek(MyQueue* obj) {
   if(EmptyStack(&obj->st2))
   {
    while(!EmptyStack(&obj->st1))
    {
        int ret=TopStack(&obj->st1);
        PushStack(&obj->st2,ret);
        PopStack(&obj->st1);
    }
   }
   int re=TopStack(&obj->st2);
    return re;
}
bool myQueueEmpty(MyQueue* obj) {
    return EmptyStack(&obj->st1)&&EmptyStack(&obj->st2);
}

void myQueueFree(MyQueue* obj) {
    DestroyStack(&obj->st1);
    DestroyStack(&obj->st2);
    free(obj);
}

 第四题

622. 设计循环队列 - 力扣(LeetCode)

设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

  • MyCircularQueue(k): 构造器,设置队列长度为 k 。
  • Front: 从队首获取元素。如果队列为空,返回 -1 。
  • Rear: 获取队尾元素。如果队列为空,返回 -1 。
  • enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
  • deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
  • isEmpty(): 检查循环队列是否为空。
  • isFull(): 检查循环队列是否已满。

OK这里要提醒各位,什么事循环队列

1大小固定

2内存利用满

3有队头队尾,一旦满队就不能插入元素了

这个题目可以使用数组以及队列来实现

本次博客使用数组来实现

细节

如何初始化,如何判断队列满,以及队列为空

初始化也同时决定了判满以及判空

1我们可以使用多开一个空间

2我们可以增加一个结构体变量size

本人更加倾向于第二种,因为第一种更加抽象,

使用多开空间实现判空,以及判满

当然在这之前,我们要先初始化队尾与队头,

我们一开始让队尾与队头为0,队尾为最后元素的下一个

插入元素,队尾++ 删除元素 队头++

不管是那种方法,判空时:我们都应该清楚,队头等于队尾时队列为空

当然,对于增加结构体变量size这个情况 可以直接与0比较

那么判满时,对于size而言,直接与队列的容量相比

但是对于增加一个容量来说有两种情况要考虑,这时可以使用求余

具体看图吧

OK,接下来来看看

插入,删除,取队头,取队尾的操作吧!

为了方便理解,我们下面的代码采用增加一个size成员变量来理解

插入元素

很简单,如果栈不为满,在队尾的位置插入元素然后tail++

当然这里同样要取余,因为,tail可能会循环一遍队列,所以tail=(tail+1)%(k)

删除元素

也同样简单,如果栈不为空,头head++即可,但是队头也有可能会回循环一遍队列

所以head=(head+1)%k

取队头

简单,只要栈不为空,返回head所指向的值即可

取队尾

有两种情况

这里要画个图来理解

OK,看代码吧

typedef struct {
    int *a;
    int k;
    int front;
    int rear;
    int size;
} MyCircularQueue;
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a=(int *)malloc(sizeof(int)*(k));
    obj->front=obj->rear=0;
    obj->k=k;
    obj->size=0;
    return obj;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    else
    {
        obj->a[obj->rear]=value;
        obj->rear=(obj->rear+1)%(obj->k);
        obj->size++;
        return true;
    }
}

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

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

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        int ret=(obj->rear-1+obj->k)%(obj->k);
        return obj->a[ret];
    }
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->size==0;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return obj->size==obj->k;
}

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

总结

这几道oj题主要还是运用了栈与队列的特性

希望在以后对于数据的处理上,我们可以多一种思维方式,好吧就到这里了

 

 

 

 

 

  • 17
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值