栈和队列面试题

目录

一:括号匹配问题

 二:用队列实现栈

三:用栈实现队列

 四:设置循环队列


一:括号匹配问题

🍑原题链接:

力扣

🤔题目详情:

🙋🏻‍♂️思路:

括号匹配问题为栈的经典例题。因为栈遵循的规则是后进先出,所以对于此道题我们可以创建一个栈,然后开始进行遍历字符串,如果字符为左括号“ ( ”、“ [ ”、“ { ”,则入栈;如果字符为右括号“ ) ”、“ ] ”、“ } ”,则栈中的栈顶元素(左括号)出栈与右括号进行匹配,如果不匹配则报错,匹配则继续遍历匹配,直到遍历完字符串。遍历完后我们还需要检查栈是否为空,如果为空则均匹配,不为空则未完成匹配。

✍🏻题解:

//创建栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a; //存储数据
	int top;       //记录栈顶的位置
	int capacity;  //记录栈的容量
}ST;

//初始化栈
void StackInit(ST* ps);

//销毁栈
void StackDestory(ST* ps);

//压栈(栈规定只能在栈顶插入,所以只有一种插入方式)
void StackPush(ST* ps, STDataType x);

//出栈
void StackPop(ST* ps);

//判断栈中的数据是否为空
bool StackEmpty(ST* ps);

//访问栈顶元素
STDataType StackTop(ST* ps);

//获取栈中有效元素个数
int StackSize(ST* ps);

//初始化栈
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

//销毁栈
void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

//压栈(栈规定数据只能在栈顶插入,所以只有一种插入方式)
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//满了扩容
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ps->a = (STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType));
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}

		ps->capacity = newCapacity; //扩容
	}

	ps->a[ps->top] = x; //数据压栈
	(ps->top)++; //栈顶更新
}

//出栈
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);//如果要出栈,至少要保证栈中有元素
	(ps->top)--;
}

//判断栈中的数据是否为空
bool StackEmpty(ST* ps)
{
	/*法一
	assert(ps);

	if (ps->top > 0)
	{
		return false;
	}
	else
	{
		return true;
	}*/
	//法二
	return ps->top == 0;
}

//访问栈顶元素
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

//获取栈中有效元素个数
int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

//创建完栈开始实现
bool isValid(char* s) {
    ST st;//先创建一个栈
    StackInit(&st);//初始化栈
    while (*s)
    {
        if (*s == '[' || *s == '(' || *s == '{')
        {
            StackPush(&st, *s); //如果是左括号就入栈
            ++s;
        }
        else
        {
            if (StackEmpty(&st))
            {
                return false; //如果栈为空,说明栈中无左括号,必定不匹配,直接返回false
            }
            char top = StackTop(&st); //获取栈顶元素赋值给top
            StackPop(&st); //出栈进行匹配
            if ((*s == ']' && top != '[')
                || (*s == ')' && top != '(')
                || (*s == '}' && top != '{'))
            {
                StackDestory(&st); //此种为不匹配情况,直接销毁栈,防止内存泄漏
                return false; 
            }
            else
            {
                //此种情况为匹配,继续遍历
                ++s;
            }
        }
    }
    //栈为空,说明所有左括号都完成了匹配
    bool ret = StackEmpty(&st);
    StackDestory(&st); //返回前先销毁,防止内存泄漏
    return ret;
}

 二:用队列实现栈

🍑原题链接:

力扣

🤔题目详情:

🙋🏻‍♂️思路:

做这道题前我们必须知道栈与队列的最基本性质:

 栈:后进先出            队列:先进先出

如果我们想要用队列实现栈,我们就需要创建两个队列。

入栈:push数据到不为空的队列。

出栈:把不为空的队列的前N-1个数据导入另外一个空队列,然后将剩下的一个数据删掉。

本质:保持一个队列存储数据,另外一个队列空着,要出栈时,空队列用来导数据。

✍🏻题解:

//创建队列
typedef int QDataType;
typedef struct QueueNode
{
	QDataType data;         //储存数据
	struct QueueNode* next; //储存下一个结点地址
}QNode;

//记录队列头尾结点地址
typedef struct Queue
{
	QNode* head;  //储存头结点地址
	QNode* tail;  //储存尾结点地址
}Queue;

//初始化队列
void QueueInit(Queue* pq);

//销毁队列
void QueueDestory(Queue* pq);

//入列
void QueuePush(Queue* pq, QDataType x);

//出列
void QueuePop(Queue* pq);

//判断队列是否为空
bool QueueEmpty(Queue* pq);

//获取队列中元素个数
size_t QueueSize(Queue* pq);

//获取队头元素
QDataType QueueFront(Queue* pq);

//获取队尾元素
QDataType QueueBack(Queue* pq);


//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);//断言
	pq->head = pq->tail = NULL;//置空
}

//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	//从队头开始遍历销毁结点
	while (cur)
	{
		QNode* next = cur->next;//销毁前保存下一个结点地址
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
}

