目录
栈的实现
1.Stack.h文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义栈的结构
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top; //指向栈顶的位置,也表示有效元素个数
int capacity;//栈的容量
}ST;
//初始化栈
void StackInit(ST* ps);
//入栈——栈顶
void StackPush(ST* ps, STDataType x);
//判断栈是否为空
bool StackEmpty(ST* ps);
//出栈——栈顶
void StackPop(ST* ps);
//取栈顶元素
STDataType StackTop(ST* ps);
//获取栈中有效元素个数
int StackSize(ST* ps);
//栈的销毁
void StackDestroy(ST* ps);
2.Stack.c文件
#include"Stack.h"
//初始化栈
void StackInit(ST* ps)
{
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
//入栈——栈顶
void StackPush(ST* ps, STDataType x)
{
assert(ps);
//判断空间是否足够
if (ps->top == ps->capacity)
{
//增容
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
ps->arr[ps->top++] = x;
}
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
//如果return后面的表达式正确
//则返回ture,即栈为空
}
//出栈——栈顶
void StackPop(ST* ps)
{
assert(!StackEmpty(ps));//栈不为空才能继续出栈操作
ps->top--;
}
//取栈顶元素 (!=出栈,并没有减少有效元素个数)
STDataType StackTop(ST* ps)
{
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
//获取栈中有效元素个数
int StackSize(ST* ps)
{
return ps->top;
}
//栈的销毁
void StackDestroy(ST* ps)
{
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
队列的实现
1.Queue.h文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDataType;
//队列结点的结构
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QueueNode;
//队列的结构
typedef struct Queue
{
QueueNode* phead;
QueueNode* ptail;
}Queue;
//队列的初始化
void QueueInit(Queue* pq);
//入队——队尾
void QueuePush(Queue* pq, QDataType x);
//队列判空
bool QueueEmpty(Queue* pq);
//出队
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//队列有效个数
int QueueSize(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);
2.Queue.c文件
#include"Queue.h"
//队列初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
}
//入队——队尾
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//申请空间
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->data = x;
newnode->next = NULL;
//队列为空
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
}
//非空
else {
pq->ptail->next = newnode;
pq->ptail = pq->ptail->next;
}
}
//队列判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->phead == NULL;
}
//出队——队头
void QueuePop(Queue* pq)
{
assert(!QueueEmpty(pq));
//只有一个结点,phead和ptail都要置空,否则ptail变野指针
if (pq->phead == pq->ptail)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else {
QueueNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
}
//取队头数据
QDataType QueueFront(Queue* pq)
{
assert(!QueueEmpty(pq));
return pq->phead->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
//队列有效个数
int QueueSize(Queue* pq)
{
QueueNode* pcur = pq->phead;
int size = 0;
while (pcur)
{
size++;
pcur = pcur->next;
}
return size;
}
//销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* pcur;
pcur = pq->phead;
while (pcur != NULL)
{
QueueNode* next = pcur->next;
free(pcur);
pcur = next;
}
pq->phead = pq->ptail = NULL;
}
栈和队列OJ
1.有效的括号
理解题目:
根据题目中满足有效字符串的三个条件,得知每个类型的左括号必须有相同类型的右括号按照正确的顺序闭合,可以比喻成消消乐,如实例1,左右小括号对应消除后什么都没有了,示例1满足;实例2也是一样的道理,从左到右小、中、大括号依次消除后没有剩余,示例2满足;同理,示例3括号不匹配则“消不掉”,不满足;实例4从内部的中括号开始消除,消除完后外层的小括号也可以对应消除,最后也没有剩余多余的括号,满足题意。
解题思路:
思路概括:运用数据结构——栈,遍历字符串,若是左括号则入栈,如果是右括号,则与栈顶的左括号匹配,如果匹配成功,栈顶的左括号出栈,更换新的栈顶,字符串也继续向后遍历,进行新一轮的比较;如果匹配不成功,也就是“消不掉”,那就无法成为有效字符串,直接返回false;如果遍历到右括号时发现栈内为空,那也不可能形成有效字符串,返回false;最后遍历完字符串后,还得判断栈内是否为空(左括号是否被完全匹配消除),如果为空则返回true,反之则返回false。具体代码如下:(为了避免内容重复冗余,实现栈功能的代码不再呈现在下面,可自行往上翻找查看“Stack.c文件”的内容)
定义栈的结构:
typedef char STDataType;
typedef struct Stack
{
STDataType* arr;
int top;//指向栈顶的位置,也表示有效元素个数
int capacity;//栈的容量
}ST;
具体解题代码:
//运用数据结构——栈
bool isValid(char* s) {
ST st;
StackInit(&st);//初始化栈
char* pi = s;//遍历字符串
while(*pi != '\0')
{
//若遍历到左括号
if(*pi == '(' || *pi == '[' || *pi == '{')
{
StackPush(&st,*pi);//左括号入栈
}
//若遍历到右括号
else
{
//若此时栈为空,只有右括号则不可能形成有效字符串
if(StackEmpty(&st))
{
StackDestroy(&st);
return false;
}
//栈不为空
char top = StackTop(&st);
if((top == '(' && *pi != ')')
|| (top == '[' && *pi != ']')
|| (top == '{' && *pi != '}'))
{
//遍历到的任一右括号如果与栈顶的左括号不匹配,则也不可能形成有效字符串
StackDestroy(&st);
return false;
}
//右括号与栈顶匹配成功,则栈顶的左括号出栈,更换新的栈顶继续匹配
StackPop(&st);
}
//匹配成功后继续往后遍历
pi++;
}
//遍历结束后得考虑栈内元素是否为空,为空方可为有效字符串
bool ret = StackEmpty(&st) ? true : false;
StackDestroy(&st);
return ret;
//以上每个“return”前记得销毁栈
}
2.用队列实现栈
解题思路:
对于队列的基本实现方法这里也不一一展示,可以返回观看Queue.c文件部分的内容。
出栈:要用两个队列实现栈功能,我们知道队列满足先进先出,而栈是先进后出的,假设往一个队列里入队1、2、3、4,正常情况下出队也是按照相同的顺序,但是我们要实现先进后出的栈,因此4必须先出来,所以我们就要借助第二个队列,将前面三个数也就是前面size-1的数入队到第二个空队列中,然后剩余的一个保存后出队,最后返回保存的数即可。
入栈:由出栈的思路可以知道,空队列是用于保存前size-1个数的,因此要入栈只能插入到非空队列中。
取栈顶元素:由于入栈时插入的是不为空的队列,因此直接取不为空队列的队尾元素即可。
先定义队列的结构
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* phead;
QueueNode* ptail;
}Queue;
以下是实现题目要求的功能
1.初始化两个队列
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&pst->q1);
QueueInit(&pst->q2);
2.实现入栈功能
void myStackPush(MyStack* obj, int x) {
//往不为空的队列中插入数据
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else{
QueuePush(&obj->q2,x);
}
}
3.出栈并返回栈顶元素
int myStackPop(MyStack* obj) {
//将不为空队列中前size-1个数据挪到另一个队列中
//再将最后一个数据出队列
Queue* emp = &obj->q1;
Queue* noneEmp = &obj->q2;
if(QueueEmpty(&obj->q2))
{
emp = &obj->q2;
noneEmp = &obj->q1;
}
while(QueueSize(noneEmp)>1)
{
//取非空队列首元素插入空队列
QueuePush(emp,QueueFront(noneEmp));
//插入后将非空队列首元素删除
QueuePop(noneEmp);
}
//此时非空队列只剩下要出队的那个元素
int top = QueueFront(noneEmp);
QueuePop(noneEmp);
return top;
}
4.取栈顶元素
int myStackTop(MyStack* obj) {
//取非空队列的队尾元素
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else{
return QueueBack(&obj->q2);
}
}
5.判断栈是否为空
bool myStackEmpty(MyStack* obj) {
//若两个队列均为空则栈才为空
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
6.栈的销毁
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
obj = NULL;
}
3.用栈实现队列
解题思路:
栈的功能实现函数回看前面Stack.c文件的内容
栈的进出法则是先进后出(后进先出),而队列是先进先出。假设将1、2、3、4压入栈,栈底就是1,栈顶则是4,按照正常情况应该是4先出,而我们要实现队列,则应该1先出,此时我们就要借助另一个栈了,将原栈里面的数全部压入第二个栈,此时会惊奇的发现,第二个栈的栈顶元素变成了1,栈底元素变成了4,就可以1234依次出栈,这不就是我们想要实现出队的顺序吗?那我们想要入队列的时候,数据应该压入哪个栈呢,显然是压入第一个栈,因为第二个栈是要出“队首”的数据,如果新数据压入第二个栈就会出现“插队”的情况。总结起来整体过程就是,数据先入第一个栈,要出队的时候把第一个栈的所有元素压入第二个栈,由第二个栈出数据;想要再插入数据时,插入第一个栈,等到第二个栈的数据全部出完后,第一个栈的所有数据再压入第二个栈,周而复始。可以发现,第一个栈是用来专门“入队列”的,命名为PushST;第二个栈则是专门用来“出队列”的,命名为PopST。
定义栈的结构
typedef int STDataType;
typedef struct Stack
{
STDataType*arr;
int top;
int capacity;
}ST;
1.用两个栈定义队列初始化并返回
typedef struct {
ST PushST;
ST PopST;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&pq->PushST);
StackInit(&pq->PopST);
return pq;
}
2.插入元素到队尾
void myQueuePush(MyQueue* obj, int x) {
//往PushST中插入数据
StackPush(&obj->PushST,x);
}
3.出队列并返回元素
//检查PopST是否为空
//不为空则直接出PopST栈顶
//为空,PushST的数据全部导入PopST,再出PopST栈顶
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->PopST))
{
//导数据
while(StackSize(&obj->PushST))
{
StackPush(&obj->PopST,StackTop(&obj->PushST));
StackPop(&obj->PushST);
}
}
int top = StackTop(&obj->PopST);
StackPop(&obj->PopST);
return top;
}
4.返回队列开头的元素
int myQueuePeek(MyQueue* obj) {
//与出队列操作基本一致,只是不需要删除元素
if(StackEmpty(&obj->PopST))
{
//导数据
while(StackSize(&obj->PushST))
{
StackPush(&obj->PopST,StackTop(&obj->PushST));
StackPop(&obj->PushST);
}
}
//直接返回即可
return StackTop(&obj->PopST);
}
5.判断队列是否为空
bool myQueueEmpty(MyQueue* obj) {
//两个栈均为空,则队列才为空
return StackEmpty(&obj->PushST) && StackEmpty(&obj->PopST);
}
6.销毁队列
void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->PushST);
StackDestroy(&obj->PopST);
free(obj);
obj = NULL;
}