1.定义:栈(stack)是只允许在一端进行插入或删除操作的线性表
重要术语:栈顶、栈底、空栈
栈顶:允许插入和删除的一端 栈底:不允许插入和删除的一端
栈的特点:后进先出(LIFO ~ last in first out)
n个不同的元素进栈,出栈元素不同排列的个数有
种
2.顺序栈
(1)顺序栈的定义:
#define MAXSIZE 10 //定义栈中元素个数
typedef struct {
int a;
double b;
}*ElemType; //栈中元素的类型
typedef struct SqStack {
ElemType data[MAXSIZE]; //用静态数组存放栈中元素
int top; //栈顶指针
}SqStack;
(2)栈的初始化:
两种思想:
a.栈顶指针用于指向栈顶元素的位置,所以在没有元素时,让top指针指向0位置是不合理的。
因此,规定在栈中没有元素时,栈顶指针指向-1。
b.栈顶指针指向栈顶元素的下一个位置,因此在没有元素时,让top指针指向0位置
本文采用思想a,至于思想b,请各位读者自行实现。
代码实现如下:
void InitStack(SqStack &S) {
S.top = -1;
}
(3)判断是否为空栈:
由(2)可知,当栈为空时,栈顶指针指向-1位置,若空栈返回true,非空栈返回false,则实现如下:
bool EmptyStack(SqStack S) {
if (-1 == S.top) //空栈
return true;
return false;
}
(4)入栈(增):
入栈需要考虑栈的内存是否已满,若已满则返回false。
bool Push(SqStack& S, ElemType e) {
if (S.top + 1 == MAXSIZE) //栈满
return false;
S.top += 1; //让栈顶指针向后移动一位
S.data[S.top] = e;
return true;
}
//上述操作也可简化为如下代码
bool Push(SqStack& S, ElemType e) {
if (S.top + 1 == MAXSIZE)
return false;
S.data[++S.top]=e; //注意:由于需要先++后运算,所以不能写成S.top++
return true;
}
(5)栈顶元素出栈(删):
出栈操作需要删除栈顶元素,并用e将其带回,同时使栈顶指针减1
出栈操作需要考虑到栈是否为空,若为空栈,则无法实现栈顶元素出栈操作,故返回false
bool Pop(SqStack& S, ElemType& e) { //因为要用元素e将栈顶元素带回,因此需要用到引用&
if (-1 == S.top) //空栈
return false;
e = S.data[S.top];
S.top--; //栈顶指针下移
return true;
}
//同入栈函数,出栈函数也可简化为:
bool Pop(SqStack& S, ElemType& e) {
if (-1 == S.top) //空栈
return false;
e=S.data[S.top--];
return true;
}
(6)读取栈顶元素(查):
该函数与出栈函数几乎相同,唯一的不同是栈顶指针不用下移
bool GetTop(SqStack S,ElemType& e) {
if (-1 == S.top) //空栈
return false;
e = S.data[S.top];
return true;
}
(7)顺序栈的缺点:栈的大小不可变,可能会导致大片空间的浪费,可以采用共享栈来减少空间的浪费。
3.共享栈:两个栈共享一片存储空间
判断共享栈满的条件:top0+1==top1
这里只简单列举一些操作,有关共享栈的其他操作大家可以自行实现。
(1)共享栈的定义:
typedef struct {
ElemType data[MAXSIZE];
int top0, top1; //0号栈顶指针和1号栈顶指针
}ShStack;
(2)初始化共享栈:
void InitStack(ShStack& S) { //两个栈分别从栈底和栈顶出发
S.top0 = -1;
S.top1 = MAXSIZE;
}
(3)判断是否为空栈:
bool Empty(ShStack S) {
if (S.top0 == -1 && S.top1 == MAXSIZE)
return true;
return false;
}
4.链栈:用链式存储的方式实现的栈
(1)链栈的定义:
与链表的定义相同
typedef struct LinkNode{
ElemType data;
struct LinkNode* next;
}*LiStack;
(2)链栈的初始化:
//对于链栈,我们常采用不带头结点的链表来实现
void InitStack(LiStack& S) {
S = NULL;
}
(3)链栈的判空:
bool Empty(LiStack S) {
if (S == NULL) //空栈
return true;
return false;
}
(4)入栈:
链栈插入元素的方式其实就是链表对头结点的后插操作。

bool Push(LiStack& S,ElemType e) { //链栈不会有栈满的情况,因此不需要判断
LinkNode* p = new LinkNode;
if (p == NULL)
return false;
p->data = S->data; //将头指针中存放的值赋给新的指针p
S->data = e; //将头指针中的值改为e
p->next = S->next; //让p指向头指针的下一个结点
S->next = p; //让头指针的下一个结点为p
return true;
}
(5)出栈:
bool Pop(LiStack &S, ElemType& x) {
if (S == NULL) //空栈
return false;
x = S->data; //将栈顶元素赋给x
LinkNode* p = new LinkNode;
p = S; //让p指针指向S
S = S->next;
delete p; //删除栈顶元素
return true;
}
(6)获取栈顶元素:
bool GetTop(LiStack S, ElemType& x) {
if (S == NULL) //空栈
return false;
x = S->data;
return true;
}
本文介绍了栈的基本概念,包括栈顶、栈底和后进先出的特点。详细阐述了顺序栈的定义、初始化、判断空栈、入栈、出栈和读取栈顶元素的操作。此外,讨论了顺序栈的缺点并提出了共享栈的概念,指出共享栈在空间利用上的优势。最后,简要提及了链栈作为另一种实现方式,同样包含了初始化、入栈、出栈和获取栈顶元素等操作。
601

被折叠的 条评论
为什么被折叠?



