【数据结构】栈和队列总结——基本知识要点汇总

halo~我是bay_Tong桐小白
本文内容是桐小白个人对所学知识进行的总结和分享,知识点会不定期进行编辑更新和完善,了解最近更新内容可参看更新日志,欢迎各位大神留言、指点

【更新日志】

最近更新:

  • 更新内容——计算机统考408考纲考察目标与变动情况(2020.9.16)
  • 新增内容——计算机统考408考纲要求(2020.9.13)
  • 更新内容——增加栈解决表达式问题图示详解(2020.7.12)
  • 新增内容——双端队列(2020.7.12)
  • 持续更新中……
计算机统考408考纲要求

2021计算机统考408考纲数据结构学科考察目标

  • 掌握数据结构的基本概念、基本原理和基本方法
  • 掌握数据结构的逻辑结构、存储结构及基本操作的实现,能够对算法进行基本的时间复杂度与空间复杂度的分析
  • 能够运用数据结构的基本原理和方法进行问题的分析与求解,具备采用C或C++语言设计与实现算法的能力

2021计算机统考408考纲变动情况
在这里插入图片描述
2021计算机统考408考纲对栈、队列和数组部分考察要求
在这里插入图片描述

考纲中将栈、队列和数组归为一类进行考察,数组的详细总结可见本栏文章《数组和广义表总结——基本知识要点汇总》

栈的工作原理

栈的相关概念

  • 与线性表的联系:栈是具有一定操作约束的线性表,只在一端(栈顶)做插入删除操作(称为入栈与出栈)
  • 特点:只在一端进行操作,先进后出【FILO】,后进先出【LIFO】

栈的存储结构

【不同教材对于栈顶位置的定义方式不同,但其核心思想是一致的,对于同一问题的研究需要统一该定义方式,否则容易产生歧义。常见的定义方式分为两种,一种是栈顶元素的下一个位置为栈顶位置,另一种为栈顶元素的位置就是栈顶位置】
栈顶元素的下一个位置为栈顶位置的存储描述
栈顶元素的位置就是栈顶位置的存储描述
【栈顶位置与栈底位置并不是绝对概念,例如假设一维数组A[1,2,3,…,n]来表示一个栈,则可以规定A[n]作为栈底,也可以规定A[1]作为栈底。在栈中对栈进行入栈出栈操作的一端为栈顶,与其对应的另一端是栈底。栈底一旦进行规定是固定不变的,栈顶则会根据栈中元素情况进行变化】

栈顶位置与栈底位置描述

顺序栈的基本操作集(C语言实现)

【为了方便调用,采用多文件编程方式,代码已基本完成,实际使用时依据题目实际情况进行直接调用或简单修改后调用,算法的健壮性有待进一步测试,后续会持续对代码进行完善和优化】
【此处对于栈顶位置的定义采用栈顶元素的位置就是栈顶位置的定义方式】

Stack.h文件

#pragma once
#ifndef STACK
#define STACK

#define MaxSize_S 100
typedef void* SElementType;

/*顺序栈基本操作集*/
typedef struct SNode* Stack;
struct SNode {
    
    
	SElementType data[MaxSize_S];
	int top;
};

Stack CreateStack();//生成空堆栈
int IsFullS(Stack S);//判断堆栈S是否已满
int IsEmptyS(Stack S);//判断堆栈S是否为空
void Push(Stack S, SElementType item);//将元素item压入堆栈(入栈)
SElementType Pop(Stack S);//删除并返回栈顶元素(出栈)
SElementType GetTop(Stack S);//获取栈顶元素

#endif // !STACK

Stack.c文件

#include<stdio.h>
#include<stdlib.h>
#include"Stack.h"
#define ERROR -1

//生成空堆栈
Stack CreateStack() {
    
    
	Stack S = (Stack)calloc(1, sizeof(struct SNode));
	if (!S) {
    
     perror("error\n"); exit(1); }
	S->top = -1;
	return S;
}

//判断堆栈S是否已满,返回1或0
int IsFullS(Stack S) {
    
    
	if (S->top == MaxSize_S - 1) {
    
     return 1; }
	else {
    
     return 0; }
}

