第三章:堆栈和队列
Q&A:
1、阐述栈和队列的特点,并说明它们的作用
- 栈是一种遵循先入后出原则的数据结构
- 队列是一种遵循先入先出原则的数据结构
2、是分别写出判队列为空和队列为满的条件
- 判断队列为空的条件:front==rear
- 判断队列为满的条件:(rear+1) %Max==front
3、什么是递归、递归算法?比较递归和非递归算法的优缺点
递归(recursion)是数学概念,也是一种有用的程序设计方法,通过函数调用自身来解决问题。它主要包含两个阶段。
- 递:程序不断深入地调用自身,通常传入更小或更简化的参数,直到达到“终止条件”。
- 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。
而从实现的角度看,递归算法代码主要包含三个要素。
- 终止条件:用于决定什么时候由“递”转“归”。
- 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。
- 返回结果:对应“归”,将当前递归层级的结果返回至上一层。
递归调用优缺点:
- 优点:程序非常简洁清晰,且易于分析
- 缺点:费时间(频繁的进栈出栈,甚至重复计算)、费空间(系统实现递归需要一个系统栈,当一个函数被调用时,系统创建一个工作记录📝,称为栈帧,并将其置于栈顶)
4、利用栈可以检查表达式中括号是否配对,是编写算法实现之
bool check(char str[])
{
stack s;
int i =0; //遍历表达式
while(str[i]!='\0')
{
if(str[i] == '(' || str[i] == '[' || str[i] == '{')
s.push(str[i]); //若是任意左括号,push
i++;
}
if(IsEmpty(s))
return true; //表达式中无括号
i=0;
while(str[i]!='\0')
{
switch(str[i])
{
case ')':
if(s.top()!='(')
return false;
else
{
s.pop();
break;
}
case ']':
if(s.top()!='[')
return false;
else
{
s.pop();
break;
}
case '}':
if(s.top()!='{')
return false;
else
{
s.pop();
break;
}
}
i++;
}
if(!IsEmpty(s))
return false;
else
return true; //检查栈中是否还存在单身括号,若存在表达式括号不匹配
}
优化后 ——
#include <stack>
bool check(char str[])
{
std::stack<char> s;
int i = 0;
// 遍历字符串,将左括号压入栈中
while (str[i] != '\0')
{
if (str[i] == '(' || str[i] == '[' || str[i] == '{')
{
s.push(str[i]);
}
else if (str[i] == ')' || str[i] == ']' || str[i] == '}')
{
if (s.empty())
return false;
char topChar = s.top();
s.pop();
// 匹配对应的括号
if ((str[i] == ')' && topChar != '(') ||
(str[i] == ']' && topChar != '[') ||
(str[i] == '}' && topChar != '{'))
{
return false;
}
}
i++;
}
// 如果栈不为空,说明还有未匹配的左括号
return s.empty();
}
5、循环队列元素放在q[1...n]中,实现队列的各种操作
#include <iostream>
#define MAX 100
class CircularQueue {
int q[MAX];
int front, rear, size;
public:
CircularQueue(int n) : front(0), rear(0), size(n) {}
bool isFull() {
return (rear + 1) % size == front;
}
bool isEmpty() {
return front == rear;
}
void enqueue(int x) {
if (isFull()) {
std::cout << "Queue is full\n";
return;
}
q[rear] = x;
rear = (rear + 1) % size;
}
int dequeue() {
if (isEmpty()) {
std::cout << "Queue is empty\n";
return -1;
}
int x = q[front];
front = (front + 1) % size;
return x;
}
int getFront() {
if (isEmpty()) {
std::cout << "Queue is empty\n";
return -1;
}
return q[front];
}
};
6、利用队列将栈中元素逆置的算法
void reverseStack(std::stack<int>& s) {
std::queue<int> q;
// 将栈中的元素全部弹出并放入队列中
while (!s.empty()) {
q.push(s.top());
s.pop();
}
// 将队列中的元素再依次放回栈中
while (!q.empty()) {
s.push(q.front());
q.pop();
}
}
7、递归算法实现数组功能
- 求数组中的最大整数
- 求数组中n个数的平均值
求数组中的最大整数:
int findMax(int A[], int n) {
if (n == 1)
return A[0];
return std::max(A[n-1], findMax(A, n-1));
}
求数组中n个数的平均值:
double findAverage(int A[], int n) {
if (n == 1)
return A[0];
return ((A[n-1] + (n-1) * findAverage(A, n-1)) / n);
}
8、设计递归算法,在一个线性表中搜索指定关键字
//n为数组长度
int recursiveSearch(int A[], int n, int key) {
if (n == 0)
return -1; // 搜索失败,返回-1
if (A[n-1] == key)
return n-1; // 返回关键字的位置
return recursiveSearch(A, n-1, key);
}
相关考法 🌈:
-
栈的应用
-
1、括号匹配
-
-
-
强化改成switch
-
-
-
-
2、算术表达式
-
-
性质,中缀转前、后缀都有多种形式,为确保算法的确定性,需要规定,后缀“左优先”原则 前缀“右优先”原则
-
后缀
-
前缀
-
概要
-
-
-
-
3、递归
-
强化学会写递归算法,训练
-
了解怎么消除斐波那契的重复计算
-
-
P98,t5、6
-
-
-
栈
-
顺序栈考题
-
强化实现共享栈
-
-
链栈
-
强化实现操作
-
-
栈选择考题
-
判断合法性,耐性推就行 进阶:P70,t29
-
知道一个卡特兰数,用于求个数
-
-
-
队列
-
队列考题
-
判断合法性(往往双端队列考题多,耐心分析)
-
-
知道队头队尾,确定队列元素个数 仔细审题,注意初始条件未必符合常理
p84、t6
-
1、如图,rear=n-1,p86,t21
-
2、链队中不带头结点的操作
-
和单链表一块总结
-
-
强化
-
1、实现顺序队判队满的3个方式
1、Size
2、tag
3、rear+1
-
-
-