目录
前言
有一说一,昨天学的确实少。今天看了看栈的内容,发现有一个问题,栈用顺序存储的形式实现比较简单,用链式存储来实现有点麻烦。当时学习的时候,我认为栈的链式存储方式和单链表一样,就没有去细想,现在仔细想想还是有不一样的地方。
比如头节点的指针最好是prior指针,这样方便进行出入栈的操作。这是我还没有实现时的想法,等等我再去找点资料好好复习复习。
一、栈的基本概念
栈 (Stack) 是只允许在一端进行插入和删除操作的线性表
基本特点是先进后出(FILO),例如电梯
术语:栈底、栈顶、空栈
1、栈底:不允许进行操作的一端
2、栈顶:允许进行操作的一端
3、空栈:栈中无元素
二、栈的顺序存储
1、初始化
此处比较简单,由于顺序栈与顺序表不一样,顺序栈没有位序的概念,只有栈顶与栈底的概念,所以顺序栈中第一个元素一般记为0,而不是1,所以在初始化时需要注意top的值。
不过也可以与顺序表相对应,将第一个元素记为1,但需要注意其在数组中的下标为0。
typedef int ElemType;
#define MaxSize 10
typedef struct {
ElemType data[MaxSize];
int top; //指向栈顶
} SqStack;
//初始化顺序栈
bool InitSqStack(SqStack &stack) {
stack.top = -1; //top记为 -1 表示栈顶为空
return true;
}
int main() {
SqStack stack;
if(InitSqStack(stack)) printf("Initial Success!\ntop = %d\n", stack.top);
return 0;
}
2、判空
判断是否为空可以用top来判断,需要考虑初始化时top的赋值。此处top == -1 时表示栈空。
//判断顺序栈是否为空
//初始化时top=-1
bool isEmpty(SqStack stack) {
if(stack.top == -1) return true;
else return false;
}
3、入栈
当栈不满时才可以添加,只能在栈顶除添加。
//入栈,在顺序栈stack中添加数据元素elem
bool Push(SqStack &stack, ElemType elem) {
if(stack.top >= MaxSize-1) return false; //栈满,无法插入
stack.top += 1; //将top指向第一位元素存放位置(此处top起始为-1)
stack.data[stack.top] = elem; //赋值
return true;
}
4、出栈
当栈不为空时才能出栈,并获取该元素。
//出栈,将栈顶元素记录在elem中
bool Pop(SqStack &stack, ElemType &elem) {
if(isEmpty(stack)) return false; //栈空,无栈顶元素
elem = stack.data[stack.top]; //存储
stack.top -= 1; //top下移一位
return true;
}
5、获取栈顶元素
出栈时怎么获取栈顶元素,这边就怎么获取
//读栈顶元素
bool getTop(SqStack stack, ElemType &elem) {
if(isEmpty(stack)) return false; //栈空,无栈顶元素
elem = stack.data[stack.top]; //存储
return true
}
6、优缺点
优缺点与顺序表的优缺点一样。
7、测试
int main() {
SqStack stack;
ElemType elem;
if(InitSqStack(stack)) printf("Initial Success!\n\n");
if(isEmpty(stack)) printf("Stack is Empty!\n\n");
for(int i = 0; i<6; i++) {
ElemType temp = rand() % 10 + 1; //生成一个1~10的数
if(Push(stack, temp)) printf("Push %d Success! top = %d\n", temp, stack.top);
else printf("Push %d Fail! top = %d\n", temp, stack.top);
}
puts("");
if(Pop(stack, elem)) printf("Pop %d Success! top = %d\n\n", elem, stack.top);
else printf("Pop Fail! top = %d\n\n", stack.top);
if(getTop(stack, elem)) printf("Get Top Data %d Success! top = %d\n", elem, stack.top);
else printf("Get Top Data Fail! top = %d\n", stack.top);
return 0;
}
三、共享栈
两个栈共用一个存储空间。
初始化时,top0 = -1,top1 = MaxSize,判满则是top0 + 1 == top1
四、栈的链式存储
我的构想如图。话说,如果只有一个指针,是否用next更好?
1、初始化
与单链表一样,初始化分为带头节点的初始化与不带头节点的初始化。两种初始化都行,我个人倾向于使用不带头节点的初始化方法。
typedef int ElemType;
#define MaxSize 10
typedef struct StackNode {
ElemType data;
struct StackNode *prior;
} LNode, *LinkStack;
//带头节点的初始化
bool InitStack_Test(LinkStack &stack){
stack = (LNode*)malloc(sizeof(LNode));
if(stack == NULL) return false; //声明空间出错
stack->prior = NULL; //头节点指向NULL
return true;
}
//不带头节点的初始化
//stack指向的时链栈的栈顶,即top
bool InitStack(LinkStack &stack) {
stack = NULL; //初始指向NULL
return true;
}
2、判空
由于我用的是不带头节点的初始化,所以当栈顶指针stack指向NULL时,该链栈为空。
//判空
bool isEmpty(LinkStack stack) {
if(stack) return false; //stack不指向NULL
return true;
}
3、入栈
入栈比较简单,在插入新节点后,将栈顶指针stack指向该节点即可。
//入栈,数据元素为elem
bool Push(LinkStack &stack, ElemType elem) {
LNode* node = (LNode*)malloc(sizeof(LNode));
if(node == NULL) return false; //声明空间失败
node->data = elem; //赋值
node->prior = stack; //将stack指向的节点变为node的前驱
stack = node; //将stack指向node指向的节点
return true;
}
4、出栈
注意要用free函数释放空间
//出栈,将栈顶元素存到elem中
bool Pop(LinkStack &stack, ElemType &elem) {
if(isEmpty(stack)) return false; //栈空,无栈顶元素
LNode* node = stack; //node指向stack的栈顶
elem = node->data; //转移数据
stack = node->prior; //stack向前移
free(node); //释放栈顶节点
return true;
}
5、取栈顶元素
出栈怎么转移元素,这边就怎么取元素
//取栈顶元素,存到elem中
bool getTop(LinkStack stack, ElemType &elem) {
if(isEmpty(stack)) return false; //栈空,无栈顶元素
elem = stack->data; //转移元素
return true;
}
6、测试
void PrintStack(LinkStack stack) {
while(stack) {
printf("%d ", stack->data);
stack = stack->prior;
}
puts("\n");
}
int main() {
LinkStack stack; //测试用链栈
ElemType elem; //测试用数据元素
if(InitStack(stack)) printf("Initial Success!\n\n");
for(int i = 0; i<6; i++) {
ElemType temp = rand() % 10 + 1; //生成一个1~10的数
if(Push(stack, temp)) printf("Push %d Success!\n", temp);
else printf("Push %d Fail!\n", temp);
}
PrintStack(stack);
if(Pop(stack, elem)) printf("Pop %d Success!\n", elem);
else printf("Pop Fail!\n");
PrintStack(stack);
if(getTop(stack, elem)) printf("Get Top Data %d Success!\n", elem);
else printf("Get Top Data Fail!\n");
return 0;
}
后记
光顾着敲代码了,没有写上自己的感悟。我觉得自己对于指针的使用已经掌握了一些皮毛了,指针只是一个定位器,指向的是地址、是空间。对指针进行操作,就是对那个空间进行操作。
所以操作指针就是操作空间。使用指针时,不要只想着节点中的数据;而是要想着节点所在的空间,然后才是空间中存储数据元素的那块空间中的数据。
明天就是队列的相关知识,有普通队列,有循环队列,可以用顺序存储的形式,也能用链式存储的形式。应该是比较好实现的。