//判断堆栈S是否为空,返回1或0
int IsEmptyS(Stack S) {
    
    
	if (S->top < 0) {
    
     return 1; }
	else {
    
     return 0; }
}

//将元素item压入堆栈(入栈)
void Push(Stack S, SElementType item) {
    
    
	if (IsFullS(S)) {
    
     printf("堆栈满\n"); return; }
	else {
    
     S->data[++(S->top)] = item; }
}

//删除并返回栈顶元素(出栈)
SElementType Pop(Stack S) {
    
    
	if (IsEmptyS(S)) {
    
     printf("堆栈空\n"); return ERROR; }
	else {
    
     return (S->data[(S->top)--]); }
}

//获取栈顶元素
SElementType GetTop(Stack S) {
    
    
	if (IsEmptyS(S)) {
    
     printf("堆栈空\n"); return ERROR; }
	else {
    
     return (S->data[S->top]); }
}

链栈的基本操作集(C语言实现)

Stack_chain.h文件

#pragma once
#ifndef STACK_CHAIN
#define STACK_CHAIN

typedef void* SElementType;
/*链栈基本操作集*/
typedef struct SNode_chain* Stack_chain;
struct SNode_chain {
    
    
	SElementType data;
	struct SNode* next;
};

Stack_chain CreateStack_chain();//生成空堆栈
int IsEmptyS_chain(Stack_chain S);//判断堆栈S是否为空
void Push_chain(Stack_chain S, SElementType item);//将元素item压入堆栈(入栈)
SElementType Pop_chain(Stack_chain S);//删除并返回栈顶元素(出栈)
SElementType GetTop_chain(Stack_chain S);//获取栈顶元素

#endif // !STACK_CHAIN

Stack_chain.c文件

#include<stdio.h>
#include<stdlib.h>
#include"Stack_chain.h"
#define ERROR -1

//生成空堆栈
Stack_chain CreateStack_chain() {
    
    
	Stack_chain S = (Stack_chain)calloc(1, sizeof(struct SNode_chain));
	if (!S) {
    
     perror("error\n"); exit(1); }
	S->next = NULL;
	return S;
}

//判断堆栈S是否为空,返回1或0
int IsEmptyS_chain(Stack_chain S) {
    
    
	return	(S->next == NULL);
}

//将元素item压入堆栈(入栈)
void Push_chain(Stack_chain S, SElementType item) {
    
    
	Stack_chain node = (Stack_chain)calloc(1, sizeof(struct SNode_chain));
	if (!node) {
    
     perror("error\n"); exit(1); }
	node->data = item;
	node->next = S->next;
	S->next = node;
}

/*删除并返回栈顶元素(出栈)*/
SElementType Pop_chain(Stack_chain S) {
    
    
	Stack_chain node;
	SElementType TopData;
	if (IsEmptyS_chain(S)) {
    
     printf("堆栈空\n"); return ERROR; }
	else {
    
    
		node = S->next;
		S->next = node->next;
		TopData = node->data;
		free(node);
		return TopData;
	}
}

/*获取栈顶元素*/
SElementType GetTop_chain(Stack_chain S) {
    
    
	if (S == NULL)
		return ERROR;
	else
		return S->data;
}

栈解决表达式问题

已知中缀表达式进行表达式求值的大致思路步骤:

  • 利用栈将中缀表达式转化为后缀表达式
  • 利用栈对后缀表达式进行计算求值

利用栈实现中缀表达式转后缀表达式

思路:运算符号进行出入栈操作,运算数直接输出
举例描述
具体步骤

  • 从头至尾依次读取中缀表达式的每个对象
  • 读取到运算数则直接进行输出
  • 读取到左括号则压入堆栈
  • 读取到右括号则将栈顶的运算符依次弹出并输出,直到遇到左括号(左右括号均出栈不输出)
  • 读取到运算符,若其优先级大于栈顶运算符时则把它压入堆栈;若其优先级小于等于栈顶运算符时则将栈顶运算符弹出并输出,再与新的栈顶运算符进行优先级比较,直到该运算符优先级大于栈顶运算符优先级为止
  • 若中缀表达式的每个对象均读取完成,堆栈中仍有存留运算符,则将堆栈中存留的运算发一并输出

