何为栈?
栈(Stack):就是只允许在一端(限定只在表尾)进行插入或删除操作的线性表。
所以,对于栈而言,是有所区分栈顶(top)和栈底(bottom) 的。具体就如下图:
所以,在使用栈的存储时,是存在先进后出的一个规律。
当我们将元素依次存储入栈后,最先进栈储存的元素在取出(出栈)时,是需要等到后入栈压在其上的元素被全部取出后,才能出栈。
栈的状态
一个栈它是有着四种不同的状态
- 空栈
- 满栈
- 入栈(进栈、压栈push)
- 出栈 (弹栈pop)
这也就对应着最基础的几个栈的操作API。
ADT设计
对于栈的程序设计,我们主要编写这几个对栈的操作
栈方法名 | 操作 |
---|---|
initStack(*S) | 初始化栈,建立一个空栈 |
destoryStack(*S) | 若栈S存在,销毁栈 |
is_Full(S) | 栈是否已满 |
clearStack(*S) | 将栈中元素清空 |
getTop(S,*e) | 若栈S非空,用e获取栈顶元素 |
push(*S,e) | 若栈S存在,将元素e压入到栈中 |
pop(*S,*e) | 删除栈S的栈顶元素,并用e返回被删除的值 |
stackLength(S) | 得到栈的长度 |
C语言实现两种类型栈
顺序栈
顺序栈是利用连续的内存空间(数组)进行存储栈内元素,并实现栈的功能特性。
使用顺序栈的优点:
- 构造简单,存储地址连续,便于操作
缺点:
- 内存空间固定,可拓展性差
- 且需要占用整块内存
代码实现
# include<stdio.h>
# include<stdlib.h>
# define MaxSize 50 // 定义栈的深度
typedef int ElemType; // 定义栈储元素的类型
// 结构体构造栈的结构
typedef struct {
ElemType data[MaxSize]; // 构造存储的栈内存结构
int top; // 定义栈顶指针
} SeqStack;
// 初始化栈
SeqStack* initStack(SeqStack* s){
// 上面已经使用结构体构造了栈的结构
// 这里要给定义好的栈结构进行内存的开辟
s = (SeqStack*)malloc(sizeof(SeqStack));
// 将栈顶指针设置到栈底
// 这里是按照数组进行储存,所以栈首是从0开始的
// 所以这里的初始化,要设置为-1
s->top = -1;
return s;
}
// 判断栈是否为空
int is_Full(s){
if(s->top == MaxSize-1){
return 0;
}
return -1;
}
// 入栈
void push(SeqStack* s, Elemtype e){
if(is_Empty){
printf("Stack is Full\n");
}else{
s->top ++;
s->data[s->top] = e;
}
}
// 出栈
void pop(SeqStack* s){
int fetchElem;
//若栈内无元素
if(s->top == -1){
printf("Stack is Empty\n");
}else{
fetchElem = s->data[s->top];
s->top --;
printf("%d\n",fetchElem);
}
}
// 获取栈顶元素
// 这里是进行查看栈顶元素,而非出栈
void getTop(SeqStack s, Elemtyep e){
// 若栈为空
if(s->top == -1){
printf("Stack is Empty\n");
}else{
e = s->data[s->top];
printf("%d\n", e);
}
}
// 获取栈的长度
void stackLength(SeqStack s){
int length;
length = s->top + 1;
printf("%d\n", length);
}
非连续栈
非连续栈是指在栈的结构构造上,不依赖于固定连续的内存空间,而是使用指针将分散的栈储元素进行连接,实现栈的功能。
非连续栈的优点:
- 存储的元素分散,不限制大小,可任意拓宽深度
- 不需要使用整块内存
缺点:
- 元素间使用指针连接,操作复杂
代码实现
# include<stdio.h>
# include<stdlib.h>
typedef int ElemType;
// 定义单个栈储节点
typedef struct Node {
// 定义数据域,用于存放栈储元素
int data;
// 定义指针域,指向下一个栈储元素
struct Node* next;
} LinkStack;
// 初始化栈
void initStack(LinkStack* s){
// 一次先开辟一个节点的内存空间
s = (LinkStack*)malloc(sizeof(LinkStack));
// 将指针清空
s->Next = NULL;
}
// 判断是否为空
int is_Empty(LinkStack* s){
return(s->next == NULL)
}
// 销毁栈
void destroyStack(LinkStack* s){
Node *h = s;
// 先遍历到栈的最后一个栈储空间
// 然后释放掉,再从头遍历到最后一个人栈储空间
while(s != NULL){
s = s->next;
free(p);
p = s;
}
}
// 入栈
void push(LinkStack* s, ElemType e){
LinkStack* p;
p = (LinkStack*)malloc(sizeof(LinkStack));
p->data = e;
// 将待压栈栈储的指针指向首栈元素
p->next = s->next;
// 再使首栈元素为待插入的元素
s->next = p;
}
// 出栈
void pop(LinkStack* s){
// 判断栈是否为空
if(s->next == NULL){
printf("Stack is Empty\n");
}else{
// 暂存待删首栈储
LinkStack* temp = s->next;
// 使首栈为原首栈的下一栈
s->next = temp->next;
// 释放原首栈的存储空间
free(temp);
}
}
相关经典题目
关于数据结构的栈在PTA中有一些经典的习题,这里将进行解决
将十进制转换为二进制,使用初中学习的短除法,进行循环取2余后,在从底往上书写结果
,这一过程恰好是符合我们栈的FILO的原则。所以如下:
#include <stdio.h>
#include <stdlib.h>
#define M 100
typedef int ElemType;
typedef struct
{
ElemType data[M];
int top;
}Stack;
//初始化栈
void InitStack(Stack *s)
{
s->top = -1;
}
int Push(Stack *s,ElemType e)
{
if (s->top == M-1)
{
printf("栈满\n");
return 0;
}
s->top++;
s->data[s->top]=e;
return 1;
}
//判断是否为空
int Empty(Stack *s)
{
return(s->top==-1);
}
//出栈
int Pop(Stack *s,ElemType *e)
{
if(Empty(s))
{
printf("\n Stack is free");
return 0;
}
*e=s->data[s->top];
s->top--;
return 1;
}
void Conversion(int N)
{
int e;
Stack *s = (Stack *)malloc(sizeof(Stack));
InitStack(s);
while(N)
{
Push(s,N%2);
N=N/2;
}
while(!Empty(s))
{
Pop(s,&e);
printf("%d",e);
}
}
int main()
{
int m;
scanf("%d",&m);
Conversion(m);
}