栈和队列的实现及相关面试题

栈和队列

    • 概念与结构
    • 栈的功能
    • 栈的实现
      • 头文件Stack.h
        • 栈的结构体 Stack
      • 源文件Stack.c
        • 初始化 void StackInit(Stack* ps)
        • 压栈 void StackPush(Stack* ps, STDataType data)
        • 出栈 void StackPop(Stack* ps)
        • 返回栈顶的值 STDataType StackTop(Stack* ps)
        • 返回栈中元素的个数 int StackSize(Stack* ps)
        • 判断栈是否为空 bool StackEmpty(Stack* ps)
        • 销毁栈 void StackDestroy(Stack* ps)
  • 队列
    • 概念与结构
    • 队列的功能
    • 队列的实现
      • 头文件 Queue.h
        • 队列的结构体 Queue
      • 队列源文件 Queue.c
        • 初始化队列 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)
        • 判断队列是否为空 bool QueueEmpty(Queue* q)
        • 销毁队列 void QueueDestroy(Queue* q)
  • 栈和队列的结合笔试题
      • 力扣20. 有效的括号
        • 解题思路
        • 具体步骤
      • 答案
        • 力扣20.有效的括号

概念与结构

栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

栈的功能

1.进栈(压栈):栈的插入操作,入数据在栈顶,如下图所示:
在这里插入图片描述
2.出栈:栈的删除操作,出数据也在栈顶,如下图所示:
在这里插入图片描述

栈的实现

这里栈的实现,我们可以使用链表或者顺序表的结构来实现,这里我们使用顺序表的结构来实现,因为数组栈相较于链表栈更容易实现。

头文件Stack.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;

void StackInit(Stack* ps);
void StackPush(Stack* ps, STDataType data);

void StackPop(Stack* ps);
STDataType StackTop(Stack* ps);

int StackSize(Stack* ps);
bool StackEmpty(Stack* ps);

void StackDestroy(Stack* ps);
栈的结构体 Stack
typedef int STDataType;

typedef struct Stack
{
	STDataType* _a;
	int _top;		// 栈顶
	int _capacity;  // 容量 
}Stack;

本次变量我们以int类型来进行充当栈的存储数据类型,所以利用typedefSTDataType进行命名
再创建一种结构体类型Stack,这里的Stack结构体与顺序表中的结构体相同,内含一个**STDataType数组类型的成员变量**,一个表示栈顶的下标的整型变量_top,还有一个表示栈空间大小的整型变量_capacity

源文件Stack.c

初始化 void StackInit(Stack* ps)
void StackInit(Stack* ps)
{
	assert(ps);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = -1;
}

先断言确保程序的正常运行。
将数组指针置空以防止野指针的使用,再将数组空间_capacity置为0,栈的下标置为-1,因为此时栈中没有一个数组元素。

压栈 void StackPush(Stack* ps, STDataType data)
void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	
	if (ps->_capacity == ps->_top + 1)
	{
		int newcapacity = !ps->_capacity ? 4 : 2 * ps->_capacity;
		STDataType* tmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newcapacity);
		if(!tmp)
		{
			perror("realloc");
			return;
		}
		ps->_a = tmp;
		tmp = NULL;
		ps->_capacity = newcapacity;
	}

	ps->_top++;
	ps->_a[ps->_top] = data;
}

先断言确保程序的正常运行。
利用if语句对栈的内存空间进行检查,若空间不够就进行扩容,利用三目运算符进行扩容空间大小的选择判断,再利用realloc来为结构体中的数组重新分配空间,为防止分配失败,我们利用新命名的tmp来接收,即使分配失败我们也可以避免返回NULL来覆盖原有的数组ps->_a数据。
确保空间足够后,将栈的下标_top让自增1,再将数据放到数组的相应下标即可。

出栈 void StackPop(Stack* ps)
void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->_top > -1);
	ps->_top--;
}

先断言确保程序的正常运行,栈的下标为-1时数组中没有一个元素,不存在出栈。
这里的出栈只要让ps->_top自减1即可,因为我们是通过下标进行访问的。

返回栈顶的值 STDataType StackTop(Stack* ps)
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->_top > -1);
	return ps->_a[ps->_top];
}

