详细的在这篇文章里:https://7vs10.com/article/78
一、什么是栈?
栈是一种数据结构,元素的进出方式类似于“子弹进出子弹夹”。
二、栈的实现(C++、VS2017)
1、新建三个文件,分别为MyStack.h、MyStack.cpp、StackDemo.cpp,代码分别如下,注释详细。
MyStack.h
#define MYSTACK_H
class MyStack {
public:
MyStack(int size); //分配内存,初始化栈空间,设定栈容量,栈顶
~MyStack(); //回收栈空间内存
bool stackEmpty(); //判定栈是否为空,为空的话返回true,非空返回false
bool stackFull(); //判定栈是否已满,为满返回true,不满返回false
void clearStack(); //清空栈
int stackLength(); //已有元素个数
bool push(char elem); //元素入栈函数,栈顶上升
bool pop(char &elem); //元素出栈,栈顶下降
void stackTraverse(bool isFromButtom); //遍历栈中所有元素
private:
char *m_pBuffer; //栈空间指针
int m_iSize; //栈容量
int m_iTop; //栈顶,也代表了栈中的元素个数
};
MyStack.cpp
//具体实现栈类
#include"stdafx.h"
#include "MyStack.h"
#include<iostream>
using namespace std;
// 实现栈的构造函数
MyStack::MyStack(int size) {
m_iSize = size; //首先将 栈的size 赋值给该类的数据成员
m_pBuffer = new char[size]; //有了变量之后就可以申请内存了,用一个指针来指向这段内存,内存的数据类型为char
m_iTop = 0; //初始化栈顶,栈顶为零也就是栈为空了
}
// 实现栈的析构函数,用来释放回收栈所占的内存空间
MyStack::~MyStack() {
delete []m_pBuffer; // 因为是释放一个内存的数组,所以加[]
}
// 实现栈的判空函数
bool MyStack::stackEmpty(){
if (0 == m_iTop) { //很简单,即判断下栈顶iTop是否为0
return true;
}
return false;
}
// 实现栈的判满函数
bool MyStack::stackFull() {
if (m_iTop == m_iSize) {
return true;
}
return false;
}
// 清空栈函数实现,原理很简单,就是将栈顶设为0,这样的话,栈其他的位置无论是啥都没用了,下次再赋新值的时候就会覆盖
void MyStack::clearStack() {
m_iTop = 0;
}
// 获取栈当中已有的元素个数
int MyStack::stackLength() {
return m_iTop; // 很简单,栈顶的数字即元素个数
}
// 实现 元素入栈、栈顶上升 函数
bool MyStack::push(char elem) { // 将elem入栈
if (stackFull()) { // 如果栈已经满了
return false; // 入栈失败
}
m_pBuffer[m_iTop] = elem; // 元素入栈
m_iTop++; // 栈顶上升
return true;
}
// 元素出栈,栈顶下降
bool MyStack::pop(char &elem) {
if (stackEmpty()) {
return false;
}
m_iTop--;// 出栈一定要先将栈顶降低,以指向最高的一个数值,不然栈顶指向的一直是个空的东西
elem = m_pBuffer[m_iTop];// 将当前的栈顶元素给elem不就行了吗
return true;
}
// 遍历整个栈
void MyStack::stackTraverse(bool isFromButtom) { // 加一个判断从顶还是底开始遍历的开关
if (isFromButtom) {
for (int i = 0; i < m_iTop; i++) { // 栈底到栈顶做遍历
cout << m_pBuffer[i] << ",";
}
}
else {
for (int i = m_iTop - 1; i >= 0; i--) { // 栈顶到栈底遍历
cout << m_pBuffer[i] << ",";
}
}
}
StackDemo.cpp
//这是运行文件
//栈类具体实现文件另建
#include "stdafx.h"
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
using namespace std;
int main()
{
MyStack *pStack = new MyStack(5); // 首先实例化MyStack类的一个对象
pStack->push('h'); // 底
pStack->push('e');
pStack->push('l');
pStack->push('l');
pStack->push('o'); // 顶
pStack->stackTraverse(true); // true就是从底到顶遍历
cout << endl;
char elem = 0;
pStack->pop(elem); // 弹出顶
cout << elem << endl;
pStack->clearStack();
cout << pStack->stackLength() << endl; // 输出栈元素的个数
if (pStack->stackEmpty()) {// 通过对象这个指针来判栈是否为空
cout << "栈为空" << endl;
}
if (pStack->stackFull()) {
cout << "栈为满" << endl;
}
delete pStack; // 用完之后销毁指针
pStack = NULL; // 再将指针指向 NULL
system("pause");
return 0;
}
运行结果:
2、上面就已经实现了一个简单的栈,可以对栈进行简单的元素push、pop,但是只能对char类型的元素,然而栈可以对所有类型的元素进行操作,因此下面进行改进:
①我们会定义一个 Coordinate 坐标类;
②改造栈类,使其可以适用于坐标类。
可以灵活掌握栈机制,理解抽象数据类型在栈中的应用。
我们另外建了俩文件,现在一共五个源文件:MyStack.h、MyStack.cpp、StackDemo.cpp、Coordinate.cpp、Coordinate.h,其源代码分别如下:
MyStack.h
#define MYSTACK_H
#include"Coordinate.h"
class MyStack {
public:
MyStack(int size); //分配内存,初始化栈空间,设定栈容量,栈顶
~MyStack(); //回收栈空间内存
bool stackEmpty(); //判定栈是否为空,为空的话返回true,非空返回false
bool stackFull(); //判定栈是否已满,为满返回true,不满返回false
void clearStack(); //清空栈
int stackLength(); //已有元素个数
bool push(Coordinate elem); //元素入栈函数,栈顶上升
bool pop(Coordinate &elem); //元素出栈,栈顶下降
void stackTraverse(bool isFromButtom); //遍历栈中所有元素
private:
Coordinate *m_pBuffer; //栈空间指针
int m_iSize; //栈容量
int m_iTop; //栈顶,也代表了栈中的元素个数
};
MyStack.cpp
//具体实现栈类
#include"stdafx.h"
#include "MyStack.h"
#include<iostream>
using namespace std;
// 实现栈的构造函数
MyStack::MyStack(int size) {
m_iSize = size; //首先将 栈的size 赋值给该类的数据成员
m_pBuffer = new Coordinate[size]; //有了变量之后就可以申请内存了,用一个指针来指向这段内存,内存的数据类型为char
m_iTop = 0; //初始化栈顶,栈顶为零也就是栈为空了
}
// 实现栈的析构函数,用来释放回收栈所占的内存空间
MyStack::~MyStack() {
delete []m_pBuffer; // 因为是释放一个内存的数组,所以加[]
}
// 实现栈的判空函数
bool MyStack::stackEmpty(){
if (0 == m_iTop) { //很简单,即判断下栈顶iTop是否为0
return true;
}
return false;
}
// 实现栈的判满函数
bool MyStack::stackFull() {
if (m_iTop == m_iSize) {
return true;
}
return false;
}
// 清空栈函数实现,原理很简单,就是将栈顶设为0,这样的话,栈其他的位置无论是啥都没用了,下次再赋新值的时候就会覆盖
void MyStack::clearStack() {
m_iTop = 0;
}
// 获取栈当中已有的元素个数
int MyStack::stackLength() {
return m_iTop; // 很简单,栈顶的数字即元素个数
}
// 实现 元素入栈、栈顶上升 函数
bool MyStack::push(Coordinate elem) { // 将elem入栈
if (stackFull()) { // 如果栈已经满了
return false; // 入栈失败
}
m_pBuffer[m_iTop] = elem; // 元素入栈
m_iTop++; // 栈顶上升
return true;
}
// 元素出栈,栈顶下降
bool MyStack::pop(Coordinate &elem) {
if (stackEmpty()) {
return false;
}
m_iTop--;// 出栈一定要先将栈顶降低,以指向最高的一个数值,不然栈顶指向的一直是个空的东西
elem = m_pBuffer[m_iTop];// 将当前的栈顶元素给elem不就行了吗
return true;
}
// 遍历整个栈
void MyStack::stackTraverse(bool isFromButtom) { // 加一个判断从顶还是底开始遍历的开关
if (isFromButtom) {
for (int i = 0; i < m_iTop; i++) { // 栈底到栈顶做遍历
//cout << m_pBuffer[i] << ","; // 因为<<胜任不了输出复杂数据类型,所以不用<<输出
m_pBuffer[i].printCoordinate();
}
}
else {
for (int i = m_iTop - 1; i >= 0; i--) { // 栈顶到栈底遍历
//cout << m_pBuffer[i] << ",";
m_pBuffer[i].printCoordinate();
}
}
}
StackDemo.cpp
//这是运行文件
//栈类具体实现文件另建
#include "stdafx.h"
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
using namespace std;
int main()
{
MyStack *pStack = new MyStack(5); // 首先实例化MyStack类的一个对象
pStack->push(Coordinate(1,2)); // 底
pStack->push(Coordinate(3,4));
pStack->stackTraverse(true); // true就是从底到顶遍历
cout << endl;
pStack->clearStack();
cout << pStack->stackLength() << endl; // 输出栈元素的个数
if (pStack->stackEmpty()) {// 通过对象这个指针来判栈是否为空
cout << "栈为空" << endl;
}
if (pStack->stackFull()) {
cout << "栈为满" << endl;
}
delete pStack; // 用完之后销毁指针
pStack = NULL; // 再将指针指向 NULL
system("pause");
return 0;
}
Coordinate.cpp
#include"stdafx.h"
#include"Coordinate.h"
#include<iostream>
using namespace std;
Coordinate::Coordinate(int x, int y) {
m_iX = x;
m_iY = y;
}
void Coordinate::printCoordinate() {
cout << "(" << m_iX << "," << m_iY << ")" << endl;
}
Coordinate.h
#define COORDINATE_H
class Coordinate {
public:
Coordinate(int x=0,int y=0); // 默认构造函数,很重要
void printCoordinate();
private:
int m_iX;
int m_iY;
};
运行结果:
3、尽管已经实现了可以使用栈存储输出Coordinate坐标类,但我们的目标是使其适用于任何数据类型,因此我们要将普通栈改造为类模板栈。只需要修改几个地方,源代码文件还是五个:MyStack.h、MyStack.cpp、StackDemo.cpp、Coordinate.cpp、Coordinate.h,分别如下:
MyStack.h
#define MYSTACK_H
template<typename T> // 将普通栈改造为类模板栈
class MyStack {
public:
MyStack(int size); //分配内存,初始化栈空间,设定栈容量,栈顶
~MyStack(); //回收栈空间内存
bool stackEmpty(); //判定栈是否为空,为空的话返回true,非空返回false
bool stackFull(); //判定栈是否已满,为满返回true,不满返回false
void clearStack(); //清空栈
int stackLength(); //已有元素个数
bool push(T elem); //元素入栈函数,栈顶上升
bool pop(T &elem); //元素出栈,栈顶下降
void stackTraverse(bool isFromButtom); //遍历栈中所有元素
private:
T *m_pBuffer; //栈空间指针
int m_iSize; //栈容量
int m_iTop; //栈顶,也代表了栈中的元素个数
};
template<typename T>
// 实现栈的构造函数
MyStack<T>::MyStack(int size) {
m_iSize = size; //首先将 栈的size 赋值给该类的数据成员
m_pBuffer = new T[size]; //有了变量之后就可以申请内存了,用一个指针来指向这段内存,内存的数据类型为char
m_iTop = 0; //初始化栈顶,栈顶为零也就是栈为空了
}
template<typename T>
// 实现栈的析构函数,用来释放回收栈所占的内存空间
MyStack<T>::~MyStack() {
delete[]m_pBuffer; // 因为是释放一个内存的数组,所以加[]
}
template<typename T>
// 实现栈的判空函数
bool MyStack<T>::stackEmpty() {
if (0 == m_iTop) { //很简单,即判断下栈顶iTop是否为0
return true;
}
return false;
}
template<typename T>
// 实现栈的判满函数
bool MyStack<T>::stackFull() {
if (m_iTop == m_iSize) {
return true;
}
return false;
}
template<typename T>
// 清空栈函数实现,原理很简单,就是将栈顶设为0,这样的话,栈其他的位置无论是啥都没用了,下次再赋新值的时候就会覆盖
void MyStack<T>::clearStack() {
m_iTop = 0;
}
template<typename T>
// 获取栈当中已有的元素个数
int MyStack<T>::stackLength() {
return m_iTop; // 很简单,栈顶的数字即元素个数
}
template<typename T>
// 实现 元素入栈、栈顶上升 函数
bool MyStack<T>::push(T elem) { // 将elem入栈
if (stackFull()) { // 如果栈已经满了
return false; // 入栈失败
}
m_pBuffer[m_iTop] = elem; // 元素入栈
m_iTop++; // 栈顶上升
return true;
}
template<typename T>
// 元素出栈,栈顶下降
bool MyStack<T>::pop(T &elem) {
if (stackEmpty()) {
return false;
}
m_iTop--;// 出栈一定要先将栈顶降低,以指向最高的一个数值,不然栈顶指向的一直是个空的东西
elem = m_pBuffer[m_iTop];// 将当前的栈顶元素给elem不就行了吗
return true;
}
template<typename T>
// 遍历整个栈
void MyStack<T>::stackTraverse(bool isFromButtom) { // 加一个判断从顶还是底开始遍历的开关
if (isFromButtom) {
for (int i = 0; i < m_iTop; i++) { // 栈底到栈顶做遍历
cout << m_pBuffer[i]; // 因为<<胜任不了输出复杂数据类型,所以不用<<输出
//m_pBuffer[i].printCoordinate();
}
}
else {
for (int i = m_iTop - 1; i >= 0; i--) { // 栈顶到栈底遍历
cout << m_pBuffer[i];
//m_pBuffer[i].printCoordinate();
}
}
}
MyStack.cpp
//具体实现栈类
#include"stdafx.h"
#include "MyStack.h"
#include<iostream>
using namespace std;
StackDemo.cpp
//这是运行文件
//栈类具体实现文件另建
#include "stdafx.h"
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
#include"Coordinate.h"
using namespace std;
int main()
{
//现在MyStack<>这个尖括号里的数据类型你就可以随便使用了,比如在这里用了Coordinate类型的
MyStack<Coordinate> *pStack = new MyStack<Coordinate>(5); // 首先实例化MyStack类的一个对象
pStack->push(Coordinate(1,2)); // 底
pStack->push(Coordinate(3,4));
pStack->stackTraverse(true); // true就是从底到顶遍历
cout << endl;
pStack->clearStack();
cout << pStack->stackLength() << endl; // 输出栈元素的个数
if (pStack->stackEmpty()) {// 通过对象这个指针来判栈是否为空
cout << "栈为空" << endl;
}
if (pStack->stackFull()) {
cout << "栈为满" << endl;
}
delete pStack; // 用完之后销毁指针
pStack = NULL; // 再将指针指向 NULL
system("pause");
return 0;
}
Coordinate.cpp
#include"stdafx.h"
#include"Coordinate.h"
#include<iostream>
using namespace std;
Coordinate::Coordinate(int x, int y) {
m_iX = x;
m_iY = y;
}
void Coordinate::printCoordinate() {
cout << "(" << m_iX << "," << m_iY << ")" << endl;
}
ostream &operator<<(ostream &out, Coordinate &coor) {
out << "(" << coor.m_iX << "," << coor.m_iY << ")" <<endl;
return out;
}
Coordinate.h
#define COORDINATE_H
#include<ostream>
using namespace std;
class Coordinate {
friend ostream &operator<<(ostream &out, Coordinate &coor);
public:
Coordinate(int x=0,int y=0); // 默认构造函数,很重要
void printCoordinate();
private:
int m_iX;
int m_iY;
};
这样一来,你想要使用什么数据类型都可以了,只要在StackDemo.cpp文件里将MyStack<>简括里的数据类型进行相应的修改即可:
比如
三、接下来就是俩用栈来操作的小例子
1、栈应用–进制转换
描述:输入任意的十进制正整数N,分别输出该整数N的二进制、八进制、十六进制表示
公式:N = (N div d)* d + N mod d (div表示整除,mod表示求余)
(1348)(十进制) = (2504)(八进制) = (544)(十六进制) = (10101000100)(二进制)
以后只用 StackDemo.cpp 和 MyStack.h 俩文件就OK了,具体代码如下:
MyStack.h不变,只修改StackDemo.cpp:
//这是运行文件
//栈类具体实现文件另建
#include "stdafx.h"
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
#include"Coordinate.h"
using namespace std;
/*
栈应用--进制转换
描述:输入任意的十进制正整数N,分别输出该整数N的二进制、八进制、十六进制表示
公式:N = (N div d)* d + N mod d (div表示整除,mod表示求余)
(1348)(十进制) = (2504)(八进制) = (544)(十六进制) = (10101000100)(二进制)
*/
// 首先需要定义好进制
#define BINARY 2
#define OCTONARY 8
#define HEXADECIMAL 16
int main()
{
char num[] = "0123456789ABCDEF"; // 为了16进制考虑
MyStack<int> *pStack = new MyStack<int>(30); // 首先实例化MyStack类的一个对象
int N = 2016; // 以2016为例
int mod = 0; // 用 mod 来存储余数
while (N != 0) { // 只要N不为零,就可以一直进行操作,进制转换的原理
mod = N % HEXADECIMAL; // 用八进制举例子
pStack->push(mod);// 不断将余数存储进栈
N = N / HEXADECIMAL;
}
//pStack->stackTraverse(false);// false就是从顶到底遍历
int elem = 0;
while (!pStack->stackEmpty()) {
pStack->pop(elem);
cout << num[elem];
}
delete pStack; // 用完之后销毁指针
pStack = NULL; // 再将指针指向 NULL
system("pause");
return 0;
}
2、栈应用–括号匹配
描述:任意输入一组括号,判断括号是否匹配
字符串示例:[()] [()()] [()[()]] [[()]]
思路就是:将字符串每个字符按照顺序入栈,一旦当有最靠近的俩字符串匹配后,将其出栈,最后所有字符串入栈结束后,我们判断在栈中是否有未出栈的字符,也就是说栈是否为空,为空说明匹配完毕,所有字符串都匹配成功了。(栈顶就是亟需匹配的字符,匹配成功,出栈)
MyStack.h不变,修改StackDemo.cpp即可,代码如下:
//这是运行文件
//栈类具体实现文件另建
#include "stdafx.h"
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
#include"Coordinate.h"
using namespace std;
/*
栈应用--括号匹配(发散思维可知,不止可以括号匹配)
描述:任意输入一组括号,判断括号是否匹配
字符串示例:[()] [()()] [()[()]] [[()]]
思路就是,将字符串每个字符按照顺序入栈,一旦当有最靠近的俩字符串匹配后,将其出栈,最后所有字符串入栈结束后,我们判断在栈中是否有未出栈的字符,也就是说栈是否为空,为空说明匹配完毕,所有字符串都匹配成功了。
*/
int main(void)
{
MyStack<char> *pStack = new MyStack<char>(30);
MyStack<char> *pNeedStack = new MyStack<char>(30);
char str[] = "[()]";
char currentNeed = 0;
for (int i = 0; i < strlen(str); i++) {
if (str[i] != currentNeed) {
pStack->push(str[i]);
switch (str[i]) {
case '[':
if (currentNeed != 0) {
pNeedStack->push(currentNeed);
}
currentNeed = ']';
break;
case '(':
if (currentNeed != 0) {
pNeedStack->push(currentNeed);
}
currentNeed = ')';
break;
default:
cout << "不匹配" << endl;
system("pause");
return 0;
}
}
else {
char elem;
pStack->pop(elem);
if (!pNeedStack->pop(currentNeed)) {// 判断出栈是否成功
currentNeed = 0;
}
}
}
if (pStack->stackEmpty()) {
cout << "匹配啦" << endl;
}
else {
cout << "不匹配" << endl;
}
delete pStack;
pStack = NULL;
delete pNeedStack;
pNeedStack = NULL;
system("pause");
return 0;
}
完。