数据结构:栈和队列

目录

文章目录

前言

一、栈

1.1 栈的概念和结构

1.2 栈的实现

二、队列

2.1 队列的概念和结构

 2.2 队列的实现

2.3 循环队列

 三、栈和队列的面试题

3.1 括号匹配问题。OJ链接

3.2 用队列实现栈。OJ链接

 3.3 用栈实现队列。OJ链接

 3.4 设计循环队列。OJ链接


前言

栈和队列是一种重要的数据结构,本文章将介绍栈和队列的结构以及一些关于栈和队列的面试题。


一、栈

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 队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

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 循环队列

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型
时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

循环队列的实现:

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 ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

本题使用栈来实现,栈的实现代码前面已经给过了,在后面加上解题代码:

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已经给过,大家可以尝试实现。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值