数据结构之栈

本文介绍了栈的基本概念、数组实现方法,并通过模板类定义通用栈。演示了栈在数制转换和括号匹配问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下代码基本来自于慕课网:http://www.imooc.com/learn/611


1.栈的定义

栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。


2.栈的数组实现

才发现代码中的单词stack写成了stake,凑合看吧,懒得改了。

stake.h:

#include<iostream>
using namespace std;

class MyStake
{
public:
    MyStake(int size); //initiate stake;
    ~MyStake(); //destroy stake;
    void clearStake(); //clear stake;
    bool stakeEmpty(); //determine whether stake is empty;
    bool stakeFull(); //determine whether stake is full;
    int stakeLength(); //get length of the stake;
    bool push(char elem); //push new element in stake;
    bool pop(char &elem); //pop top element out of stake;
    void stakeTraverse(bool isFromButtom); //print elements of the stake
private:
    char *m_pBuffer; //pointer points to the stake
    int m_iSize; //capacity of the stake
    int m_iTop; //top of the stake
};


stake.cpp:

#include<iostream>
#include"stake.h"
using namespace std;


MyStake::MyStake(int size)
{
    m_iSize = size;
    m_pBuffer = new char[m_iSize];
    m_iTop = 0;
}

MyStake::~MyStake()
{
    delete []m_pBuffer;
    m_pBuffer = NULL;
}

void MyStake::clearStake()
{
    m_iTop = 0;//if m_iTop=0,then even the stake still contains others,it will be covered lately.
}

bool MyStake::stakeEmpty()
{
    if(0 == m_iTop ) // compares to if(m_iTop == 0 ) ,it can avoid "if(m_iTop = 0 )"
    {
        return true;
    }
    else
    {
        return false;
    }
}

bool MyStake::stakeFull()
{
    if(m_iTop == m_iSize)
    {
        return true;
    }
    else
    {
        return false;
    }
}

int MyStake::stakeLength()
{
     return m_iTop;
}

bool MyStake::push(char elem)
{
     if(stakeFull())
     {
        return false;
     }
     else
     {
        m_pBuffer[m_iTop] = elem;
        m_iTop++;
        return true;
     }
}

bool MyStake::pop(char &elem)
{
    if(stakeEmpty())
     {
        return false;
     }
     else
     {
        elem = m_pBuffer[m_iTop - 1];
        m_iTop--;
        return true;
     }
}

void MyStake::stakeTraverse(bool isFromButtom)
{
     if(isFromButtom)
     {
         cout << "-----------------" << endl;
         for(int i = m_iTop-1; i >=0; i--)
         {
         cout << m_pBuffer[i] << ",";
         }
         cout << endl;
         cout << "-----------------" << endl;
     }
     else
     {
        cout << "-----------------" << endl;
        for(int i = 0; i < m_iTop; i++)
        {
         cout << m_pBuffer[i] << ",";
        }
        cout << endl;
        cout << "-----------------" << endl;
     }

}


main.cpp:

#include <iostream>
#include "stake.h"

using namespace std;

int main()
{
    MyStake *s = new MyStake(5);

    s -> push('a');

    s -> push('b');
    s -> push('c');
    s -> push('d');
    s -> push('e');


    s -> stakeTraverse(true);
    s -> stakeTraverse(false);

    char element;
    s -> pop(element);
    cout << "pop:" << element << endl;
    s -> stakeTraverse(true);

    s -> clearStake();
    s -> stakeTraverse(true);

    delete s;
    s = NULL;

    return 0;
}


程序运行结果:


3.使用模板类来定义栈

对于很多编译器而言,不支持.h和.cpp文件的分开编译,所以,对于栈模板而言,我们在这里将两个文件进行合并。下面,先上代码。

stack.h:

#include<iostream>
using namespace std;

