Stack(堆栈):一种操作受限制的线性表,只允许在一头进行添加和删除
定义:
· “栈”也叫“堆栈”,是一种操作受限的线性表,栈只允许在线性表的一端进行插入/删除等操作,不允许其他位置插入/删除
· 线性表中可以进行插入/删除的一端称为:栈顶(top),栈顶保存的元素为“栈顶元素”,相对另一端称为“栈底”(bottom)
· 如果栈中没有数据称为“空栈”
· 向栈中插入元素,称为“进栈/入栈/压栈”,从栈中删除元素称为“退栈/出栈/弹栈”;
· 栈的插入/删除操作只允许在栈顶进行,后进栈的元素必定先出栈,称为:后进先出表LIFO;
堆栈抽象数据类型的定义:
ADT Stack{
数据对象 : D={a0,a1,a2...,an, 其中ai(i=1,2..n)是同一种数据类型的元素}
数据关系 : R={<ai,ai+1>}
基本操作:
InitStack(&S): 初始化栈,分配内存空间
DestroyStack(&S): 销毁并释放栈的内存
ClearStack(&S): 清空栈
IsEmpty(S): 判断栈是否为空
GetSize(S): 返回栈元素个数
Peek(S): 偷看栈顶元素
Push(&S,e): 压栈、入栈
Pop(&S,&e): 弹栈、出栈
Traverse(S): 从栈底到栈顶依次遍历
}ADT Stack;
卡特兰数:如果有n个不同元素进栈,出栈元素不同的排列的个数为:1/(n+1) * Cⁿ2n
(还有一种栈叫“共享栈”,逻辑上是2个栈,物理上共享一片连续的内存)
栈的顺序实现:
顺序栈一般通过数组实现;堆栈的操作都在栈顶完成,选择数组中索引值较大的一端作为栈顶,也就是数组尾部作为栈顶;
栈的链式实现:
使用链表作为栈的存储结构,称为链栈;链栈只允许在线性表的一端进行操作,可以选择链表的头部作为栈顶,不管是入栈/出栈都在链表的头节点上进行;
链栈的代码:
//不带头结点的链栈,缺点是获取栈的大小不方便
#define ElemType int
#include <stdio.h>
#include <stdlib.h>
typedef struct Node{ //单链表的节点
ElemType data; //数据域
struct Node* next; //指针域
}Node,Stack;
// InitStack(&S): 初始化栈
void InitStack(Stack** s){
*s = NULL;
}
// DestroyStack(&S): 销毁并释放栈的内存
void DestroyStack(Stack** s){
//空栈,*s没指向任何Node,直接返回
if(*s == NULL) return;
//栈非空,层层销毁
Stack* p;//临时指针p
while((*s)->next != NULL){//若Node的next指针非空,进入循环进行删除
p = *s;
*s = (*s)->next;
printf("删除节点\n");
free(p);
}
//删完之后,此时只剩头节点,将头节点删除
printf("删除节点\n");
free(*s);
*s = NULL;//头指针置空
}
// IsEmpty(S): 判断栈是否为空
int IsEmpty(Stack* s){//栈空返回1
return s == NULL;
}
// GetSize(S): 返回栈元素个数
int GetSize(Stack* s){
Stack* p = s;
int count = 0;
while(p!=NULL){
count++;
p = p->next;
}
return count;
}
// Peek(S): 偷看栈顶元素
ElemType Peek(Stack* s){
if( IsEmpty(s) ){
exit(1);
}
return s->data;
}
// Push(&S,e): 压栈,成功返回1
int Push(Stack** s, ElemType e){
Node* newNode = (Node*) malloc(sizeof(Node));
if(newNode == NULL){ exit(1); }
newNode->data = e;
newNode->next = *s;//使新节点的next指向栈顶元素
//更改栈顶指针
*s = newNode;
return 1;
}
// Pop(&S,&e): 弹栈,并返回该元素
ElemType Pop(Stack** s){
//空栈,弹栈失败并提示信息
if( IsEmpty(*s) ){
printf("Failure, Empty Stack!");
exit(1);
}
//临时指针p,指向旧的栈顶位置
Stack* p = *s;
//保存栈顶元素ret
ElemType ret = (*s)->data;
//更新栈顶指针
*s = (*s)->next;
//弹栈并返回
free(p);
return ret;
}
// Traverse(S): 从栈顶依次遍历
void Traverse(Stack* s){
printf("\n");
Stack* p = s;
while(p!=NULL){
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
//=============测试程序==============================================================
int main(int argc, char const *argv[])
{
Stack* stack;
InitStack(&stack);
//遍历
Traverse(stack);
printf("is IsEmpty:%d\n", IsEmpty(stack));
Push(&stack, 6);
Push(&stack, 16);
Push(&stack, 26);
Push(&stack, 36);
Push(&stack, 46);
//遍历
Traverse(stack);
printf("栈大小为:%d\n", GetSize(stack));
printf("栈顶元素为:%d\n", Peek(stack));
printf("弹出了:%d\n", Pop(&stack));
printf("弹出了:%d\n", Pop(&stack));
printf("弹出了:%d\n", Pop(&stack));
printf("is IsEmpty:%d\n", IsEmpty(stack));
printf("栈大小为:%d\n", GetSize(stack));
//遍历
Traverse(stack);
//销毁栈
DestroyStack(&stack);
Traverse(stack);
printf("is IsEmpty%d\n", IsEmpty(stack));
return 0;
}
运行结果:
结论:c语言实现的链表结构,如果只设置一个结构体来表示链表和结点,虽然省了一个结构体,但是需要双重指针,且获取栈的大小不方便。
---------------------------------------------
以上内容并不完善,欢迎评论指正补充,共同进步OwO ~~