前面几篇写的都是数据结构原理性内容,也在写栈的时候提到了栈的应用场景很欠缺,所以这两天把栈的几个应用写一写,贴出来以后查阅方便。
栈的几个典型应用例子包括:1、汉诺塔问题;2、括号匹配问题;3、迷宫老鼠问题;4、电路防交叉的布线问题(接近括号匹配);5、就是这个博客贴出来的车厢重排问题(其实是一个排序问题,只不过要将排序过程展现出来)
这里面应该要贴出来车厢重排问题是个什么问题:大致意思就是进入车站的列车的车厢顺序是混乱的,需要通过几个缓冲轨道,将列车的车厢顺序重新排列
下面贴出代码:
下面是头文件的内容,写成类主要是为了几个栈变量调用起来方便,相当于全局变量:
#ifndef _H_STACK_RAILROAD_H
#define _H_STACK_RAILROAD_H
#include <iostream>
#include <stack>
class stackRailRoad
{
public:
stackRailRoad(std::stack<int>& st);
~stackRailRoad();
void deal(void);
void output(void);
protected:
private:
std::stack<int> m_stack;
std::stack<int> m_resStack;
std::stack<int> *m_stackCache;
};
#endif
下面是cpp文件的内容:
#include "stackRailRoad.h"
stackRailRoad::stackRailRoad(std::stack<int>& st)
{
m_stack = st;
m_stackCache = new std::stack<int>[m_stack.size() - 1];
}
stackRailRoad::~stackRailRoad()
{
delete[] m_stackCache;
}
void stackRailRoad::deal(void)
{
int num = m_stack.size();
结束循环的标志是结果栈的和输入栈的原始size一样大
while (num != m_resStack.size())
{
/// 判断输入栈是否为空,不为空则直接压入结果栈或者压入缓冲栈
if( !m_stack.empty() )
{
/// 判断输入栈的元素能否直接进入到结果栈
int topVal = m_stack.top();
其实这里只需要判断topval是否为1即可,如果为1,必然压栈
不可能有其他值。当然这仅限于列车的车厢重排问题
对于排序问题,可能需要修改条件为最小值或者其他条件
if (m_resStack.empty() && topVal == 1)
{
m_resStack.push(topVal);
std::cout<<"\tpush int m_resStack directly"<<std::endl;
m_stack.pop();
}
因为涉及到要将m_resStack的top值返回来,
所以需要判断是否为空,不然会出错
else if(!m_resStack.empty())
{
if (topVal - 1 == m_resStack.top())
{
m_resStack.push(topVal);
std::cout<<"\tpush into m_resStack directly"<<std::endl;
m_stack.pop();
}
}
/// 上面的任意一种压栈情况满足之后均需要到这个if( !m_stack.empty() )外面去
/// 因为如果不去外面的话,直接执行下面会导致相同的topval要去进缓冲栈,而如果
/// 不出去直接在下面再取一次topval又可能因为m_stack中没有元素而报错
}
if( !m_stack.empty() )
{
/// 判断输入栈的元素进入到哪个缓冲栈
/// 循环查询到底哪一个缓冲栈的元素满足进入结果栈的要求
/// 进入结果栈的要求和上面的要求其实是一样,正常应该封装成函数
/// 进入到哪一个缓冲栈的要求是满足:
/// 大于缓冲栈顶元素的最小值
int topVal = m_stack.top();
int idx = -1;/// 用于保存到底存储在哪一个缓冲栈,初始为-1是为了之后判断是否需要新的栈
bool isFirst = true;
int emptyIdx = 0;/// 这两个变量使用来记录在所有已有元素的缓冲栈不满足的情况下应该要存储到哪一个新的空缓冲栈中
int minVal = num;
for (int ii = 0; ii < num - 1; ii++)
{
if (m_stackCache[ii].empty())
{
if (isFirst)
{
isFirst = false;
emptyIdx = ii;/// 当第一次遇到一个空的缓冲栈的时候,记录其索引号,
方便之后其它含有元素的缓冲栈均不能接受元素时保存m_stack中的top值
}
}
else 缓冲栈不为空,需要判断栈顶元素和当前的topval是否匹配
{
/// 既然不为空,那就要比较一下到底哪个是最小的
if (topVal < m_stackCache[ii].top() && minVal > m_stackCache[ii].top() - topVal)
{
minVal = m_stackCache[ii].top() - topVal;/// 记录下最小值
idx = ii;/// 其实这里可以增加判断minVal是否等于1,等于1其实就可以break
}
}
}
if (idx == -1) 表示含有元素的缓冲栈中,没有满足进入缓冲栈条件的
{
idx = emptyIdx;
}
m_stackCache[idx].push(topVal);
std::cout<<"\t\tpush into m_stackCache "<< idx<<std::endl;
m_stack.pop();
}
/// 判断那个缓冲栈的元素进入到结果栈
for (int ii = 0; ii < num - 1; ii++)
{
if (!m_stackCache[ii].empty()) 首先要判断是否为空
{
/ (记住一点,1,或者说最小的那个值,是不会来缓冲栈里面的)
但是不能因为这一点而不判断结果栈是否为空。
在结果栈是空的时候,也可能到这一步来执行
if (!m_resStack.empty())
{
if (m_stackCache[ii].top() - 1 == m_resStack.top())
{
m_resStack.push(m_stackCache[ii].top());
std::cout<<"\t\t\tpush into m_resStack from m_stackCache "<<ii<<std::endl;
m_stackCache[ii].pop();
ii = ii - 1;/// 这么写是为了暂时不出while循环,看看当前栈是否还有可以进入到结果栈的元素
}
}
}
}
}
}
void stackRailRoad::output(void)
{
std::cout<<std::endl;
while(!m_resStack.empty())
{
std::cout<<"\t"<<m_resStack.top();
m_resStack.pop();
}
std::cout<<std::endl;
}
下面是测试使用的例子,选取的是最占用缓冲栈空间的例子:1,n-1,n-2,…,2
#include <iostream>
#include <stack>
#include "stackRailRoad.h"
void main(void)
{
std::stack<int> st;
st.push(1);
st.push(5);
st.push(4);
st.push(3);
st.push(2);
stackRailRoad srr(st);
srr.deal();
srr.output();
std::system("pause");
}
输出的结果如下截图