数据结构初阶--栈和队列OJ题

目录

  • 前言
  • 有效的括号
    • 思路分析
    • 代码实现
  • 用队列实现栈
    • 思路分析
    • 代码实现
  • 用栈实现队列
    • 思路分析
    • 代码实现
  • 设计循环队列
    • 思路分析
    • 代码实现

前言

本篇文章将对部分栈和队列综合运用题进行讲解,以对栈和队列有一个更深层次的理解。

有效的括号

先来看题
数据结构初阶--栈和队列OJ题_第1张图片

思路分析

这里我们采取的方法是将左括号入栈,右括号与栈顶的括号进行比较,如果不匹配,则返回false,当所有括号匹配完则返回true
但是,这里最后几个测试用例会设置各种各样的坑。
1. 我们在每次取栈顶元素时首先要判空,如果为空则返回false。
2. 在将所有的左括号匹配完后,还需对栈判空,如果不为空,说明还有右括号没有匹配,则返回false。
3. 在取出栈顶元素后别忘了将其删除(pop)。

代码实现

由于代码是c语言版本,必须要自己创建一个栈,所以代码可能会有些冗长。

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int Capacity;
}ST;


bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->Capacity = 0;
	pst->top = 0;
}
void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->Capacity = 0;
	pst->top = 0;
}
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	if (pst->Capacity == pst->top)
	{
		int newCapacity = pst->Capacity == 0 ? 4 : pst->Capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
		}
		pst->a = tmp;
		pst->Capacity = newCapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}
void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	pst->top--;
}
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	return pst->a[pst->top - 1];
}
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}


bool isValid(char * s){
    ST pst;
    STInit(&pst);
    int len=strlen(s);
    if(len%2==1)
    return false;
    for(int i=0;i<len;i++)
    {
        if(s[i]=='('||s[i]=='['||s[i]=='{')
        {
            STPush(&pst,s[i]);
        }
        else
        {
            if(s[i]==')')
            {
                if(!STEmpty(&pst))
                {
                    char ch = STTop(&pst);
                    STPop(&pst);
                    if(ch!='(')
                    return false;
                }
                else
                return false;
            }
            if(s[i]==']')
            {
                if(!STEmpty(&pst))
                {
                    char ch = STTop(&pst);
                    STPop(&pst);
                    if(ch!='[')
                    return false;
                }
                else
                return false;
            }
            if(s[i]=='}')
            {
                if(!STEmpty(&pst))
                {
                    char ch = STTop(&pst);
                    STPop(&pst);
                    if(ch!='{')
                    return false;
                }
                else
                return false;
            }
        }
    }
    if(!STEmpty(&pst))
    {
        return false;
    }
    return true;
}

用队列实现栈

先来看题
数据结构初阶--栈和队列OJ题_第2张图片

思路分析

这道题的主体思路就是用两个队列queue1、queue2,先将元素存入queue2,要出栈时将queue2中的元素出队列存入queue1中,剩下一个queue2得队尾元素用于出栈即可。
若不是第一次出栈,则将queue1中的元素出队列存入queue2中,剩下一个queue1得队尾元素用于出栈即可

代码实现

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;

	pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
	assert(pq);
	//先删节点
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	//再删头尾指针
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);

		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}

	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}
QDataType QueueFront(Queue* pq)
{
	assert(pq);
    assert(!QueueEmpty(pq));

	return pq->phead->data;
}
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}


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


MyStack* myStackCreate() {
    MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
    if(obj==NULL)
    {
        perror("malloc fail\n");

    }
    QueueInit(&obj->q1);
    QueueInit(&obj->q2);

    return obj;
}

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

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

    return top;



}

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


}

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

}

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

    free(obj);

}

用栈实现队列

先看题
数据结构初阶--栈和队列OJ题_第3张图片

思路分析

主体思路就是将其中一个栈用于放入,另一个栈用于取出,每次出栈元素顺序都会翻转,出栈一次正好和队列顺序相匹配。

注意: 在返回队列开头元素时,应先判断用于取出的栈是否为空,若不为空,则直接将栈顶元素取出即可,若为空,则将用于放入的栈元素全部放入用于取出的栈,再返回栈顶元素。

代码实现

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int Capacity;
}ST;

bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->Capacity = 0;
	pst->top = 0;
}
void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->Capacity = 0;
	pst->top = 0;
}
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	if (pst->Capacity == pst->top)
	{
		int newCapacity = pst->Capacity == 0 ? 4 : pst->Capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
		}
		pst->a = tmp;
		pst->Capacity = newCapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}
void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	pst->top--;
}
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	return pst->a[pst->top - 1];
}
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}


typedef struct {
    ST pushst;//放
    ST popst;//取
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
    if(obj==NULL)
    {
        perror("malloc fail\n");
        return NULL;
    }
    STInit(&obj->pushst);
    STInit(&obj->popst);

    return obj;

}

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

int myQueuePop(MyQueue* obj) {
    int front=myQueuePeek(obj);
    STPop(&obj->popst);
    return front;
}

int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&obj->popst))
    {
        while(!STEmpty(&obj->pushst))
        {
            STPush(&obj->popst,STTop(&obj->pushst));
            STPop(&obj->pushst);
        }
    }

    return STTop(&obj->popst);

}

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

}

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->pushst);
    STDestroy(&obj->popst);

    free(obj);

}

设计循环队列

先看题
数据结构初阶--栈和队列OJ题_第4张图片

思路分析

我们首先要考虑一个问题,如何区分满和空?
当队列满或空时,rear和front的下标相同

为了解决这个问题,我们考虑增加一个空间,不存储任何数据,如果rear+1等于front则满
在解决这个问题以后,我们再来看其他操作,
假设循环队列存储5个数据,则我们先开辟六个空间。
数据结构初阶--栈和队列OJ题_第5张图片
此时rear==front为空。

那么此时我们该如何判满呢?
比如当front为0,rear为5,此时队列满了,又或者是当rear是2,front是3,此时又满了,似乎只要满足rear+1=front即可,但我们仔细想这样一个问题,数组又没法像链表那样,可以直接从尾部找到头部,所以数组下标如果超出了存储空间,我们取模即可让下标又从头开始往后走,

如何插入数据呢?
这个很简单,只需将值放入下标为rear的位置,再将rear++即可,注意如果超出范围,则需取模,由于小于最大范围取模还是等于它本身,我们干脆每次都取模

如何删除数据?
我们只需将front++即可,同样要取模

如何获取队首元素?
很简单,直接返回下标为front的元素即可。

如何获取队尾元素?
队尾元素即为rear前一个下标对应的元素,但存在特殊情况,假设此时rear指向了5,我们应该返回4,如果指向0,应该返回5,但5-1=4,0-1很显然不等于5,所以我们还是考虑用取模的方法,这里直接给出公式了
(rear+k)%(k+1).

代码实现

typedef struct {
    int front;
    int rear;
    int k;
    int* a;

} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->front=obj->rear=0;
    obj->k=k;

    return obj;

}

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

}

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

}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    return false;
    obj->a[obj->rear]=value;
    obj->rear++;

    obj->rear%=(obj->k+1);
    return true;

}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    return false;

    obj->front++;
    obj->front%=(obj->k+1);
    return true;

}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    return -1;

    return obj->a[obj->front];

}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    return -1;

    return obj->a[(obj->rear+obj->k)%(obj->k+1)];

}


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

}

以上就是本篇文章全部内容,如有出入,欢迎指正。

你可能感兴趣的:(数据结构初阶,数据结构,c语言,算法,链表)