数据结构-详细分析“栈(Stack)”

详细的在这篇文章里: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;
}

这里写图片描述

完。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值