栈,存储货物或供旅客住宿的地方,可引申为仓库、中转站,所以引入到计算机领域里,就是指数据暂时存储的地方,所以才有进栈、出栈的说法。可以类比相当于吃饭,吃进去吐出来就是栈(忍着胃部强烈不适码了这句)
栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
栈的特性:后进先出
注意:栈不能进行遍历操作
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的
代价比较小。
Stack.h
#pragma once
typedef int DataType;
typedef struct Stcak
{
DataType *arr;
int capacity; //容量大小
int size; //有效元素个数---栈顶
}Stack;
//栈的初始化
void StackInit(Stack *ps);
//入栈
void StackPush(Stack *ps, DataType data);
//出栈
void StackPop(Stack *ps);
//获取栈顶元素
DataType StackTop(Stack *ps);
//获取栈的大小
int StackSize(Stack *ps);
//判断栈内是否有元素
int StackEmpty(Stack *ps);
//销毁栈
void StackDestroy(Stack *ps);
void TestStack();
Stack.c
#include"Stack.h"
#include
#include
#include
//栈的初始化
void StackInit(Stack *ps)
{
assert(ps);
ps->arr = (DataType *)malloc(sizeof(DataType)* 3);
if (NULL == ps->arr) //检测空间是否申请成功
{
assert(0);
return;
}
ps->capacity = 3;;
ps->size = 0;
}
//检查容量
void CheckCapacity(Stack *ps)
{
if (ps->size == ps->capacity)
{
ps->arr = (DataType*)realloc(ps->arr, sizeof(DataType)*ps->capacity * 2);
if (NULL == ps->arr) //检测空间是否申请成功
{
assert(0);
return;
}
ps->capacity *= 2;
}
}
//入栈
void StackPush(Stack *ps, DataType data)
{
assert(ps);
CheckCapacity(ps); //扩容
ps->arr[ps->size++] = data;
}
//出栈
void StackPop(Stack *ps)
{
assert(ps);
if (StackEmpty(ps)) //检测栈此时是否为空
return;
ps->size--;
}
//获取栈顶元素
DataType StackTop(Stack *ps)
{
assert(ps && !StackEmpty(ps));
//此处不能使用if条件判断,因为if条件判断的为合法情况
//若是栈为空此时栈没有元素,要获取栈顶元素 则为非法情况
//可以用assert进行判断
//if (StackEmpty(ps))
// return;
return ps->arr[ps->size - 1];
}
//获取栈的大小
int StackSize(Stack *ps)
{
assert(ps);
return ps->size;
}
//判断栈内是否有元素
int StackEmpty(Stack *ps)
{
assert(ps);
return 0 == ps->size;
}
//销毁栈
void StackDestroy(Stack *ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
ps->arr = NULL;
ps->capacity = 0;
ps->size = 0;
}
}
void TestStack()
{
Stack con;
StackInit(&con);
StackPush(&con, 1);
StackPush(&con, 2);
StackPush(&con, 3);
StackPush(&con, 4);
StackPush(&con, 5);
StackPush(&con, 6);
printf("size = %d\n", StackSize(&con));
printf("top = %d\n", StackTop(&con));
StackPop(&con);
StackPop(&con);
StackPop(&con);
printf("size = %d\n", StackSize(&con));
printf("top = %d\n", StackTop(&con));
StackDestroy(&con);
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
int main()
{
TestStack();
return 0;
}
1.括号匹配问题 OJ
typedef char DataType;
typedef struct Stcak
{
DataType *arr;
int capacity; //容量大小
int size; //有效元素个数---栈顶
}Stack;
//栈的初始化
void StackInit(Stack *ps)
{
assert(ps);
ps->arr = (DataType *)malloc(sizeof(DataType)* 3);
if (NULL == ps->arr) //检测空间是否申请成功
{
assert(0);
return;
}
ps->capacity = 3;;
ps->size = 0;
}
//检查容量
void CheckCapacity(Stack *ps)
{
if (ps->size == ps->capacity)
{
ps->arr = (DataType*)realloc(ps->arr, sizeof(DataType)*ps->capacity * 2);
if (NULL == ps->arr) //检测空间是否申请成功
{
assert(0);
return;
}
ps->capacity *= 2;
}
}
//入栈
void StackPush(Stack *ps, DataType data)
{
assert(ps);
CheckCapacity(ps); //扩容
ps->arr[ps->size++] = data;
}
//出栈
void StackPop(Stack *ps)
{
assert(ps);
if (StackEmpty(ps)) //检测栈此时是否为空
return;
ps->size--;
}
//获取栈顶元素
DataType StackTop(Stack *ps)
{
//assert(ps);
assert(ps && !StackEmpty(ps));
//此处不能使用if条件判断,因为if条件判断的为合法情况
//若是栈为空此时栈没有元素,要获取栈顶元素 则为非法情况
//可以用assert进行判断
return ps->arr[ps->size - 1];
}
//获取栈的大小
int StackSize(Stack *ps)
{
assert(ps);
return ps->size;
}
//判断栈内是否有元素
int StackEmpty(Stack *ps)
{
assert(ps);
return 0 == ps->size;
}
//销毁栈
void StackDestroy(Stack *ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
ps->arr = NULL;
ps->capacity = 0;
ps->size = 0;
}
}
bool isValid(char * s)
{
bool flag=false;
Stack ms;
StackInit(&ms);
int n=strlen(s);
if(n%2==0)
{
for(int i=0;i<n;i++)//遍历
{
//如果检测该括号为左括号,则入栈
if(s[i]=='('||s[i]=='['||s[i]=='{')
{
StackPush(&ms,s[i]);
}
else
{
//当前取的括号为右括号且此时栈刚好为空
if(StackEmpty(&ms))
{
StackDestroy(&ms);
return false;
}
char top=StackTop(&ms);
if((top=='('&&s[i]==')')||
(top=='['&&s[i]==']')||
(top=='{'&&s[i]=='}'))
{
StackPop(&ms);
}
else
{
break;
}
}
}
if(StackEmpty(&ms))
{
flag=true;
}
StackDestroy(&ms);
return flag;
}
else
{
StackDestroy(&ms);
return false;
}
}
逆波兰表达式又叫做后缀表达式,逆波兰表示法是波兰逻辑学家J・卢卡西维兹(J・ Lukasiewicz)于1929年首先提出的一种表达式的表示方法 [1] 。后来,人们就把用这种表示法写出的表达式称作“逆波兰表达式”。逆波兰表达式把运算量写在前面,把算符写在后面。
它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下:
如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。
相当于吃进去拉出来。
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,进行插入操作的一端称为队尾 出队列,进行删除操作的一端称为队头。
队列的特性:先进先出。
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
Queue.h
#pragma once
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front;
QNode* rear;
}Queue;
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);
Queue.c
#include"Queue.h"
#include
#include
#include
QNode* BuyQueueNode(QDataType data)
{
QNode* node = (QNode *)malloc(sizeof(QNode));
if (NULL == node)
{
assert(0);
return NULL;
}
node->data = data;
node->next = NULL;
return node;
}
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
q->front = q->rear = BuyQueueNode(0);
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
assert(q);
q->rear->next = BuyQueueNode(data);
q->rear = q->rear->next;
}
// 队头出队列
void QueuePop(Queue* q)
{
QNode *delNode = NULL;
if (QueueEmpty(q))
return;
delNode = q->front->next;
q->front->next = delNode->next;
//如果队列中此时只有一个元素,将该元素删除后还需将rear放在front的位置
if (delNode->next == NULL)
q->rear = q->front;
free(delNode);
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(!QueueEmpty(q));
return q->front->next->data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(!QueueEmpty(q));
return q->rear->data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
int count = 0;
QNode* cur = q->front->next;
while (cur)
{
cur = cur->next;
count++;
}
return count;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q)
{
assert(q);
return q->front->next == NULL;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
q->front = cur->next;
free(cur);
cur = q->front;
}
q->front = q->rear = NULL;
}
//测试
void TestQueue()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueuePush(&q, 5);
QueuePush(&q, 6);
printf("size = %d\n", QueueSize(&q));
printf("front = %d\n", QueueFront(&q));
printf("rear = %d\n", QueueBack(&q));
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
printf("size = %d\n", QueueSize(&q));
printf("front = %d\n", QueueFront(&q));
printf("rear = %d\n", QueueBack(&q));
QueuePop(&q);
QueuePop(&q);
printf("size = %d\n", QueueSize(&q));
printf("front = %d\n", QueueFront(&q));
printf("rear = %d\n", QueueBack(&q));
QueuePop(&q);
printf("size = %d\n", QueueSize(&q));
if (QueueEmpty(&q))
{
printf("Empty\n");
}
else
{
printf("Error\n");
}
QueueDestroy(&q);
}
test.c
#include"Queue.h"
int main()
{
TestQueue();
system("pause");
return 0;
}
队列可以用数组Q[1…m]来存储,数组的上界m即是队列所容许的最大容量。在队列的运算中需设两个指针:head,队头指针,指向实际队头元素;tail,队尾指针,指向实际队尾元素的下一个位置。一般情况下,两个指针的初值设为0,这时队列为空,没有元素。数组定义Q[1…10]。Q(i) i=3,4,5,6,7,8。头指针head=2,尾指针tail=8。队列中拥有的元素个数为:L=tail-head。现要让排头的元素出队,则需将头指针加1。即head=head+1这时头指针向上移动一个位置,指向Q(3),表示Q(3)已出队。如果想让一个新元素入队,则需尾指针向上移动一个位置。即tail=tail+1这时Q(9)入队。当队尾已经处理在最上面时,即tail=10,如果还要执行入队操作,则要发生"上溢",但实际上队列中还有三个空位置,所以这种溢出称为"假溢出"。
克服假溢出的方法有两种。一种是将队列中的所有元素均向低地址区移动,显然这种方法是很浪费时间的;另一种方法是将数组存储区看成是一个首尾相接的环形区域。当存放到n地址后,下一个地址就"翻转"为1。在结构上采用这种技巧来存储的队列称为循环队列。
1.用队列模拟实现栈 OJ
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* next;
QDataType data;
}QNode;
// 队列的结构
typedef struct Queue
{
QNode* front;
QNode* rear;
}Queue;
QNode* BuyQueueNode(QDataType data)
{
QNode* node = (QNode *)malloc(sizeof(QNode));
if (NULL == node)
{
assert(0);
return NULL;
}
node->data = data;
node->next = NULL;
return node;
}
// 初始化队列
void QueueInit(Queue* q)
{
assert(q);
q->front = q->rear = BuyQueueNode(0);
}
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
assert(q);
q->rear->next = BuyQueueNode(data);
q->rear = q->rear->next;
}
// 队头出队列
void QueuePop(Queue* q)
{
QNode *delNode = NULL;
if (QueueEmpty(q))
return;
delNode = q->front->next;
q->front->next = delNode->next;
//如果队列中此时只有一个元素,将该元素删除后还需将rear放在front的位置
if (delNode->next == NULL)
q->rear = q->front;
free(delNode);
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
assert(!QueueEmpty(q));
return q->front->next->data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
assert(!QueueEmpty(q));
return q->rear->data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
assert(q);
int count = 0;
QNode* cur = q->front->next;
while (cur)
{
count++;
cur = cur->next;
}
return count;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0
int QueueEmpty(Queue* q)
{
assert(q);
return q->front->next == NULL;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
assert(q);
QNode* cur = q->front;
while (cur)
{
q->front = cur->next;
free(cur);
cur = q->front;
}
//free(q);
q->front = q->rear = NULL;
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
/** Initialize your data structure here. */
MyStack* myStackCreate() {
//不能使用MyStack ms直接创建局部变量,因为函数执行结束局部变量会被销毁,因此需要从堆上动态申请空间
MyStack *ms=(MyStack *)malloc(sizeof(MyStack));
if(NULL==ms)
{
return NULL;
}
QueueInit(&ms->q1);
QueueInit(&ms->q2);
return ms;
}
/** Push element x onto stack. */
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
/** Removes the element on top of the stack and returns that element. */
int myStackPop(MyStack* obj) {
int ret=0;
if(!QueueEmpty(&obj->q1))
{
//将q1中前N-1个元素放入q2
while(QueueSize(&obj->q1)>1)
{
QueuePush(&obj->q2,QueueFront(&obj->q1));
QueuePop(&obj->q1);
}
//此时q1中只剩一个元素
ret=QueueFront(&obj->q1);
//删除最后一个元素
QueuePop(&obj->q1);
}
else{
while(QueueSize(&obj->q2)>1)
{
QueuePush(&obj->q1,QueueFront(&obj->q2));
QueuePop(&obj->q2);
}
//此时q2中只剩一个元素
ret=QueueFront(&obj->q2);
QueuePop(&obj->q2);
}
return ret;
}
/** Get the top element. */
int myStackTop(MyStack* obj) {
if(!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
/** Returns whether the stack is empty. */
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
2.用栈模拟实现队列 OJ
typedef int DataType;
typedef struct Stcak
{
DataType *arr;
int capacity; //容量大小
int size; //有效元素个数---栈顶
}Stack;
//栈的初始化
void StackInit(Stack *ps)
{
assert(ps);
ps->arr = (DataType *)malloc(sizeof(DataType)* 3);
if (NULL == ps->arr) //检测空间是否申请成功
{
assert(0);
return;
}
ps->capacity = 3;;
ps->size = 0;
}
//检查容量
void CheckCapacity(Stack *ps)
{
if (ps->size == ps->capacity)
{
ps->arr = (DataType*)realloc(ps->arr, sizeof(DataType)*ps->capacity * 2);
if (NULL == ps->arr) //检测空间是否申请成功
{
assert(0);
return;
}
ps->capacity *= 2;
}
}
//入栈
void StackPush(Stack *ps, DataType data)
{
assert(ps);
CheckCapacity(ps); //扩容
ps->arr[ps->size++] = data;
}
//出栈
void StackPop(Stack *ps)
{
assert(ps);
if (StackEmpty(ps)) //检测栈此时是否为空
return;
ps->size--;
}
//获取栈顶元素
DataType StackTop(Stack *ps)
{
//assert(ps);
assert(ps && !StackEmpty(ps));
//此处不能使用if条件判断,因为if条件判断的为合法情况
//若是栈为空此时栈没有元素,要获取栈顶元素 则为非法情况
//可以用assert进行判断
return ps->arr[ps->size - 1];
}
//获取栈的大小
int StackSize(Stack *ps)
{
assert(ps);
return ps->size;
}
//判断栈内是否有元素
int StackEmpty(Stack *ps)
{
assert(ps);
return 0 == ps->size;
}
//销毁栈
void StackDestroy(Stack *ps)
{
assert(ps);
if (ps->arr)
{
free(ps->arr);
ps->arr = NULL;
ps->capacity = 0;
ps->size = 0;
}
}
typedef struct {
Stack s1;
Stack s2;
} MyQueue;
/** Initialize your data structure here. */
MyQueue* myQueueCreate() {
MyQueue *mq=(MyQueue *)malloc(sizeof(MyQueue));
if(mq==NULL)
{
return NULL;
}
StackInit(&mq->s1);
StackInit(&mq->s2);
return mq;
}
/** Push element x to the back of queue. */
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->s1,x);
}
/** Removes the element from in front of queue and returns that element. */
int myQueuePop(MyQueue* obj) {
if(StackEmpty(&obj->s2))
{
while(!StackEmpty(&obj->s1))
{
StackPush(&obj->s2,StackTop(&obj->s1));
StackPop(&obj->s1);
}
}
int ret=StackTop(&obj->s2);
StackPop(&obj->s2);
return ret;
}
/** Get the front element. */
int myQueuePeek(MyQueue* obj) {
if(StackEmpty(&obj->s2))
{
while(!StackEmpty(&obj->s1))
{
StackPush(&obj->s2,StackTop(&obj->s1));
StackPop(&obj->s1);
}
}
return StackTop(&obj->s2);
}
/** Returns whether the queue is empty. */
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->s1)&&StackEmpty(&obj->s2);
}
void myQueueFree(MyQueue* obj) {
StackDestroy(&obj->s1);
StackDestroy(&obj->s2);
free(obj);
}
3.设计循环队列 OJ
typedef struct {
int *arr;//空间
int capacity;//容量
int front;//队头
int rear;//队尾
int count;//有效元素总数
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue *mcq=(MyCircularQueue *)malloc(sizeof(MyCircularQueue));//申请结构体空间
if(NULL==mcq)//判空
{
return NULL;
}
mcq->arr=(int *)malloc(sizeof(int)*k);//申请数组空间
if(mcq->arr==NULL)//判空
{
return NULL;
}
mcq->capacity=k;
mcq->front=mcq->rear=mcq->count=0;
return mcq;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
// if(obj->count==0)
// {
// return true;
// }
// return false;
return obj->count==0;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
// if(obj->count=obj->capacity)
// {
// return true;
// }
// return false;
return obj->count==obj->capacity;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
{
return false;
}
//rear可能在空间末尾,此时若是队首有空间则需要将rear指向0位置
if(obj->rear==obj->capacity)
{
obj->rear=0;
}
obj->arr[obj->rear++]=value;
//obj->rear++;
obj->count++;
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return false;
}
obj->front++;
//front可能在空间末尾,此时若是队首有元素则需要将front指向0位置
if(obj->front==obj->capacity)
{
obj->front=0;
}
obj->count--;//有效元素个数减一
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->arr[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
{
return -1;
}
return obj->arr[(obj->rear-1+obj->capacity)%obj->capacity];
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->arr);
free(obj);
}
关于栈和队列的相关知识点和面试题我这里就只写这么多了吧,以后要是有想到其他的我接着慢慢补充,我初期实现就是这些,要是有不同的观点或者有不同思路的朋友欢迎大家赏光私我哈,本着相互进步的原则,希望大家能多多向我提意见,谢谢~~
栈和队列到此也基本结束了,下一章节----树开始预习,啦啦啦~