1.基本概念
栈是一种逻辑结构,是特殊的线性表,特殊在只能在固定一端操作
只要满足上述条件,那么这种特殊的线性表就会呈现出一种"后进先出"的逻辑,这种逻辑就被称为栈,栈在生活中到处可见,比如堆叠的盘子、电梯中的人等等。
由于约定了只能在线性表固定的一端进行操作,于是给栈这种特殊的线性表的"插入"、“删除”,另起了下面这些特殊的名称:
-
栈顶 : 可以进行插入删除的一端
-
栈底:栈顶的对端
-
入栈: 将节点插入栈顶之上,也称为压栈,函数名通常为push()
-
出栈:将节点从栈顶剔除,也称为弹栈,函数名通常额外pop()
-
取栈顶: 取得栈顶元素,但不出栈,函数名通常为top()
基于这种固定一端操作的简单约定,栈获得了"后进先出"的基本特征,如图所示,最后一个放入的元素,最先被拿出来:
### 2.存储方式
栈只是一种数据逻辑,如何将数据存储于内存则是另外一回事,一般而言,可以采用顺序存储形成顺序栈,或者采用链式存储形成链式栈。
1.顺序栈
顺序存储意味着开辟一块连续的内存用于存储数据节点,一般而言,管理栈数据除了需要一块连续的内存之外,还需要记录栈的总容量、当前栈的元素个数、当前栈顶元素位置,如果有多线程还需要配合互斥锁和信号量等信息,为了方便管理,通常将这些信息统一于一个管理结构体中;
```c
struct seqStack
{
datatype *data; // 顺序栈入口
int size; // 顺序栈总容量
int top; // 顺序栈栈顶元素下标
};
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/3526b25e2861413480bc948d0c7e7b78.png)
2.链式栈
链式栈的组织形式与链表无异,只不过插入删除被约束在固定的一端。为了便于操作,通常也会创建所谓管理结构体,用来存储栈顶指针、栈元素个数等信息:
```c
// 链式栈节点
typedef struct node
{
datatype data;
struct node *next;
}node;
// 链式栈管理结构体
struct linkStack
{
node *top; // 链式栈栈顶指针
int size; // 链式栈当前元素个数
};
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/43a3c29b892c41e18679bc8c71d40ceb.png)
3.基本操作
不管是顺序栈,链式栈,栈的操作逻辑都是一样的,但由于存储形式不同,代码的实现是不同的。下面分别将顺序栈和链式栈的基本核心操作罗列出来:
1.顺序栈
// 顺序栈管理结构体
typedef struct
{
datatype *data; // 顺序栈入口
int size; // 顺序栈总容量
int top; // 顺序栈栈顶元素下标
}seqStack;
// 初始化空栈
seqStack *initStack(int size)
{
seqStack *s = (seqStack *)malloc(sizeof(seqStack))
if(s != NULL)
{
s->data = (datatype *)malloc(sizeof(datatype) * size));
if(s->data == NULL)
{
free(s);
return NULL;
}
s->size = size;
s->top = -1;
}
return s;
}
// 判断栈是否已满
bool isFull(seqStack *s)
{
return s->top == s->size-1;
}
// 判断栈是否为空
bool isEmpty(seqStack *s)
{
return s->top == -1;
}
// 入栈
bool push(seqStack *s, datatype data)
{
if(isFull(s))
return false;
s->data[++s->top] = data;
return true;
}
// 出栈
bool pop(seqStack *s, datatype *pm)
{
if(top(s, pm) == false)
return false;
s->top--;
return true;
}
// 取栈顶元素
bool top(seqStack *s, datatype *pm)
{
if(isEmpty(s))
return false;
*pm = s->data[s->top];
return true;
}
「题目1」
使用顺序栈,接收键盘的输入,实现如下功能:
-
输入数字时,依次入栈。
-
输入字母时,依次出栈。
-
每次入栈或者出栈,都将顺序栈中的各个元素输出出来。
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> typedef int dataType; // 声明栈管理结构体 typedef struct seqStack { dataType *data; // 指向数组首元素地址 int size; // 数组空间 int top; // 栈顶,数组下标 }seqStack; // 初始化顺序栈 struct seqStack *init_seqStack(int cap) { seqStack *st = malloc(sizeof(seqStack)); if(st == NULL) return NULL; st->data = calloc(cap,sizeof(dataType)); if(st->data == NULL) return NULL; st->size = cap; st->top = -1;// 栈顶,其实就是数组的下标 return st; } // 判断栈是否满 bool isFull(seqStack *st) { if(st->top == st->size-1) return true; return false; } // 入栈 bool push(seqStack *st, dataType data) { if(isFull(st)) return false; st->data[++st->top] = data; return true; } // 判断栈是否空 bool isEmpty(seqStack *st) { return st->top == -1; } // 去栈顶元素不出栈 bool top(seqStack *st, dataType *data) { if(isEmpty(st)) return false; // 取栈顶元素 *data = st->data[st->top]; return true; } // 出栈 bool pop(seqStack *st, dataType *data) { // 访问栈顶元素并取出,如果无法访问表示栈顶没有元素可取 if(!top(st,data)) return false; st->top--; return true; } // 显示 void show(seqStack *st) { if(isEmpty(st)) { printf("空栈\n"); return; } for(int i = st->top; i >= 0; i--) { printf(" %d",st->data[i]); if(i == st->top) { printf("<---栈顶"); printf("\n"); } } printf("-----------------\n"); printf("一共有%d个元素\n",st->top+1); } int main(int argc, char const *argv[]) { // 初始化栈 seqStack *st = init_seqStack(5); if(st == NULL) { printf("init stack failed:\n"); return -1; } while(1) { dataType data; if(1 == scanf("%d",&data)) // 输入整数 { if(false == push(st,data)) { printf("栈满,无法入栈\n"); continue; } } else // 输入字母 { //printf("%d\n",__LINE__); // 清空输入缓冲区 while(getchar() != '\n'); // 出栈 if(!pop(st,&data)) { printf("栈空,无法出栈\n"); break; } } show(st); } return 0; }
2.链式栈
// 链式栈节点
typedef struct node
{
datatype data;
struct node *next;
}node;
// 链式栈管理结构体
typedef struct linkStack
{
node *top; // 链式栈栈顶指针
int size; // 链式栈当前元素个数
}linkStack;
// 初始化空栈
linkStack * initStack(void)
{
linkStack * s = (linkStack *)malloc(sizeof(linkStack));
if(s != NULL)
{
s->top = NULL;
s->size = 0;
}
return s;
}
// 判断栈是否为空
bool isEmpty(linkStack *s)
{
return (top->size == 0);
}
// 入栈
bool push(linkStack *s, datatype data)
{
// 创建链表节点
node *new = (node *)malloc(sizeof(node));
if(new == NULL)
return false;
new->data = data;
// 将节点置入栈顶
new->next = s->top;
s->top = new;
// 更新栈元素个数
s->size++;
return true;
}
// 出栈
bool pop(linkStack *s, datatype *pm)
{
if(isEmpty(s))
return false;
linkStack *tmp = s->top;
// 将原栈顶元素剔除出栈
s->top = tmp->next;
tmp->next = NULL;
// 返回栈顶元素,并释放节点
*pm = tmp->data;
free(tmp);
return true;
}
// 取栈顶元素
bool top(linkStack *s, datatype *pm)
{
if(isEmpty(s))
return false;
// 返回栈顶元素,并释放节点
*pm = s->top->data;
return true;
}
「题目」
-
使用链式栈,实现十进制转八进制:键盘输入一个十进制数,经过链式栈的相关算法,输出八进制数。