中缀表达式转后缀表达式描述
(表达式相关问题还可使用二叉树进行解决,详细见本栏文章《树和二叉树总结——基本知识要点汇总》二叉树遍历部分)

利用栈实现后缀表达式求值

思路:运算数进行出入栈操作,运算符号与弹出的运算数进行运算,得出的结果继续进行出入栈操作
后缀表达式计算描述
具体步骤

  • 从头至尾依次读取中缀表达式的每个对象
  • 读取到运算数则压入堆栈
  • 读取到运算符则从堆栈中弹出适当数量的运算数与运算符号进行运算,计算结果继续入栈
  • 最后堆栈顶上的元素就是表达式的结果值

后缀表达式求值举例描述

递归函数工作栈

【详细递归过程及相关概念后续进行完善……】

  • 递归:在定义自身的同时又出现了对自身的调用
    递归函数工作栈描述
  • 程序调用函数时,主调函数的返回地址必须压入函数调用栈
  • 一系列的函数调用,其对应的一组返回地址将按照后进先出的顺序被压入函数调用栈,这样使得每个函数能够正确返回到它的主调函数
  • 函数每次被调用时产生的局部变量保存在函数调用栈中,称为函数调用的活动记录
  • 发生一次函数调用,对应的活动记录入栈;当函数返回时,对应的活动记录出栈(即当前正在运行函数的活动记录必在栈顶)

队列的工作原理

队列的相关概念

  • 与线性表的联系:队列是具有一定操作约束的线性表,只在一端(队尾)插入在另一端(队头)删除(称为入队与出队)
  • 特点:只在一端进行插入另一端进行删除,先进先出【FIFO】,后进后出【LILO】

队列的存储结构

【不同教材对于队头队尾位置的定义方式不同,但其核心思想是一致的,对于同一问题的研究需要统一该定义方式,否则容易产生歧义。常见的定义方式分为两种,一种是从队头标记的下一个位置到队尾标记之间的序列为实际使用队列,另一种为从队头标记到队尾标记的前一个位置之间的序列为实际使用队列】

  • 队列的一般形式

从队头标记的下一个位置到队尾标记之间的序列为实际使用队列存储描述
从队头标记到队尾标记的下一个位置之间的序列为实际使用队列存储描述

  • 循环队列:重复利用数组单元提高空间利用率(链式也可构造循环队列,一般常见于顺序存储)
    循环队列存储描述
  • 链队列的存储结构:利用front指针与rear指针进行队列的构建
    链队列存储结构描述
    链队列入队操作描述链队列出队操作描述

循环队列顺序存储的基本操作集(C语言实现)

【此处对于队列的处理采用从队头标记的下一个位置到队尾标记之间的序列为实际使用队列】

Queue.h文件

#pragma once
#ifndef QUEUE
#define QUEUE

/*顺序存储循环队列基本操作集*/
typedef int Position;
typedef void* QElementType;
typedef struct QNode* Queue;
struct QNode {
    
    
	QElementType* data;
	Position rear, front;
	int MaxSize;
};

Queue CreateQueue(int MaxSize);//生成长度为MaxSize的空队列
int IsFullQ(Queue Q);//判断队列Q是否已满
int IsEmptyQ(Queue Q);//判断队列Q是否为空
void AddQ(Queue Q, QElementType item);//将数据元素item插入队列Q中(入队)
QElementType DeleteQ(Queue Q);//将对头数据元素从队列中删除并返回(出队)

#endif // !QUEUE

Queue.c文件

#include<stdio.h>
#include<stdlib.h>
#include"Queue.h"

//生成长度为MaxSize的空队列
Queue CreateQueue(int MaxSize) {
    
    
	Queue Q = (Queue)calloc(1, sizeof(struct QNode));
	Q->data = (QElementType*)calloc(MaxSize, sizeof(QElementType));
	if (!Q || !Q->data) {
    
     perror("error\n"); exit(1); }
	Q->MaxSize = ++MaxSize;//为区分空队列和满队列,需要多开辟一个空间
	return Q;
}

//判断队列Q是否为空
int IsEmptyQ(Queue Q) {
    
    
	if (Q->front == Q->rear) {
    
    	return 1;	}
	else {
    
     return 0; }
}

