栈是一种特殊的线性表,只允许在线性表的一段插入或删除数据,这一段成为栈顶,另一端称为栈底。所以,栈具有后入先出的性质(last in first out)。
由于,用链表和顺序表的性能相差不大,下面我们用顺序表来实现。
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDateType;
typedef struct Stack
{
STDateType* a;
int capcity;
int top;
}Stack;
//初始化
void StackInit(Stack* ps);
//销毁
void StackDestroy(Stack* ps);
//压栈
void StackPush(Stack* ps, STDateType x);
//弹栈
void StackPop(Stack* ps);
//取栈顶
STDateType StackTop(Stack* ps);
//判空
bool StackEmpty(Stack* ps);
//栈的大小
int StackSize(Stack* ps);
void StackInit(Stack* ps)
{
assert(ps);
ps->a = (STDateType*)malloc(sizeof(STDateType) * 4);
if (!ps->a)
{
perror("malloc fail");
exit(-1);
}
ps->capcity = 4;
ps->top = -1;
}
top为栈顶元素的下标。
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capcity = 0;
ps->top = -1;
}
void StackPush(Stack* ps, STDateType x)
{
assert(ps);
if (ps->top + 1 == ps->capcity)
{
STDateType* tmp = (STDateType*)realloc(ps->a,sizeof(STDateType) * ps->capcity * 2);
if (!tmp)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capcity *= 2;
}
ps->top++;
ps->a[ps->top] = x;
}
注意: 顺序表实现栈的时候,压栈前首先检验空间是否充足。
void StackPop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
注意: 弹栈前,要注意是否为空栈。
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == -1;
}
STDateType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top];
}
注意: 取栈顶元素前,先判断栈是否为空栈。
int StackSize(Stack* ps)
{
assert(ps);
return ps->top + 1;
}
队列也是特殊的线性表,只允许在队头删除数据,在队尾插入数据。
因此,队列具有先进先出的性质(first in first out)。
由于,使用顺序表实现队列时,我们在删除队头数据时需要大量移动数据,性能较低,所以我们采用链表实现队列。
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDateType;
typedef struct QueueNode
{
QDateType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* front;
QNode* rear;
int size;
}Queue;
//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);
//插入
void QueuePush(Queue* pq, QDateType x);
//删除
void QueuePop(Queue* pq);
//判空
bool QueueEmpty(Queue* pq);
//队列大小
int QueueSize(Queue* pq);
//取队头
QDateType QueueFront(Queue* pq);
//取队尾
QDateType QueueBack(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->front = NULL;
pq->rear = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->front;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->front = NULL;
pq->rear = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDateType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (!newnode)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->rear == NULL)
{
pq->front = newnode;
pq->rear = newnode;
}
else
{
pq->rear->next = newnode;
pq->rear = newnode;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->front->next == NULL)
{
free(pq->front);
pq->front = NULL;
pq->rear = NULL;
}
else
{
QNode* next = pq->front->next;
free(pq->front);
pq->front = next;
}
pq->size--;
}
注意: 删数据前应先判断队列是否为空。
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->front == NULL && pq->rear == NULL;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
QDateType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->front->data;
}
注意: 取数据前应先判断队列是否为空。
QDateType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->rear->data;
}
这个例子就很适合用栈这种数据结构来解决,非常符号后进先出的特点。
分析: 遇到左括号我们就压栈,遇到右括号我们就取栈顶元素与之匹配,若匹配成功就继续,知道栈为空并且字符串遍历到’\0’,就证明匹配成功。
匹配不成功的场景有三种,应注意:
1,右括号多于左括号
2,左括号多于右括号
3,当前右括号与栈顶的左括号不匹配
下面看代码:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDateType;
typedef struct Stack
{
STDateType* a;
int capcity;
int top;
}Stack;
//初始化
void StackInit(Stack* ps);
//销毁
void StackDestroy(Stack* ps);
//压栈
void StackPush(Stack* ps, STDateType x);
//弹栈
void StackPop(Stack* ps);
//取栈顶
STDateType StackTop(Stack* ps);
//判空
bool StackEmpty(Stack* ps);
//栈的大小
int StackSize(Stack* ps);
void StackInit(Stack* ps)
{
assert(ps);
ps->a = (STDateType*)malloc(sizeof(STDateType) * 4);
if (!ps->a)
{
perror("malloc fail");
exit(-1);
}
ps->capcity = 4;
ps->top = -1;
}
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capcity = 0;
ps->top = -1;
}
void StackPush(Stack* ps, STDateType x)
{
assert(ps);
if (ps->top + 1 == ps->capcity)
{
STDateType* tmp = (STDateType*)realloc(ps->a,sizeof(STDateType) * ps->capcity * 2);
if (!tmp)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capcity *= 2;
}
ps->top++;
ps->a[ps->top] = x;
}
void StackPop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
STDateType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top];
}
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == -1;
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->top + 1;
}
bool isValid(char * s){
Stack st;
StackInit(&st);
//开始遍历字符串,左括号进栈,右括号与栈顶匹配
while(*s)
{
if(*s=='{'||*s=='('||*s=='[')
{
StackPush(&st,*s);
s++;
}
else
{
if(StackEmpty(&st))
{
StackDestroy(&st);
return false;
}
char tmp=StackTop(&st);
StackPop(&st);
if((*s==')'&&tmp!='(')||
(*s==']'&&tmp!='[')||
(*s=='}'&&tmp!='{'))
{
StackDestroy(&st);
return false;
}
s++;
}
}
if(StackEmpty(&st))
{
StackDestroy(&st);
return true;
}
StackDestroy(&st);
return false;
}
分析: 由于从一个栈的数据导入到另一个栈中,会有使数据倒置的性质。
所以,我们就用一个push栈和一个pop栈来实现,其中插入数据都进入到push栈中,pop数据都从pop栈中,其中如果pop栈为空的话,我们就把push栈中的数据导入到pop栈当中。
下面看代码:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDateType;
typedef struct Stack
{
STDateType* a;
int capcity;
int top;
}Stack;
//初始化
void StackInit(Stack* ps);
//销毁
void StackDestroy(Stack* ps);
//压栈
void StackPush(Stack* ps, STDateType x);
//弹栈
void StackPop(Stack* ps);
//取栈顶
STDateType StackTop(Stack* ps);
//判空
bool StackEmpty(Stack* ps);
//栈的大小
int StackSize(Stack* ps);
void StackInit(Stack* ps)
{
assert(ps);
ps->a = (STDateType*)malloc(sizeof(STDateType) * 4);
if (!ps->a)
{
perror("malloc fail");
exit(-1);
}
ps->capcity = 4;
ps->top = -1;
}
void StackDestroy(Stack* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capcity = 0;
ps->top = -1;
}
void StackPush(Stack* ps, STDateType x)
{
assert(ps);
if (ps->top + 1 == ps->capcity)
{
STDateType* tmp = (STDateType*)realloc(ps->a,sizeof(STDateType) * ps->capcity * 2);
if (!tmp)
{
perror("realloc fail");
exit(-1);
}
ps->a = tmp;
ps->capcity *= 2;
}
ps->top++;
ps->a[ps->top] = x;
}
void StackPop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
STDateType StackTop(Stack* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->a[ps->top];
}
bool StackEmpty(Stack* ps)
{
assert(ps);
return ps->top == -1;
}
int StackSize(Stack* ps)
{
assert(ps);
return ps->top + 1;
}
typedef struct {
Stack pushst;
Stack popst;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* tmp=(MyQueue*)malloc(sizeof(MyQueue));
StackInit(&tmp->pushst);
StackInit(&tmp->popst);
return tmp;
}
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->pushst,x);
}
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->popst))
{
while(!StackEmpty(&obj->pushst))
{
StackPush(&obj->popst,StackTop(&obj->pushst));
StackPop(&obj->pushst);
}
}
int ret=StackTop(&obj->popst);
StackPop(&obj->popst);
return ret;
}
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->popst))
{
while(!StackEmpty(&obj->pushst))
{
StackPush(&obj->popst,StackTop(&obj->pushst));
StackPop(&obj->pushst);
}
}
int ret=StackTop(&obj->popst);
return ret;
}
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->pushst)&&StackEmpty(&obj->popst);
}
void myQueueFree(MyQueue* obj) {
StackEmpty(&obj->pushst);
StackEmpty(&obj->popst);
free(obj);
}
分析: 队列的性质是先进先出,要想pop出栈顶元素,那么我们可以将有数据的队列中的数据挪到另一个队列中,只剩一个数据,然后弹出。
例如:进栈的顺序为 1 2 3 4 的一组数据
我们要pop出栈顶数据,那么
将4弹出即可。
但是在入栈的时候要注意,要将数据压入到有数据的一个队列中。
下面看代码:
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* front;
QNode* back;
int len;
}Queue;
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->back = NULL;
pq->front = NULL;
pq->len = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->front;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->back = pq->front = NULL;
pq->len = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (!newnode)
{
perror("malloc fail");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->back == NULL)
{
pq->front = pq->back = newnode;
}
else
{
pq->back->next = newnode;
pq->back = newnode;
}
pq->len++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->front->next == NULL)
{
free(pq->front);
pq->back = pq->front = NULL;
}
else
{
QNode* next = pq->front->next;
free(pq->front);
pq->front = next;
}
pq->len--;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->back == NULL && pq->front == NULL;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->len;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->front->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->back->data;
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* tmp=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&tmp->q1);
QueueInit(&tmp->q2);
return tmp;
}
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
int myStackPop(MyStack* obj) {
QNode* emptyQ=&obj->q1;
QNode* nonemptyQ=&obj->q2;
if(!QueueEmpty(&obj->q1))
{
nonemptyQ=&obj->q1;
emptyQ=&obj->q2;
}
//将非空的队列数据移动到空的队列中,就剩最后一个数据
while(QueueSize(nonemptyQ)>1)
{
QueuePush(emptyQ,QueueFront(nonemptyQ));
QueuePop(nonemptyQ);
}
int ret=QueueFront(nonemptyQ);
QueuePop(nonemptyQ);
return ret;
}
int myStackTop(MyStack* obj) {
QNode* emptyQ=&obj->q1;
QNode* nonemptyQ=&obj->q2;
if(!QueueEmpty(&obj->q1))
{
nonemptyQ=&obj->q1;
emptyQ=&obj->q2;
}
return QueueBack(nonemptyQ);
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
分析: 如果使用单项循环链表来实现循环队列的话:
当front指针与rear指针相等时,就是队列为空的时候。所以rear指针就指向了最后一个结点的下一个结点,这样我们在获取队尾数据时就会很不方便。
所以,我们采用顺序表来实现循环队列:
顺序表的优势就在于支持下标的随机访问,我们在pop数据时,front指针也会随之变化,这样就不会出现假溢出现象。
应注意的是,顺序表如何实现循环呢?
这个好解决,无论front好事rear指针走到顺序表尾的时候,再运动的时候,就需要(front+1)%(顺序表长度)达到循环的效果。
还有一个问题就是当队列满的时候也是rear等于front,所以我们就少使用一个位置,
当front领先rear并且,他们中间空了一个结点时就为满
如图:
typedef struct {
int* a;
int front;
int rear;
int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* tmp=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
tmp->a=(int*)malloc(sizeof(int)*(k+1));
tmp->front=tmp->rear=0;
tmp->k=k;
return tmp;
}
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+1)%(obj->k+1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return false;
}
obj->front=(obj->front+1)%(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);
}
谢谢观看,欢迎指正。