先断言确保程序的正常运行,栈的下标为-1时数组中没有一个元素,也就不存在栈顶的值了。
利用下标直接返回数组的值即可。

返回栈中元素的个数 int StackSize(Stack* ps)
int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top + 1;
}

先断言确保程序的正常运行。
返回数组下标的值加1即可。

判断栈是否为空 bool StackEmpty(Stack* ps)
int StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->_top == -1;
}

先断言确保程序的正常运行。
若为空则返回0,但栈顶下标为-1时,栈为空,所以返回 ps->_top == -1 这判断语句的值即可。

销毁栈 void StackDestroy(Stack* ps)
void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = -1;
}

先断言确保程序的正常运行。
free栈中的数组ps->_a,再将ps->_capacityps->_top置为初始值即可。

队列

概念与结构

队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列要求先进先出
FIFO
(First In First Out)的方式。

队列的功能

  1. 入队列:进行插入操作的一端称为队尾。
  2. 出队列:进行删除操作的一端称为队头。

队列的实现

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

头文件 Queue.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;

typedef struct QListNode
{
	QDataType _data;
	struct QListNode* _next;
}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);

bool QueueEmpty(Queue* q);

void QueueDestroy(Queue* q);
队列的结构体 Queue
typedef struct QListNode
{
	QDataType _data;
	struct QListNode* _next;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
}Queue;
  1. struct QListNode:定义一个链表结点的结构体。
  2. struct Queue:定义一个存放链表头结点_front与尾结点_rear的结构体,这就表示一个队列的结构体。

队列源文件 Queue.c

初始化队列 void QueueInit(Queue* q)
void QueueInit(Queue* q)
{
	assert(q);
	q->_front = q->_rear = NULL;
}

先断言确保程序的正常运行。
将队列中表示链表头结点与尾结点的指针都置为空即可,因为队列中没有任何数据。

队尾入队列 void QueuePush(Queue* q, QDataType data)
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* node = (QNode*)malloc(sizeof(QNode));
	assert(node);
	node->_next = NULL;
	node->_data = data;
	if (q->_front == NULL)
	{
		q->_front = q->_rear = node;
		return;
	}
	q->_rear->_next = node;
	q->_rear = node;
}

先断言确保程序的正常运行。
定义一个node指针,指向malloc所申请的一块大小为QNode的空间,再断言node,以确保node指针的正常使用,再将其初始化即可。
利用if语句判断队列中的头结点是否为空,若为空则将刚刚申请的node赋给队列的头结点与尾结点,然后直接退出函数即可
若不为空,则向链表进行尾插,再将node指向尾结点的指针赋给q->_rear即可

队头出队列 void QueuePop(Queue* q)
void QueuePop(Queue* q)
{
	assert(q);
	assert(q->_front);

	if (q->_front == q->_rear)
	{
		free(q->_front);
		q->_front = q->_rear = NULL;
		return;
	}

	QNode* pcur = q->_front->_next;

	free(q->_front);
	q->_front = pcur;
}

先断言确保程序的正常运行,若q->_front为空,则不存在出队列这一说法。
利用if语句判断队列是否只有一个结点这一特殊情况,若为特殊情况,则先用free释放掉唯一的结点,再将q->_frontq->_rear置为空即可
若为正常情况,则进行单链表的头删。

获取队列头部元素 QDataType QueueFront(Queue* q)
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->_front);
	return q->_front->_data;
}

先断言确保程序的正常运行,若q->_front为空,则不存在获取队列头部元素这一说法。
若通过,直接返回头结点的值即可。

获取队列队尾元素 QDataType QueueBack(Queue* q)
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->_rear);
	return q->_rear->_data;
}

先断言确保程序的正常运行,若q->_rear为空,则不存在获取队列尾部元素这一说法。
若通过,直接返回尾结点的值即可。

获取队列中有效元素个数 int QueueSize(Queue* q)
int QueueSize(Queue* q)
{
	assert(q);
	QNode* pcur = q->_front;
	int x = 0;
	while (pcur)
	{
		x++;
		pcur = pcur->_next;
	}
	return x;
}

先断言确保程序的正常运行。
定义一个指向队列头结点的指针pcur和一个记录队列中有效元素的整型变量x,再利用while循环进行链表的遍历,最后在返回x即可