template <typename T>
class MyStake
{
public:
    MyStake(int size); //initiate stake;
    ~MyStake(); //destroy stake;
    void clearStake(); //clear stake;
    bool stakeEmpty(); //determine whether stake is empty;
    bool stakeFull(); //determine whether stake is full;
    int stakeLength(); //get length of the stake;
    bool push(T elem); //push new element in stake;
    bool pop(T &elem); //pop top element out of stake;
    void stakeTraverse(bool isFromButtom); //print elements of the stake
private:
    T *m_pBuffer; //pointer points to the stake
    int m_iSize; //capacity of the stake
    int m_iTop; //top of the stake
};

template <typename T>
MyStake<T>::MyStake(int size)
{
    m_iSize = size;
    m_pBuffer = new T[m_iSize];
    m_iTop = 0;
}

template <typename T>
MyStake<T>::~MyStake()
{
    delete []m_pBuffer;
    m_pBuffer = NULL;
}

template <typename T>
void MyStake<T>::clearStake()
{
    m_iTop = 0;//if m_iTop=0,then even the stake still contains others,it will be covered lately.
}

template <typename T>
bool MyStake<T>::stakeEmpty()
{
    if(0 == m_iTop ) // compares to if(m_iTop == 0 ) ,it can avoid "if(m_iTop = 0 )"
    {
        return true;
    }
    else
    {
        return false;
    }
}

template <typename T>
bool MyStake<T>::stakeFull()
{
    if(m_iTop == m_iSize)
    {
        return true;
    }
    else
    {
        return false;
    }
}

template <typename T>
int MyStake<T>::stakeLength()
{
     return m_iTop;
}

template <typename T>
bool MyStake<T>::push(T elem)
{
     if(stakeFull())
     {
        return false;
     }
     else
     {
        m_pBuffer[m_iTop] = elem;
        m_iTop++;
        return true;
     }
}

template <typename T>
bool MyStake<T>::pop(T &elem)
{
    if(stakeEmpty())
     {
        return false;
     }
     else
     {
        elem = m_pBuffer[m_iTop - 1];
        m_iTop--;
        return true;
     }
}

template <typename T>
void MyStake<T>::stakeTraverse(bool isFromButtom)
{
     if(isFromButtom)
     {
         cout << "-----------------" << endl;
         for(int i = m_iTop-1; i >=0; i--)
         {
         cout << m_pBuffer[i];
         }
         cout << endl;
         cout << "-----------------" << endl;
     }
     else
     {
        cout << "-----------------" << endl;
        for(int i = 0; i < m_iTop; i++)
        {
         cout << m_pBuffer[i];
        }
        cout << endl;
        cout << "-----------------" << endl;
     }
}


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;
};

Coordinate::Coordinate(int x,int y)
{
    m_iX = x;
    m_iY = y;
}

ostream &operator<< (ostream &out,Coordinate &coor)
{
   out << "(" << coor.m_iX << "," << coor.m_iY << ")" << endl;
   return out;
}


main.cpp:

#include <iostream>
#include "stack.h"
#include "coordinate.h"
using namespace std;

int main()
{
    MyStake<Coordinate> *s = new MyStake<Coordinate>(5);

    s -> push(Coordinate(1,2));

    s -> push(Coordinate(3,4));
    s -> push(Coordinate(5,6));


    s -> stakeTraverse(true);
    s -> stakeTraverse(false);

    Coordinate element = Coordinate(0,0);
    s -> pop(element);
    cout << "pop:" << element << endl;
    s -> stakeTraverse(true);

    s -> clearStake();
    s -> stakeTraverse(true);

    delete s;
    s = NULL;

    return 0;
}

程序运行结果:


(1)使用模板类来定义栈,与定义单一数据类型的栈的区别在于,将原有的数据类型替换为“T”。并且,在类定义和每个成员函数的前面加上,“template <typename T>”。我们已知,当成员函数在类外定义时,必须在函数名前面加上类名,予以限定。所以,我们还需要在函数名前面的类名后面加上"<T>"。

(2)注意到,在MyStake类的构造函数中,我们使用了如下语句:m_pBuffer = new T[m_iSize];。而对于复杂数据类型而言,必须要有一个默认的构造函数。所以,在上述代码中定义的复杂类型:Coordinate,有一个默认的构造函数,Coordinate(int x = 0,int y = 0);