//入列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建一个新的结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	assert(newnode); //新结点不能为空
	//将所要入列的元素赋值给新创建的结点
	newnode->data = x;
	newnode->next = NULL;
	//队列为空的情况
	if (pq->tail == NULL)
	{
		assert(pq->tail == NULL);//队列为空,其头结点也必定为空
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;//刷新尾结点
	}
}

//出列
void QueuePop(Queue* pq)
{
	assert(pq);

	assert(pq->head && pq->tail);//出列必须保证队列不为空
	//当出列到只剩下一个元素,tail为野指针,需单独判断
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	//return pq->head == NULL && pq->next==NULL;
	return pq->head == NULL;
}

//获取队列中元素个数
size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}

	return size;
}

//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->head->data;
}

//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->tail->data;
}

//队列创建完毕,现在开始模拟实现栈

typedef struct {
	Queue q1; //队列q1
	Queue q2; //队列q2
} MyStack;


MyStack* myStackCreate() {
	MyStack* pst = (MyStack*)malloc(sizeof(MyStack)); //malloc一个模拟实现的栈
	assert(pst);
	QueueInit(&pst->q1);//初始化队列q1
	QueueInit(&pst->q2);//初始化队列q2
	return pst;
}

void myStackPush(MyStack* obj, int x) {
	assert(obj);
	if (!QueueEmpty(&obj->q1))
	{
		QueuePush(&obj->q1, x);//如果q1不为空,就往q1插入数据
	}
	else
	{
		QueuePush(&obj->q2, x);//q1为空,数据直接插入p2
	}
	
}

int myStackPop(MyStack* obj) {
	assert(obj);
	Queue* emptyQ = &obj->q1; //默认q1为空
	Queue* nonEmtpyQ = &obj->q2;//默认q2不为空
	if (!QueueEmpty(&obj->q1))
	{
		emptyQ = &obj->q2; //若假设错误,则q2为空
		nonEmtpyQ = &obj->q1;//q1不为空
	}
	while (QueueSize(nonEmtpyQ) > 1)
	{
		QueuePush(emptyQ, QueueFront(nonEmtpyQ)); //把非空队列数据导到空的队列,直到只剩一个
		QueuePop(nonEmtpyQ); //此时把非空的队头数据给删掉,方便后续导入数据
	}
	int top = QueueFront(nonEmtpyQ); //记录栈顶数据
	QueuePop(nonEmtpyQ); //删除栈顶数据,使该队列置空
	return top;
}

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

}

bool myStackEmpty(MyStack* obj) {
	assert(obj);
	//两个队列均为空,则栈为空
	return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);

}

void myStackFree(MyStack* obj) {
	assert(obj);
	QueueDestory(&obj->q1); //释放q1
	QueueDestory(&obj->q2); //释放q2
	free(obj);
}

三:用栈实现队列

🍑原题链接:

力扣

🤔题目详情:

 🙋🏻‍♂️思路:

和上道题相似的是首先我们需要创建两个栈,在上一道题中我们需要将数据在两个队列中来回导,从而实现栈的后进先出功能。而这一次我们无需来回导,我们创建两个栈pushST和popST,我们入队时将数据放入栈pushST中,假设我们入队元素为1 2 3 4,则将1 2 3 4放入栈pushST中,出队列时我们将pushST中元素导入popST中,因为栈遵循后进先出,所以存入popST中的元素顺序为4 3 2 1,此时popST元素出栈顺序为1 2 3 4,入栈1 2 3 4,出栈1 2 3 4,满足队列的先进先出性质。

 

✍🏻题解:


//创建栈
typedef int STDataType;
typedef struct Stack
{
	STDataType* a; //存储数据
	int top;       //记录栈顶的位置
	int capacity;  //记录栈的容量
}ST;

//初始化栈
void StackInit(ST* ps);

//销毁栈
void StackDestory(ST* ps);

//压栈(栈规定只能在栈顶插入,所以只有一种插入方式)
void StackPush(ST* ps, STDataType x);

//出栈
void StackPop(ST* ps);

//判断栈中的数据是否为空
bool StackEmpty(ST* ps);

//访问栈顶元素
STDataType StackTop(ST* ps);

//获取栈中有效元素个数
int StackSize(ST* ps);

//初始化栈
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

//销毁栈
void StackDestory(ST* ps)
{
	assert(ps);
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

//压栈(栈规定数据只能在栈顶插入,所以只有一种插入方式)
void StackPush(ST* ps, STDataType x)
{
	assert(ps);
	//满了扩容
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ps->a = (STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType));
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}

		ps->capacity = newCapacity; //扩容
	}

	ps->a[ps->top] = x; //数据压栈
	(ps->top)++; //栈顶更新
}

//出栈
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);//如果要出栈,至少要保证栈中有元素
	(ps->top)--;
}

//判断栈中的数据是否为空
bool StackEmpty(ST* ps)
{
	/*法一
	assert(ps);

	if (ps->top > 0)
	{
		return false;
	}
	else
	{
		return true;
	}*/
	//法二
	return ps->top == 0;
}

//访问栈顶元素
STDataType StackTop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	return ps->a[ps->top - 1];
}

//获取栈中有效元素个数
int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