判断队列是否为空 bool QueueEmpty(Queue* q)
bool QueueEmpty(Queue* q)
{
	assert(q);
	return q->_front == NULL;
}

先断言确保程序的正常运行。
若队列中的头结点为空,则返回1,反之返回0

销毁队列 void QueueDestroy(Queue* q)
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* pcur = q->_front;

	while (pcur)
	{
		QNode* del = pcur;
		pcur = pcur->_next;
		free(del);
	}

	q->_front = q->_rear = NULL;
}

先断言确保程序的正常运行。
再定义一个指向队列头结点的指针pcur,再利用while循环依次删除即可,最后将q->_frontq->_rear 置为空。

栈和队列的结合笔试题

力扣20. 有效的括号

题目如下:
在这里插入图片描述

解题思路

这道题目就很适合使用栈来解决,我们可以假定有一个" { [ ( ) ] } ( ) "这样的字符串 ,根据括号的顺序,我们可以先让指向字符串首元素的指针s遍历整个字符串,在遍历的同时把左括号'{' '[' '('放入栈中,当遇到右括号'}' ']' ')'这个时候就是利用栈判断的时候,根据逻辑在遇到右括号前,上一个遇到的一定是其相应的左括号,所以若栈顶不是右括号的相应括号或栈中为空,则一定不满足条件;若s遍历了整个字符串,且此时栈中为空时则说明整个字符串都满足条件。

在这里插入图片描述

具体步骤
  1. 先将我们上面写的栈的头文件与源文件的源代码复制过来
  2. 再新建一个栈并初始化,利用while循环遍历字符串,当字符s为左括号时将其压栈,若不为右括号,则进行判断,如果栈中为空,则说明不满足条件,若不为空,则定义一个字符变量top,将栈顶的值赋给top,再利用if循环判断括号是否同类对应,若不为对应,则销毁站并返回,代码如下:
    while(*s)
    {
        if(*s=='['||*s=='('||*s=='{')
        {
            StackPush(&st,*s);
        }
        else 
        {
			if(!StackEmpty(&st))
			{
				StackDestroy(&st);
            	return false;
			}
			char top = StackTop(&st);
			StackPop(&st);
            if((*s==']'&&top!='[')
            ||(*s==')'&&top!='(')
            ||(*s=='}'&&top!='{'))
            {
				StackDestroy(&st);
                return false;
            }
        }
        s++;
    }
  1. 如此循环,遍历每一个字符,遍历完全之后跳出循环,定义布尔变量ret若栈中为空,这说明整个字符串都满足条件返回1 ,反之返回 0
	bool ret=!StackEmpty(&st);
	StackDestroy(&st);
    return ret;

答案

代码如下:

力扣20.有效的括号

typedef char STDataType;
typedef struct Stack
{
	STDataType* _a;
	int _top;		
	int _capacity;  
}Stack;

void StackInit(Stack* ps);
void StackPush(Stack* ps, STDataType data);

void StackPop(Stack* ps);
STDataType StackTop(Stack* ps);

int StackSize(Stack* ps);
int StackEmpty(Stack* ps);

void StackDestroy(Stack* ps);


void StackInit(Stack* ps)
{
	assert(ps);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = -1;
}

void StackPush(Stack* ps, STDataType data)
{
	assert(ps);
	
	if (ps->_capacity == ps->_top + 1)
	{
		int newcapacity = !ps->_capacity ? 4 : 2 * ps->_capacity;
		STDataType* tmp = (STDataType*)realloc(ps->_a, sizeof(STDataType) * newcapacity);
		assert(tmp);
		ps->_a = tmp;
		tmp = NULL;
		ps->_capacity = newcapacity;
	}

	ps->_top++;
	ps->_a[ps->_top] = data;
}

void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->_top > -1);
	ps->_top--;
}

STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->_top > -1);
	return ps->_a[ps->_top];
}

int StackSize(Stack* ps)
{
	assert(ps);
	return ps->_top + 1;
}

int StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->_top > -1;
}

void StackDestroy(Stack* ps)
{
	assert(ps);
	free(ps->_a);
	ps->_a = NULL;
	ps->_capacity = 0;
	ps->_top = -1;
}

