持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
顺序栈:是指利用顺序存储结构实现的栈
链栈:链式栈是一种数据存储结构,可以通过单链表的方式来实现
两者的区别:
- 顺序栈
- 一次分配空间,直接存,存取的效率比较高
- 存满=>需要扩容,扩容时需要搬数据,扩容比较慢
- 链栈
- 一个一个的分配空间,每个结点的插入和删除都需要分配和释放,速度比较慢
- 不用扩容
使用上具体情况具体分析:
- 需求容量固定,一次分配空间够用了:顺序栈
- 需求不确定,需要一个一个处理:链栈
上一节实现了顺序栈,下面用链栈来实现:
//
// 链栈: 实现链式栈
//
#include <stdio.h>
#include <stdlib.h>
// 链表的每个结点都是相互独立的,需要动态的申请和释放
// 定义结点
typedef struct node {
int data; // 数据域
struct node *next; // 指针域
} node;
// 定义栈(带虚拟头结点)
typedef struct stack {
struct node *head; // 头结点地址
int size; // 内部有多少个元素
} stack;
// 初始化新结点
node *get_new_node(int n) {
node *p = (node *) malloc(sizeof(node *));
p->data = n;
p->next = NULL;
return p;
}
// 初始化: 不需要传参数,没有容量上限,每个结点都是独立的
stack *init_stack() {
stack *s = (stack *)malloc(sizeof(stack));
s->head = get_new_node(0);
s->size = 0;
return s;
}
// 销毁: 先删结点,在删栈
void delete_stack(stack *s) {
node *p = s->head;
while (p!= NULL) {
// 遍历并释放每一个结点
node *q = p->next;
free(p);
p = q;
}
free(s);
}
// 入栈:不涉及容量满了需要扩容的操作, 直接申请一个新的结点放到栈中
// 头插法:插入删除获取栈顶元素,复杂度都是O(1) √ 申请新结点,插入头部
// 尾插法:插入删除获取栈顶元素,复杂度都是O(n)
int push(stack *s, int x) {
node *p = get_new_node(x);
p->next = s->head->next;
s->head->next = p;
s->size++;
return 0;
}
// 出栈
int pop(stack *s) {
if (s->size == 0) {
return -1; // 判断栈是否为空,为空则出栈失败
}
// 删除栈顶,释放空间:出的是栈顶元素,虚头的下一个元素(空间也需要释放掉)
node *p = s->head->next;
s->head->next = p->next;
free(p);
s->size--;
return 0;
}
// 获取栈顶元素
int top(stack *s) {
if (s->size == 0) {
return -1;
}
return s->head->next->data;
}
// 打印栈
void show_stack(stack *s) {
printf("size = %d\n", s->size);
for(node *p = s->head->next; p != NULL; p = p->next) {
printf("%d ", p->data);
}
printf("\n-------------------------\n");
}
int main() {
int n; // 操作次数
scanf("%d", &n);
stack *s = init_stack(); // 不需要传参数,没有容量上限,每个结点都是独立的
for(int i = 0; i < n; i++) {
// 0:入栈 1:出栈 其他:获得栈顶元素
int a;
scanf("%d", &a);
if (a == 0) {
scanf("%d", &a);
push(s, a);
} else if (a == 1) {
pop(s);
} else {
printf("top -> %d\n", top(s));
}
show_stack(s);
}
delete_stack(s);
return 0;
}