以下代码基本来自于慕课网: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。
关于栈的理解,就暂时告一段落。