//判断队列Q是否已满,返回1或0
int IsFullQ(Queue Q) {
    
    
	if ((Q->rear + 1) % Q->MaxSize == Q->MaxSize) {
    
    	return 1;	}
	else {
    
     return 0; }
}

//将数据元素item插入队列Q中(入队)
void AddQ(Queue Q, QElementType item) {
    
    
	if (IsFullQ(Q)) {
    
     printf("队列满\n"); return; }
	Q->rear = (Q->rear + 1) % Q->MaxSize;
	Q->data[Q->rear] = item;
}

//将对头数据元素从队列中删除并返回(出队)
QElementType DeleteQ(Queue Q) {
    
    
	if (IsEmptyQ(Q)) {
    
     printf("队列空\n"); return -1; }
	else {
    
    
		Q->front = (Q->front + 1) % Q->MaxSize;
		return Q->data[Q->front];
	}
}

链队列的基本操作集(C语言实现)

Queue_chain.h文件

#pragma once
#ifndef QUEUE_CHAIN
#define QUEUE_CHAIN

typedef void* QElementType;
/*链队列基本操作集*/
typedef struct Node* NodeQ;
typedef struct QNode_chain* Queue_chain;
struct Node {
    
    
	QElementType data;
	struct Node* next;
};
struct QNode_chain {
    
    
	struct Node* front;
	struct Node* rear;
	int Length;
};
NodeQ CreateNodeQ();//生成链表结点
Queue_chain CreateQueue_chain();//生成空队列
int IsEmptyQ_chain(Queue_chain Q);//判断队列Q是否为空
void AddQ_chain(Queue_chain Q, QElementType item);//将数据元素item插入队列Q中(入队)
QElementType DeleteQ_chain(Queue_chain Q);//将对头数据元素从队列中删除并返回(出队)

#endif // !QUEUE_CHAIN

Queue_chain.c文件

#include<stdio.h>
#include<stdlib.h>
#include"Queue_chain.h"
#define ERROR -1

//创建结点
NodeQ CreateNodeQ() {
    
    
	NodeQ node = (NodeQ)calloc(1, sizeof(struct Node));
	if (!node) {
    
     perror("error\n"); exit(1); }
	node->next = NULL;
	return node;
}

//生成长度为MaxSize的空队列
Queue_chain CreateQueue_chain() {
    
    
	Queue_chain Q = (Queue_chain)calloc(1, sizeof(struct QNode_chain));
	if (!Q) {
    
     perror("error\n"); exit(1); }
	Q->front = Q->rear = NULL;
	return Q;
}

//判断队列Q是否为空
int IsEmptyQ_chain(Queue_chain Q) {
    
    
	if (Q->front == NULL) {
    
     return 1; }
	else {
    
     return 0; }
}

//将数据元素item插入队列Q中(入队)
void AddQ_chain(Queue_chain Q, QElementType item) {
    
    
	NodeQ node = CreateNodeQ();
	node->data = item;
	Q->rear->next = node;
	Q->rear = node;
	Q->Length++;
}

//将队头数据元素从队列中删除并返回(出队)
QElementType DeleteQ_chain(Queue_chain Q) {
    
    
	if (IsEmptyQ_chain(Q)) {
    
     printf("队列空\n"); return ERROR; }
	NodeQ P = Q->front;
	QElementType data = P->data;
	if (Q->front == Q->rear) {
    
    
		Q->front = Q->rear = NULL;
	}
	else{
    
     Q->front = Q->front->next; }
	Q->Length--;
	free(P);
	return data;
}

双端队列

  • 双端队列是限定插入和删除操作在表的两端进行的线性表

双端队列描述

  • 在实际使用中还可以有输出受限的双端队列(即一个端点允许插入和删除,另一个端点只允许插入的双端队列)和输入受限的双端队列(即一个端点允许插入和删除,另一个端点只允许删除的双端队列)

  • 如果限定双端队列从某个端点插入的元素只能从该端点删除,则该双端队列就成为两个栈底相邻接的栈了

在这里插入图片描述

  • 尽管双端队列看起来似乎比栈和队列更灵活,但实际应用中作用不及栈和队列

持续更新中……
我是桐小白,一个摸爬滚打的计算机小白

猜你喜欢

转载自blog.csdn.net/bay_Tong/article/details/106999940