数据结构 - 第三章 栈
文章目录
一、铁路进行列车调度时常把站台设计成栈式结构的站台,试问:
(1)出栈序列
需求:设有编号为 1,2,3,4,5, 6 的 6 辆列车,顺序开入栈式 结构的站台,则可能的出栈序列有多少种?
(2)判断是否可以进栈
需求:若进站的 6 辆列车顺序如上所述,那么是否能够得到 435612,325641,154623 和 135426 的出站序列,如果不能,说明为什么不能;如果能,说明如何得到(即写出“进栈”或“出栈”的 序列)
注意这里的进栈顺组必须是按照1 2 3 4 5 的顺序
325641:(可行)
执行顺序 | 栈内元素 | 出栈序列 |
---|---|---|
1,2,3先进栈 | 1 2 3 | - |
3出栈 | 1 2 | 3 |
2出栈 | 1 | 3 2 |
4,5进栈 | 1 4 5 | 3 2 |
5出栈 | 1 4 | 3 2 5 |
6进栈 | 1 4 6 | 3 2 5 |
全部出栈 | - | 3 2 5 6 4 1 |
135426:(可行)
执行顺序 | 栈内元素 | 出栈序列 |
---|---|---|
1进栈 | 1 | - |
1出栈 | - | 1 |
2 3进栈 | 2 3 | 1 |
3出栈 | 2 | 1 3 |
4 5进栈 | 2 4 5 | 1 3 |
5出栈 | 2 4 | 1 3 5 |
4出栈 | 2 | 1 3 5 4 |
6进栈 | 2 6 | 1 3 5 4 |
全部出栈 | - | 1 3 5 4 6 2 |
435612:这样的出栈序列,说明4356出完栈之后才是1和2,那么1和2就一直在栈中,而且1要在2之前出栈,但是根据题目顺序进栈,1在2的前面进栈,那么就只能2先出栈
154623:2 3和上面的情况类似,只能是3 2的出栈顺序
二、写出下列中缀表达式的后缀表达式
中缀表达式 | 后缀表达式 |
---|---|
ABC | ABC * * 或者 ABC |
-A+B-C+D | A-B+C-D+ |
A*-B+C | A*B-C+ |
(A+B)* D+E/(F+A*D)+C | AB+D * EFAD*+/+C+ |
A&&B || !(E>F) {注:按照C++的优先级} | AB && EF>! || |
!(A && ! ((B < C) || (C > D)) ) || (C < E) | ABC< CD> || ! &&! CE< || |
三、借助栈实现单链表上的逆置运算
template <class T>
struct LinkNode {
T data;
LinkNode<T>* next;
LinkNode(LinkNode<T>* ptr = NULL) :next(ptr) {
}
//这里不能有分号
//还需要加入大括号
LinkNode(const T& item, LinkNode<T>* ptr = NULL) :data(item), next(ptr) {
}
};
template<class T>
bool LinkStack<T>::reverseList(LinkList<T>& list)
{
//先使用栈获取链表的每个元素
LinkStack<T> s1;
LinkNode<T>* p = NULL;
if (!list.first->next) {
cerr << "链表不可逆转" << endl;
exit(1);
}
p = list.first->next;
while (p) {
s1.push(p->data);
LinkNode<T>* q = p;//暂时存储需要删除的结点
p = p->next;
list.first->next = p;
delete q;
}
/*list.first->next = s1.top;*/
//s1会被释放,局部变量,所以上面只是将头结点链接之后还是会释放
LinkNode<T>* s = list.first;
while (s) {
s->next = new LinkNode<T>(s1.top->data);
s1.pop();
s = s->next;
if (!s1.top)break;
//注意,这里还不能判断s,因为s无论何时,next都为空值,如果在while判断的时候加
//s->next,那么不会进入,如果在末尾加s->next ,那么循环执行一次就结束
}
return true;
}
//测试逆转函数
/*
{
LinkList<int> list;
for (int i = 0; i < 10; i++) {
LinkNode<int>* node = new LinkNode<int>(rand() % 13);//使用后插法构建一个链表
list.ListInsert_back(node);
}
cout << "链表逆转前:";
list.ListPrint();
LinkStack<int>::reverseList(list);
cout << "链表逆转后:";
list.ListPrint();
/*
输出结果:
链表逆转前:2 7 3 6 7 7 12 4 0 11
链表逆转后:11 0 4 12 7 7 6 3 7 2
}
*/
四、双栈的实现
需求:将编号为0 和1 的两个栈存放于一个数组空间 V[m]中,栈底分别处于数组的 两端。当第 0 号栈的栈顶指针 top[0]等于-1 时该栈为空,当第 1 号栈的栈顶指针 top[1] 等于 m 时该栈为空。两个栈均从两端向中间增长。当向第 0 号栈插入一个新元素时,使 top[0]增 1 得到新的栈顶位置,当向第 1 号栈插人一个新元素时,使 top[1]减 1 得到新的栈 顶位置。当 top[0]+1 == top[1]时或 top[0]= top[1]-1 时,栈空间满,此时不能再向 任一栈加入新的元素。试定义这种双栈结构的类定义,并实现判栈空、判栈满、插人、删除 算法
//DoubleStack.h
template <class T>
class DoubleStack {
public:
DoubleStack(int size = 20);
~DoubleStack();
bool push(T x,int i);
bool pop(int i);
bool isEmpty(int i); //判断是那个栈是空的
bool isFull()const {
return top[0] + 1 == top[1]; }
void makeEmpty(int i);
private:
int top[2], bot[2];//两个栈顶指针,栈底指针
T* elem;
int maxSize;
};
//DoubleStack.hpp
#include <assert.h>
#include "DoubleStack.h"
template<class T>
DoubleStack<T>::DoubleStack(int size) {
elem = new T[size];
assert(elem != NULL);
maxSize = size;
top[0] = bot[0] = -1;
top[1] = bot[1] = size;
}
template<class T>
DoubleStack<T>::~DoubleStack()
{
delete[]elem;
}
template<class T>
inline bool DoubleStack<T>::push(T x, int i)
{
if (isFull())return false;
if (!i)elem[++top[0]] = x;
else elem[--top[1]] = x;
return true;
}
template<class T>
inline bool DoubleStack<T>::pop(int i)
{
if (isEmpty(i)) return false;
if (!i)elem[--top[0]];
else elem[--top[1]];
return true;
}
template<class T>
inline bool DoubleStack<T>::isEmpty(int i)
{
return top[i] = bot[i];
}
template<class T>
void DoubleStack<T>::makeEmpty(int i)
{
if (!i)top[0] = bot[0] = -1;
else top[1] = bot[1] = maxSize;
}
五、括号匹配
需求:试着编写一个算法,检查一个程序中的花括号,方括号,圆括号是否配对,如果能配对就返回真,否则返回假
说明:这里没有完全按照题目的需求
template<class T>
void LinkStack<T>::bracketMatch(const char* expression)
{
int size = 20;
int j = 0;
int length = strlen(expression);
LinkStack<int> s1,//小括号
s2, //中括号
s3; //大括号
for (int i = 0; i < length; i++) {
if (expression[i] == '(')s1.push(i + 1);
else if (expression[i] == ')') {
if (s1.pop(j))cout << i+1 << "与" << j << "匹配( )" << endl;
else cout << "没有与第" << i+1 << "个右小括号的左小括号" << endl;
}
if (expression[i] == '[')s2.push(i + 1);
else if (expression[i] == ']') {
if (s2.pop(j))cout << i+1 << "与" << j << "匹配[ ]" << endl;
else cout << "没有与第" << i+1 << "个右中括号的左中括号" << endl;
}
if (expression[i] == '{')s3.push(i + 1);
else if (expression[i] == '}') {
if (s3.pop(j))cout << i+1 << "与" << j << "匹配{ }" << endl;
else cout << "没有与第" << i+1 << "个右大括号的左大括号" << endl;
}
}
while (!s1.isEmpty()) {
s1.pop(j);
cout << "没有与第" << j << "个左小括号匹配的右小括号" << endl;
}
while (!s2.isEmpty()) {
s2.pop(j);
cout << "没有与第" << j << "个左中括号匹配的右中括号" << endl;
}
while (!s3.isEmpty()) {
s3.pop(j);
cout << "没有与第" << j << "个左大括号匹配的右大括号" << endl;
}
}
//测试括号匹配函数
/*
{
LinkStack<int>::bracketMatch("99()[[[[]{
{}");
/*
输出结果:
4与3匹配( )
9与8匹配[ ]
12与11匹配{ }
没有与第7个左中括号匹配的右中括号
没有与第6个左中括号匹配的右中括号
没有与第5个左中括号匹配的右中括号
没有与第10个左大括号匹配的右大括号
}
*/
六、递归算法
需求:已知f为单链表的表头指针,链表中储存的都是整形的数据,写出实现下面算法的递归算法
(1)求链表中的最大整数
template<class T>
T LinkList<T>::SearchMax_Recursion(LinkNode<T>* node)
{
if (!node) return 0;
if (!node->next) return node->data;
int max = SearchMax_Recursion(node->next);
/*
这里递归算法的实现大概是:
这里的函数会一直执行到最后一个节点,那么满足第二个if条件,返回的data才是max的初始值
,这时的max和上一个节点的数值进行比较,返回其中的最大值再次和上一个节点的数值进行比较
如此往复,最后返回最终的最大值
前面的两个if条件语句是防止进入死循环
*/
return (max > node->data) ? max : node->data;
}
//递归调用之获取最大值
/*
{
LinkList<int> list;
for (int i = 0; i < 10; i++) {
LinkNode<int>* node = new LinkNode<int>(rand() % 13);//使用后插法构建一个链表
list.ListInsert_back(node);
}
list.ListPrint();
cout << "链表的最大值是:" << LinkList<int>::SearchMax_Recursion(list.first) << endl;
/*
输出结果:
2 7 3 6 7 7 12 4 0 11
链表的最大值是:12
}
*/
(2)求链表的结点个数
template<class T>
inline T LinkList<T>::Number_Recursion(LinkNode<T>* node)
{
static int k = 0;
if (!node->next)return 1;//目的只是作为递归调用结束的条件
Number_Recursion(node->next);
k++;
return k;
}
//递归调用之获取节点数
/*
{
LinkList<int> list;
for (int i = 0; i < 10; i++) {
LinkNode<int>* node = new LinkNode<int>(rand() % 13);//使用后插法构建一个链表
list.ListInsert_back(node);
}
list.ListPrint();
cout << "该链表的节点个数是:" << LinkList<int>::Number_Recursion(list.first) << endl;
/*
输出结果:
2 7 3 6 7 7 12 4 0 11
该链表的节点个数是:10
}
*/
(3)求所有整数的平均值
template<class T>
inline float LinkList<T>::Average_Recursion(LinkNode<T>* node)
{
/*static int ave = 0;
static int n = 0;
if (!node->next) {
return node->data;
}
Average_Recursion(node->next);
ave = (ave + node->next->data);
return ave;
求总和
*/
//以1,2,3为例,计算次序为3;(3*1+2)/2=2.5;(2.5*2+1)/3=2;
static int n = 0;
if (!node->next) {
n = 0;
return 0;
}
float m = Average_Recursion(node->next);//m必须是float型
n++;
return (float)((n - 1) * m + node->next->data) / n;
}
//递归调用之获取节点数据的平均值
/*
{
LinkList<int> list(0);
for (int i = 0; i < 10; i++) {
LinkNode<int>* node = new LinkNode<int>(rand() % 13);//使用后插法构建一个链表
list.ListInsert_back(node);
}
list.ListPrint();
cout << "链表的平均值是:"<< LinkList<int>::Average_Recursion(list.first) << endl;
/*
2 7 3 6 7 7 12 4 0 11
链表的平均值是:5.9
}
*/