bool isValid(char* s) 
{
    Stack st;
    StackInit(&st);
    while(*s)
    {
        if(*s=='['||*s=='('||*s=='{')
        {
            StackPush(&st,*s);
        }
        else 
        {
			if(!StackEmpty(&st))
			{
				StackDestroy(&st);
            	return false;
			}
			char top = StackTop(&st);
			StackPop(&st);
            if((*s==']'&&top!='[')
            ||(*s==')'&&top!='(')
            ||(*s=='}'&&top!='{'))
            {
				StackDestroy(&st);
                return false;
            }
        }
        s++;
    }
	bool ret=!StackEmpty(&st);
	StackDestroy(&st);
    return ret;
}

好了,以上就为本期的全部内容喜欢请多多关注吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/152356.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTML5+CSS3小实例:悬停放大图片的旅游画廊

实例:悬停放大图片的旅游画廊 技术栈:HTML+CSS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content=&…

2、24 个常见的 Docker 疑难杂症处理技巧(二)

7Docker 容器中文异常 容器存在问题话&#xff0c;记得优先在官网查询 [问题起因] 今天登陆之前部署的 MySQL 数据库查询&#xff0c;发现使用 SQL 语句无法查询中文字段&#xff0c;即使直接输入中文都没有办法显示。 # 查看容器支持的字符集 rootb18f56aa1e15:# locale -a …

局部指令和全局指令的注册和使用

