本文仅是我用来整理学过的东西,可能有些许错误,欢迎各位大佬指正。
栈的性质
1.像一个杯子一样,不断往杯底压入数据,数据从杯口输出。(先进后出,后进先出)
顺序栈
1.即用数组来储存栈。
优点:简单方便。
缺点:使用静态数组储存,大小固定,可能会上溢,还会有存储空间的浪费。
2.拥有的变量:一个指向数组的指针,数组的实际大小(即你要开多大的数组),当前栈顶的位置。
3.拥有的函数:压入,弹出,获得栈顶元素,判断栈满,栈空,将栈设为空。
下面是一般栈的定义。
class Stack
{
private:
char *data;
int size;
int top;
public:
Stack();
Stack(int s);
~Stack();
void push( char ch ); //成员函数:入栈
void pop( ); //成员函数:出栈
char getTop( ); //成员函数:获得栈顶元素(不出栈)
bool isEmpty( ); //成员函数:栈是否为空
bool isFull( ); //成员函数:栈是否满
void setNull(); //设置栈为空
};
4.构造函数(初始化)
1.创建一个数组(如果是有参函数,数组大小为传入的参数值)
2.数组实际大小设为0;
3.top=-1;
5.析构函数:因为是new语句申请的数组空间所以要主动删除。
其他函数的过程很简单这里直接放代码。
Stack::Stack()
{
size=MAX_SIZE;
data=new char[size];
top=-1;
}
Stack::Stack(int s)
{
size=s;
data=new char[size];
top=-1;
}
Stack::~Stack()
{
delete [] data;
}
void Stack::push(char ch)
{
if(isFull())return;
data[++top]=ch;
}
void Stack::pop()
{
if(isEmpty())return;
top--;
}
char Stack::getTop()
{
return data[top];
}
bool Stack::isEmpty()
{
if(top==-1)
{
return true;
}
else
{
return false;
}
}
bool Stack::isFull()
{
if(top+1==size)
{
return true;
}
else
{
return false;
}
}
void Stack::setNull()
{
top=-1;
}
注意:其中的函数的返回类型可以改成bool。
总结来说顺序栈很简单,容易理解,但如果要存储的数据数量变化大的话就不建议使用,平常一般使用链栈。
链栈
1.用指针将一个个的数据串起来。
小知识点:数组在申请存储空间的时候,是申请一整块的完整空间,所以数组不能开得很大。
所以链栈的优缺点和顺序栈是相反的。
优点:不浪费空间,空间的利用率高。
缺点:就是太难理解,对于指针不好的小伙伴不友好。
2.定义结构体
一个数据变量data(我在例子中写的是ch,问题不大)
一个指针域指向结构体这种类型(额,一般的结构体好像都这么定义,包括后面的其它数据结构)
struct node
{
char ch;//字符
struct node* next;//指针
};
其实可以用类模板(简单点说就是数据变量的类型不确定)
不知道的小伙伴也没关系,其实我也不清楚......
3.拥有的成员
一个指向头结点的指针first,成员函数功能和顺序栈的差不多
注意:first本身的就是第一个有效结点
class LinkStack
{
public:
/** Default constructor */
LinkStack();
/** Default destructor */
~LinkStack();
/**< 数据压入堆栈 */
void push(char ch);
/**< 弹出栈顶数据 */
void pop();
/**< 读取栈顶数据 */
char getTop();
/**< 判断栈是否为空 */
bool isEmpty();
/**< 从顶部打印堆栈 */
void displayFromTop();
private:
struct node* first;//!< Member variable "指向栈顶的元素"
};
4.函数的实现
1.构造函数
LinkStack::LinkStack()
{
first = NULL;
}
2.push函数(压入数据)
使用头插法
void LinkStack::push(char x)
{
struct node* p =new struct node;
p->ch = x;
p->next = first;
first = p;
return;
}
3.pop函数(弹出数据,但不输出)
void LinkStack::pop()
{
if (isEmpty())
{
return;
}
cout << first->ch << endl;
first = first->next;
return;
}
输出语句可写可不写
4.getTop函数(获得最后压入的函数,但不弹出这个数据)
由于是头插法插入的所以first其实指向的最后一个插入的数
例如输入abcd
实际插入顺序是
1.first->a
2.first->b->a
3.first->c->b->a
4.first->d->c->c->a
所以获取头顶元素就是获取first的data值
char LinkStack::getTop()
{
if (isEmpty())
{
cout << "栈为空" << endl;
return 0;
}
return first->ch;
}
5.isEmpty函数(判断栈是否为空)
bool LinkStack::isEmpty()
{
if (first== NULL)
{
return true;
}
return false;
}
6.displayFromTop函数(从头开始依次输出数据)
void LinkStack::displayFromTop()
{
struct node* p = first;
if (isEmpty())return;
while (p != NULL)
{
cout << p->ch;
p = p->next;
}
return;
}
7.析构函数
放到最后是因为我觉得最难理解
先看一下代码
LinkStack::~LinkStack()
{
struct node* p = first;
struct node* q = first->next;
while (q!=NULL)
{
delete p;
p = q;
q = q->next;
}
delete p;
return;
}
由于我的判断条件是q!=NULL,所以最后的情况是p指向最后一个数据(且没删除)q为空指针。
所以最后要删除p结点的数据。
STL的用法
因为在平常的使用中不可能每次想用栈就把栈类重新写一遍,所以我们可以使用C++已经为我们准备好的栈类
#include<stack> //头文件
stack<int> s; //声名一个储存int类型的栈 名字叫s
stack<string> s; //声名一个储存string类型的栈 名字叫s
其中的函数功能与我上面写的差不多。
如果不记得有那些函数,可以打出栈名然后按一下 '.' 就会出现栈拥有的函数,基本是看名字就知道是什么意思。