(3)同时,在main.cpp中,我们创建Coordinate对象的时候没有使用new关键字。这里面有所讲究。

作用域不同:

不用new:作用域限定在定义类对象的方法中,当方法结束时,类对象也被方法释放了。这样比较安全,不会造成内存泄漏。

用new:创建的是指向类对象的指针,作用域变成了全局。当程序结束时,必须用delete来删除,系统不会自动释放。

从而可以看出,我们在这里不使用new的原因


4.栈的应用

(1)数制转换

输入任意的十进制正整数N,分别输出该正整数N的二进制、八进制和十六进制的数。

公式:N = (N div d)* d + N mod d

1348)(十进制)= (2504)(八进制)= (544)(十六进制)=(10101000100)(二进制)


我们已经在前面定义了栈的模板类,因此,下面可以直接使用栈。首先,来看代码:

dataTransfer.cpp:

#include <iostream>
#include "stack.h"
#include "coordinate.h"
using namespace std;

#define BINARY       2
#define OCTONARY     8
#define HEXADECIMAL 16

int main()
{
    MyStake<int> *s = new MyStake<int>(12);
    char num[] = "0123456789ABCDEF";

    int N = 180;
    int mod = 0;

    while(N!=0)
    {
        mod = N % OCTONARY;
        s -> push(mod);
        N = N / OCTONARY;
    }


    //s ->stakeTraverse(true);

    int element = 0;
    while(!(s -> stakeEmpty()))
    {
        s->pop(element);
        cout << num[element];
    }

    delete s;
    s = NULL;

    return 0;
}

程序运行结果如下:



(2)括号匹配

任意输入一组括号,判定括号是否匹配。

字符串示例:[()] [()()] [()[()]] [()]]

首先,来看一下主程序中的代码:

match.cpp:

#include <iostream>
#include "stack.h"
#include "stdlib.h"
#include "coordinate.h"
#include "string.h"
using namespace std;

int main()
{
    MyStake<char> *pStack = new MyStake<char>(30);
    MyStake<char> *pNeedStack = new MyStake<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 << "字符串" << str << "括号不匹配" << endl;
                return 0;
             }
         }
         else
         {
             char elem;
             pStack ->pop(elem);
             if(!pNeedStack ->pop(currentNeed))
             {
                currentNeed = 0;
             }

         }
    }

    if(pStack ->stakeEmpty()){
        cout << "字符串" << str << "括号匹配" << endl;
    }
    else
    {
        cout << "字符串" << str << "括号不匹配" << endl;
    }

    delete pStack;
    pStack = NULL;

    delete pNeedStack;
    pNeedStack = NULL;

    return 0;
}

运行结果如下:



这段代码有几个关键点需要注意:

(1)我们声明了两个栈,一个是pStack,用来保存暂未匹配的元素;一个是pNeedStack,用来保存下一个匹配需要用到的元素。

为什么要强调“下一个”呢?因为对于当前读取的字符,匹配需用到的元素已经赋给了currentNeed变量。如果当前字符等于currentNeed,则从栈中取出栈顶元素,作为下一个currentNeed;如果当前字符不等于currentNeed,则在currentNeed不为0的情况下,将currentNeed压入栈中,由与当前字符相匹配的字符来作为currentNeed。

理解这个“下一个”是非常重要的。

(2)当字符串的长度为奇数(2n+1)时,如果字符串的前(2n)个字符串的括号在第(2n)次for循环的时候刚好匹配,那么在第(2n)次for循环的时候,当执行到如下语句:pNeedStack ->pop(currentNeed) 的时候,该语句会返回false,也就是pop失败,因为此时栈为空。那么就以例子中的“[[][][][()]]]”字符串为例,此时的currentNeed仍为'[',所以如果不对该语句加以判断,最后会得到该字符串括号匹配的结论。所以,我们在程序中,选择对其判断,如果为false,则将currentNeed的值赋为0。


关于栈的理解,就暂时告一段落。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值