全局指令 先写一个js文件 import store from /store const directivePlugin {install(Vue) {Vue.directive(checkBtn, {inserted(el, binding) {// el: 指令绑定的那个元素对象 dom// binding.value: 指令等于号后面绑定的表达式的值 v-if"xxx"// 拿到el 拿到v…

gbase8a 认证培训课后题(一)

gbase8a 第一阶段练习题 第一次练习&#xff08;4选4&#xff09;1234 第二次练习&#xff08;5选5&#xff09;12345 第三次练习&#xff08;5选3&#xff09;12345 第四次练习&#xff08;6选4&#xff09;123456 第五次练习&#xff08;7选4&#xff09;1234567 第六次练习&…

解决requests库中UnicodeError异常的问题

摘要&#xff1a;本文介绍了使用requests库时可能遇到的UnicodeError异常&#xff0c;并提供了两种解决方法&#xff0c;以确保你的代码能够正常处理URL。 问题背景 在使用requests库时&#xff0c;当尝试获取类似’http://.example.com’这样的URL时&#xff0c;可能会遇到Un…

使用requests库下载文件的技术解析

目录 一、引言 二、使用requests库下载文件的基本流程 三、请求设置和响应处理 1、请求头部设置 2、跟随重定向 3、处理HTTP认证 4、响应状态码检查 5、响应头处理 6、响应体处理 四、异常处理 1、网络连接问题 2、HTTP请求错误 3、文件写入错误 总结 一、引言 …

【PHP】医院麻醉临床信息系统源码

麻醉临床信息系统以服务围术期临床业务工作的开展为核心&#xff0c;为医护人员、业务管理人员、院级领导提供流程化、信息化、自动化、智能化的临床业务综合管理平台。 麻醉信息系统处理的数据包含病人的手术信息、麻醉信息、病人手术过程中从监护仪上采集到的数据和病人情况等…

【第2章 Node.js基础】2.7 Node.js 的流

2.7 Node.js 的流 什么是流 流不是 Node.js 特有的概念。它们是几十年前在 Unix 操作系统中引入的。 我们可以把流看作这些数据的集合&#xff0c;就像液体一样&#xff0c;我们先把这些液体保存在一个容器里&#xff08;流的内部缓冲区 BufferList&#xff09;&#xff0c;…

[云原生2.] Kurbernetes资源管理 ---- (陈述式资源管理方式)

文章目录 1. K8s管理资源的方法类别1.1 陈述式资源管理方式1.2 声明式资源管理方式1.3 GUI式资源管理方法 2. 陈述式资源管理方式2.1 命令行工具 ---- Kubelet2.1.1 简介2.1.2 特性2.1.3 kubelet拓展命令2.1.4 kubectl基本语法2.1.5 Kubectl工具的自动补全 2.2 k8s Service 的类…

飞天使-django之数据库简介

文章目录 增删改查解决数据库不能存储中文问题创建表数据类型表的基本操作主键唯一键 unique外键实战 增删改查 四个常用的语句查询 : insert delete update select insert into student(Sno,name) values(95001,"张三") delete from student where name张三 upda…

关于git 解决分支冲突问题(具体操作,包含截图,教你一步一步解决冲突问题)

当在Git中有多个开发者在同一个分支上工作时&#xff0c;可能会发生分支冲突。分支冲突指的是多个开发者在同一时间修改相同的代码文件&#xff0c;导致Git无法自动合并这些更改。 比如说&#xff1a;我在github上进行了md文件的修改&#xff0c;我在本地仓库里面也进行md文件…

Python---数据序列中的公共方法

公共方法就是 支持大部分 数据 序列。 常见公共方法---简单 运算符描述支持的容器类型合并字符串、列表、元组*复制字符串、列表、元组in元素是否存在字符串、列表、元组、字典not in元素是否不存在字符串、列表、元组、字典 案例&#xff1a; 合并 代码&#xff1a; # …

AD教程 (十六)常用PCB封装的直接调用

AD教程 &#xff08;十六&#xff09;常用PCB封装的直接调用 打开已经做好的PCB文件 点击设计&#xff0c;生成PCB库&#xff0c;会自动把PCB里所用到的所有封装&#xff0c;全部自动生成 CtrlA 将所有元器件的封装全部选中&#xff08;或者只选中所需要的&#xff09;&#x…

成功解决 IDEA 2020 版本 代码报错不提示的几种方案

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 前言 今天中午写代码的时候&#xff0c;ID…

YOLOV5部署Android Studio安卓平台NCNN

坑非常多&#xff0c;兄弟们&#xff0c;我已经踩了三天的坑了&#xff0c;我这里部署了官方的yolov5s和我自己训练的yolov5n的模型 下载Android Studio&#xff0c;配置安卓开发环境&#xff0c;这个过程比较漫长。 安装cmake&#xff0c;注意安装的是cmake3.10版本。 根据手机…

著名的《NP问题》是个啥概念?

一、说明 关于复杂问题&#xff0c;始终是计算机科学挡在路前的一块巨石。所谓一个问题有解&#xff0c;但需要秒完成&#xff0c;这相当于说&#xff0c;此类问题无解。还有一类问题是说不清楚到底有没有一个具体解法&#xff0c;该解法能在多项式时间复杂函数上完成&#xff…

智慧城市怎么实时监测内涝积水的发生及解决办法?

随着城市化进程步伐不断加快&#xff0c;城市内涝问题越来越受到人们的关注。内涝不仅不便于人们的生活&#xff0c;还可能危害城市之中的基础设施比如路面等。因此实时监测内涝积水的发生并采取有效的解决办法是市政府的紧急任务&#xff0c;同时解决城市内涝也利于城市生命线…

通过阿里云宕机这件事,来看国内程序员的畸形职场文化

1、阿里云宕机始末 阿里在变更这块有三板斧&#xff0c;可监控、可灰度&#xff0c;可回滚。另外他们内部非常喜欢用这类简短的语句传递意图。 听起来非常简单&#xff0c;就目前大多数互联网公司基础设施都会支持这3项&#xff0c;只是支持的程度不太一样&#xff0c;普通的监…

蓝桥杯 vector

vector的定义和特性 注意&#xff1a;vector需要开C11标准 vector的常用函数 push_back():将元素添加到vector末尾 pop_back():删除vector末尾的元素 begin()和end():返回指向vector第一个元素和最后一个元素之后一个位置的迭代器。 示例 vector<int> vec{10,20,30};f…

准「AI 时代」下,如何衡量程序员的工作效率和生产力?

近 20 家科技、金融和制药公司实施了新的研发效能管理方法&#xff0c;并取得了令人鼓舞的初步结果。 客户报告的产品缺陷减少 20%-30%&#xff1b;员工体验分数提高 20%&#xff1b;客户满意度评分提高 60 个百分点。 大模型和 AIGC 技术催生了软件研发的新范式&#xff0c;也…