栈是在线性表上加以一定的约束,使其只能在一段进行添加和删除操作,俗称堆栈,在允许操作的一段称为栈顶,另一端称为栈底,当栈内没有元素时称为空栈。特点是后进先出(LIFO)。
栈的存储方式上,仍然可以使用顺序存储和链式存储,所以分为链式栈和顺序栈。
链式栈是栈的另一种存储方式,通过对链表的约束,来实现栈的逻辑,这种结构的优点是不需要预先分配固定大小的存储空间,空间利用比较灵活。
既然是对链表约束,那么为了更清晰更方便的使用链表,就定义一个带头节点的单向链表。
1.1 链式栈的节点结构
typedef int Element; //定义一个数据类型
typedef struct stackNode{
Element data; //节点结构的数据
struct stackNode *next; //指向下一个节点的指针
} StackNode;
1.2 链式栈的头结构
typedef struct{
StackNode *top; //定义了栈顶指针,方便进行插入和删除操作
int count; //链式栈以存储的元素个数
} LinkStack;
2.1 创建完头节点和节点结构后,创造一个链式栈
LinkStack *createLinkStack(){
LinkStack *stack = malloc(sizeof(LinkStack)); //申请一个大小为LinkStack的空间作为头节点stack
stack->top = NULL; //栈顶指针指向NULL此时栈内没有元素
stack->count = 0; //栈内元素数量为0
return stack;
}
2.2 有创建就要有删除
void releaseLinkStack(LinkStack *stack){
if(stack){ //如果这个栈存在
//释放栈中所有的元素
while(stack->top){ //如果栈顶指针top指向NULL,就说明栈内无元素,结束循环
StackNode *tmp = stack->top; //辅助指针tmp指向top
stack->top = tmp->next; //栈顶指针移动到下一个待删除元素的位置
free(tmp); //释放tmp指向的待删除的节点结构
--stack->count; //数量-1
}
}
}
3.1 压栈
压栈操作,先申请新元素,这个新元素,一定是栈顶指针,那选择头插还是尾插?
一旦尾插,由于是单向链表,就再也不能往回走,不能找到尾部元素的前一个元素,出栈就无能为力。
所以只能采用头插法,新元素先处理,再更新top指针
int pushLinkStack(LinkStack *stack,Element e){
StackNode *node = malloc(sizeof(LinkNode)); //申请一块大小为LinkNode的空间作为节点结构node
if(node == NULL){ //申请失败,返回-1
return -1;
}
node->data = e; //新节点结构的数据 (要插入A)
node->next = stack->top; //新节点结构的下一个指针指向栈顶指针top top<-A
stack->top = node; //栈顶指针指向新的节点结构 top->A
//整个链式栈最终是 top<->C<-B<-A
++stack->count; //数量=1
}
3.2 弹栈
也是从top指针指向的栈顶往外释放
int popLinkStack(LinkStack *stack,Element *e){
if(stack->top == NULL){ //如果top指向NULL,就表示栈中没有元素
return -1; //弹栈错误
}
*e = stack->top->data; //将栈顶的元素取出,存在e中
//开始 top<-A<-B<-C
StackNode *tmp = stack->top; //辅助指针tmp指向top
stack->top = tmp->next; //将top向下移动,移动到下一个待操作的元素节点
free(tmp); //释放tmp指向的节点结构
--stack->count; //数量-1
//结束 top<-B<-C
return 0;
}