//创建好栈,开始用栈实现队列


typedef struct {
	ST pushST;//保存数据的栈
	ST popST; //删除数据的栈
} MyQueue;

//创建队列
MyQueue* myQueueCreate() {
	MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
	assert(obj);
	//初始化栈
	StackInit(&obj->pushST);
	StackInit(&obj->popST);
	return obj;
}

//入队列
void myQueuePush(MyQueue* obj, int x) {
	assert(obj);
	StackPush(&obj->pushST, x);//将数据储存在栈pushST中
}

//出队列
int myQueuePop(MyQueue* obj) {
	assert(obj);
	//如果栈popST中的数据为空,则需从pushST中导入数据才能删除
	if (StackEmpty(&obj->popST))
	{
		//将pushST中的数据全部导入popST中
		while (!StackEmpty(&obj->pushST))
		{
			StackPush(&obj->popST, StackTop(&obj->pushST));//把pushST栈顶数据导入popST中
			StackPop(&obj->pushST);//pushST栈顶数据导入popST后将栈顶数据删除,方便后续数据的导入
		}
	}

	int front = StackTop(&obj->popST);//记录popST栈顶元素
	StackPop(&obj->popST);//删除popST栈顶数据
	return front;//返回栈顶数据
}

//取对头数据
int myQueuePeek(MyQueue* obj) {
	assert(obj);
	//如果栈popST中的数据为空,则需从pushST中导入数据
	if (StackEmpty(&obj->popST))
	{
		//将pushST中的数据全部导入popST中
		while (!StackEmpty(&obj->pushST))
		{
			StackPush(&obj->popST, StackTop(&obj->pushST));//把pushST栈顶数据导入popST中
			StackPop(&obj->pushST);//pushST栈顶数据导入popST后将栈顶数据删除,方便后续数据的导入
		}
	}
	return StackTop(&obj->popST);//返回栈顶元素
}

//判断队列是否为空
bool myQueueEmpty(MyQueue* obj) {
	//队列为空则两个栈也为空
	return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST);
}

void myQueueFree(MyQueue* obj) {
	assert(obj);
	StackDestory(&obj->pushST);
	StackDestory(&obj->popST);
	free(obj);
}

 四:设置循环队列

🍑原题链接:

力扣

🤔题目详情:

  🙋🏻‍♂️思路:

循环队列的实现可以用数组结构或者链表结构,用数组结构相对方便一些。

创建两个变量head和tail分别记录队头和队尾。刚开始head和tail都指向数组下标为0的元素,插入一个数据tail向后移动一位,tail始终为队列最后一个有效数据的下一位,而删除数据也只需要将head向后挪即可。当tail指定的数据为数组的最后一个下标,插入数据后tail重新指向0下标。

 存在问题:

对于上面的方法还有一个弊端,那就是我们无法判断什么情况下数组为空,什么情况下数组已满。

当head=tail时有两种情况,一种情况是循环队列为空,另一种情况是队列已满。

 解决方案:

我们可以在数组中空出来一个空间不存放数据来区别数组为空或满。比如我们开辟一个可以存放四个数据的数组,那么其有效存储数据个数为3。head=tail时为空,tail的下一个位置是head时为满。

✍🏻题解:

typedef struct {
	int* a;     //创建数组模拟循环队列
	int head;   //记录队头
	int tail;   //记录队尾
	int k;      //表示数组一个循环所能储存的最大元素个数
} MyCircularQueue;

//声明函数
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);

//创建循环队列
MyCircularQueue* myCircularQueueCreate(int k) {
	MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	assert(obj);
	//初始化队列
	obj->a = (int*)malloc(sizeof(int) * (k + 1));//多开一个空间,方便区分队空和队满
	obj->head = obj->tail = 0;
	obj->k = k; //循环队列一个循环能存k个数据
	return obj;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	if (myCircularQueueIsFull(obj))
	{
		return false;//队列已满,不能插入数据
	}
	obj->a[obj->tail] = value;//插入数据
	if (obj->tail == obj->k)
	{
		obj->tail = 0;//当tail走到尾端tail重新指向数组下标为0的位置
	}
	else
	{
		(obj->tail)++;
	}
	return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))
	{
		return false;//队列为空,不能删除
	}
	if (obj->head == obj->k)
	{
		obj->head = 0;
	}
	else
	{
		(obj->head)++;
	}
	return true;
}

//取队头元素
int myCircularQueueFront(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;//队列不能为空
	}
	return obj->a[obj->head];//返回队头元素
}

//取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;//队列不能为空
	}
	if (obj->tail == 0)
	{
		return obj->a[obj->k];//tail为0,队尾在长度的最后一个位置
	}
	else
	{
		return obj->a[obj->tail - 1];
	}
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	return obj->head == obj->tail;//当head==tail时为空
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
	//head在头端,tali在尾端队列为满
	if (obj->head == 0 && obj->tail == obj->k)
	{
		return true;
	}
	else
	{
		return obj->tail + 1 == obj->head;//一般情况下,当tail的下一个位置为head为满
	}
}

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

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 27
    评论
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值