目录
前言
栈和队列是一种重要的数据结构,本文章将介绍栈和队列的结构以及一些关于栈和队列的面试题。
一、栈
1.1 栈的概念和结构
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
1.2 栈的实现
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
以下是栈实现的代码
stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef char DataType;
typedef struct stack
{
DataType *data;
int top;
int capacity;
}ST;
//初始化栈
void STInit(ST* ps);
//销毁栈
void STDestroy(ST* ps);
//插入
void STPush(ST* ps,DataType x);
//删除
void STPop(ST* ps);
//取栈顶元素
DataType STTop(ST* ps);
//栈的大小
int STSize(ST* ps);
//栈是否为空
bool STEmpty(ST* ps);
stack.c
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
void STInit(ST* ps)
{
assert(ps);
ps->data = NULL;
ps->capacity =0;
ps->top = 0;
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->data);
ps->data = NULL;
ps->top = 0;
ps->capacity = 0;
}
void STPush(ST* ps,DataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
int newsize = ps->capacity == 0 ? 4 : ps->capacity * 2;
DataType* tmp = (DataType*)realloc(ps->data,sizeof(DataType)*newsize);
if (tmp == NULL)
{
perror("realloc");
exit(-1);
}
ps->data = tmp;
ps->capacity = newsize;
}
ps->data[ps->top] = x;
ps->top++;
}
//删除
void STPop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
ps->top--;
}
//取栈顶元素
DataType STTop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->data[ps->top-1];
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
二、队列
2.1 队列的概念和结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
2.2 队列的实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
![](https://img-blog.csdnimg.cn/556cac8961fb4a4092978b86fd093aa6.png)
Queue.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int DataType;
typedef struct QueueNode
{
struct QueueNode* next;
DataType data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Que;
//队列的初始化
void QueueInit(Que* pq);
//销毁队列
void QueueDestroy(Que* pq);
//入队
void QueuePush(Que* pq, DataType x);
//出队
void QueuePop(Que* pq);
//取队首元素
DataType QueueFront(Que* pq);
//取队尾元素
DataType QueueBack(Que* pq);
//判断对是否为空
bool QueueEmpty(Que* pq);
//队的大小
int QueueSize(Que* pq);
Queue.c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"
void QueueInit(Que* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestroy(Que* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueuePush(Que* pq,DataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
pq->size++;
}
DataType QueueFront(Que* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
void QueuePop(Que* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* newnode = pq->head->next;
free(pq->head);
pq->head = newnode;
}
pq->size--;
}
DataType QueueBack(Que* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
bool QueueEmpty(Que* pq)
{
assert(pq);
return pq->head == NULL;
}
int QueueSize(Que* pq)
{
assert(pq);
return pq->size;
}
typedef struct {
Que* q1;
Que* q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* st = (MyStack*)malloc(sizeof(MyStack));
QueueInit(st->q1);
QueueInit(st->q2);
return st;
}
void myStackPush(MyStack* st, int x) {
if (!QueueEmpty(st->q1))
{
QueuePush(st->q1, x);
}
else
{
QueuePush(st->q2, x);
}
}
int myStackPop(MyStack* st) {
Que* empty = st->q1;
Que* noempty = st->q2;
if (!QueueEmpty(st->q1))
{
noempty = st->q1;
empty = st->q2;
}
while (QueueSize(noempty) > 1)
{
QueuePush(empty, QueueFront(noempty));
QueuePop(noempty);
}
int top = QueueFront(noempty);
QueuePop(noempty);
return top;
}
int myStackTop(MyStack* st) {
if (QueueEmpty(st->q1))
{
QueueBack(st->q1);
}
else
{
QueueBack(st->q2);
}
}
bool myStackEmpty(MyStack* st) {
return QueueEmpty(st->q1) && QueueEmpty(st->q2);
}
void myStackFree(MyStack* st) {
QueueDestroy(st->q1);
QueueDestroy(st->q2);
free(st);
}
2.3 循环队列
实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型
时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。
![](https://img-blog.csdnimg.cn/1b96cf73678f44299f1d8625a2ab2511.png)
循环队列的实现:
typedef struct {
int *a;
int front;
int rear;
int k;
} 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->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);
}
三、栈和队列的面试题
3.1 括号匹配问题。OJ链接
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串s
,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
本题使用栈来实现,栈的实现代码前面已经给过了,在后面加上解题代码:
bool isValid(char* s) {
ST s1;
STInit(&s1);
char ch;
while (*s)
{
if (*s=='('||*s=='['||*s=='{')
{
STPush(&s1, *s);
}
else
{
if (STEmpty(&s1))
{
STDestroy(&s1);
return false;
}
ch = STTop(&s1);
STPop(&s1);
if((*s == '}' && ch != '{')
||(*s == ']' && ch != '[')
||(*s == ')' && ch != '('))
{
STDestroy(&s1);
return false;
}
}
++s;
}
bool ret = STEmpty(&s1);
STDestroy(&s1);
return ret;
}
3.2 用队列实现栈。OJ链接
实现
MyStack
类:
void push(int x)
将元素 x 压入栈顶。int pop()
移除并返回栈顶元素。int top()
返回栈顶元素。boolean empty()
如果栈是空的,返回true
;否则,返回false
。
队列实现代码前面也有,下面是实现栈的函数:
typedef struct {
Que q1;
Que q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&pst->q1);
QueueInit(&pst->q2);
return pst;
}
void myStackPush(MyStack* st, int x) {
if (!QueueEmpty(&st->q1))
{
QueuePush(&st->q1, x);
}
else
{
QueuePush(&st->q2, x);
}
}
int myStackPop(MyStack* st) {
Que* empty = &st->q1;
Que* noempty = &st->q2;
if (!QueueEmpty(&st->q1))
{
noempty = &st->q1;
empty = &st->q2;
}
while (QueueSize(noempty) > 1)
{
QueuePush(empty, QueueFront(noempty));
QueuePop(noempty);
}
int top = QueueFront(noempty);
QueuePop(noempty);
return top;
}
int myStackTop(MyStack* st) {
if (!QueueEmpty(&st->q1))
{
return QueueBack(&st->q1);
}
else
{
return QueueBack(&st->q2);
}
}
bool myStackEmpty(MyStack* st) {
return QueueEmpty(&st->q1) && QueueEmpty(&st->q2);
}
void myStackFree(MyStack* st) {
QueueDestroy(&st->q1);
QueueDestroy(&st->q2);
free(st);
}
3.3 用栈实现队列。OJ链接
实现
MyQueue
类:
void push(int x)
将元素 x 推到队列的末尾int pop()
从队列的开头移除并返回元素int peek()
返回队列开头的元素boolean empty()
如果队列为空,返回true
;否则,返回false
typedef struct {
ST pushst;
ST popst;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue *obj=(MyQueue*)malloc(sizeof(MyQueue));
STInit(&obj->pushst);
STInit(&obj->popst);
return obj;
}
void myQueuePush(MyQueue* obj, int x) {
STPush(&obj->pushst,x);
}
int myQueuePop(MyQueue* obj) {
if(STEmpty(&obj->popst))
{
while(!STEmpty(&obj->pushst))
{
STPush(&obj->popst,STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
int tmp=STTop(&obj->popst);
STPop(&obj->popst);
return tmp;
}
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);
}
3.4 设计循环队列。OJ链接
你的实现应该支持如下操作:
MyCircularQueue(k)
: 构造器,设置队列长度为 k 。Front
: 从队首获取元素。如果队列为空,返回 -1 。Rear
: 获取队尾元素。如果队列为空,返回 -1 。enQueue(value)
: 向循环队列插入一个元素。如果成功插入则返回真。deQueue()
: 从循环队列中删除一个元素。如果成功删除则返回真。isEmpty()
: 检查循环队列是否为空。isFull()
: 检查循环队列是否已满。
循环队列代码2.3已经给过,大家可以尝试实现。