栈的定义
栈(stack)是一种特殊的线性表,其操作受限于表的一端,通常称为栈顶。在这个端点,线性表元素可以添加(也称为入栈或压栈)和删除(也称为出栈或退栈),并且这种操作具有后进先出(LIFO)的特性。相对的另一端则被称为栈底。
栈的优缺点
优点:
存取速度快:栈的存取速度仅次于计算机里的寄存器,比堆要快。
数据共享:栈中的数据是可以共享的。
操作简单:栈的操作非常简单,只需要对栈顶进行操作,效率较高。同时,栈可以非常方便地实现递归操作,这在程序设计中很关键。
缺点:
数据大小及生存期限制:存在栈中的数据的大小以及生存期必须是确定的,缺乏一些灵活性,所以栈中主要用来存储一些基本数据类型的变量,比如:int,long,double,float,char以及指针等。
栈的基本运算
1.初始化栈
2.入栈
3.出栈
4.取出栈顶元素
5.栈是否为空
6.栈是否已满
7.销毁栈
8.遍历栈
9.栈的应用
10.栈的容量查询
11.帮助
栈的函数封装
typedef int DataType;
typedef struct{
//堆空间
DataType *data;
//栈容量
int maxSize;
//栈顶指针
int top;
} SeqStack;
//初始化栈
int initStack(SeqStack *S,int maxSize);
// 进栈
int push(SeqStack *S, DataType value);
//出栈
int pop(SeqStack *S,DataType *value);
//取出栈顶元素
int getTop(SeqStack *S,DataType *value);
//判断栈是否为null
int stackIsEmpty(SeqStack *S);
//判断栈是否已满
int full(SeqStack *S);
//遍历栈元素
int printStack(SeqStack *S);
//销毁栈
int destroy(SeqStack *S);
//计算二进制
int getBinary(int num);
//获取栈元素的个数
int getStackNum(SeqStack *S);
//后缀表达式
int expression();
1.初始化栈
在实现栈的过程中,首先得给栈申请一块内存空间,我们申请一块动态的内存空间手,动输入空间。使用C语言自带的malloc函数申请,再次给top指针赋初始值(不是C语言的指针),栈容量赋初始值
代码实现
int initStack(SeqStack *S,int maxSize){
//申请内存空间
S->data=(DataType*)malloc(sizeof(DataType)*maxSize);
if(!S){
printf("初始化失败,错误[10001]\n");
return 10001;
}
S->maxSize=maxSize;
S->top=-1;
return 0;
}
2.入栈
入栈的基本操作是往栈的底部进入数据先进后出的顺序,可以抽象成一个羽毛球桶,第一个塞进去去的羽毛球都是最后一个取出来
代码实现
int push(SeqStack *S, DataType value){
//判断栈是否已满
if(full(S)){
printf("栈已满,请不要再次放元素 [10002]\n");
return 10002;
}
S->top++;
S->data[S->top]=value;
return 0;
}
先判断栈是否已满(这个后面在解释怎么实现的),满了也可以动态申请内存空间,我这里是直接报错,之后top指针加一,元素也赋值给top指针下标的位置,具体实现类似于链表的头插法
3.出栈
出栈逻辑类似于:栈通常由一个动态数组实现,该数组有一个固定的大小。当一个元素被推入栈时,它被添加到数组的末尾。当一个元素从栈中弹出时,它被从数组的末尾删除。比如我这里依次入栈 222 333 444 ,最后出栈是 444
代码实现
int pop(SeqStack *S,DataType *value){
//先判断栈是否为null
if(stackIsEmpty(S)){
printf("栈为null,啥也不出来 [10003]\n");
return 10003;
}
*value=S->data[S->top];
S->top--;
return 0;
}
逻辑上是先判断栈是否为空(这里后面在解释) 之后再把出栈的值赋值给value,top指针-1
4.取出栈顶元素
这个比较简单直接使用top指针指针做下标查询,逻辑跟出栈类似,少了top--
代码实现
int getTop(SeqStack *S,DataType *value){
if(stackIsEmpty(S)){
printf("栈为null,无法往栈顶取出元素 [10004]\n");
return 10004;
}
*value=S->data[S->top];
return 0;
}
5.判断栈是否为空
这个也比较简单直接比较栈里面的top指针是否等于-1如果等于-1那就是空栈
代码实现
//判断栈是否为空
int stackIsEmpty(SeqStack *S){
return S->top==-1?1:0;
}
6.栈是否已满
这个实现逻辑跟判断空一样 栈的top指针是否等于栈的容量-1,如果相等就满了
因为之前插入了222,333,444数据,栈初始化给了20的容量,所以就还有17容量,容量是
栈的maxSize-栈的个数(第10 解释栈的元素个数)
代码实现
//判断栈是否已满
int full(SeqStack *S){
return (S->top==S->maxSize-1)?1:0;
}
//这段代码是执行的条件
case 6:
if(full(&S)){
SetConsoleTextAttribute(hConsole, 0xC);
printf("栈已满\n");
}else{
SetConsoleTextAttribute(hConsole, 0xA);
int stackNum=S.maxSize;
printf("栈还有%d的容量\n",(stackNum-getStackNum(&S)));
}
break;
7.销毁栈
销毁栈实际上是销毁栈里面的数据元素,通过调用C语言自带的free函数来释放内存。因为我之前入栈3个元素 222,333,444
代码实现
//销毁栈
int destroy(SeqStack *S){
free(S->data);
S->top=-1;
return 0;
}
//这里是执行代码的条件
case 7:
printf("是否要销毁栈(y是n取消)\n:");
scanf("%s",&yn);
if(yn=='y' || yn=='Y'){
int stackNum=getStackNum(&S);
if(destroy(&S)==0){
SetConsoleTextAttribute(hConsole, 0xC);
printf("成功销毁栈和%d个元素\n",stackNum);
}
}
break;
8.遍历栈
遍历栈通过栈的top指针作为循环条件,当i<=top指针时循环结束。我这里先判断栈是否为null,
第二张是添加 222,333,444,555,666,777,888数据遍历,因为栈是先进后出的顺序,我这里是先进先出的遍历。
代码实现
//遍历栈
int printStack(SeqStack *S){
if(stackIsEmpty(S)){
printf("栈为null,无法遍历元素 [10005]\n");
return 10005;
}
int i;
printf("元素是先进后出的顺序\n");
for(i=0;i<=S->top;i++){
printf("第%d个元素是%d\n",i+1,S->data[i]);
}
return 0;
}
9.栈的应用
当然写这个栈有什么用呢?当然有用了,可以求二进制,也可以求后缀表达式。
1.使用栈求二进制
你只需要将二进制数的每一位压入栈中,然后通过连续的pop操作获取每一位的值。由于栈是后进先出的数据结构,你弹出的顺序将会是二进制数的逆序。因此,你可以通过将弹出的每一位的值乘以2的相应次方并将它们相加来得到十进制数。
代码实现
//计算二进制
int getBinary(int num){
SeqStack S;
int b;
/*初始化栈*/
initStack(&S,32);
/*num不为0,余数进栈*/
while(num)
{
push(&S, num % 2);
num /= 2;
}
/*依次出栈*/
while(!stackIsEmpty(&S))
{
pop(&S,&b);
printf("%d", b);
}
/*销毁栈*/
destroy(&S);
}
2.使用栈求后缀表达式
首先先创建一个空栈,用于存储操作数和运算符。从左到右遍历后缀表达式中的每个元素 a. 如果当前元素是操作数,将其压入栈中。b. 如果当前元素是运算符,则从栈顶取出两个操作 数,按照运算符进行计算,将计算结果压入栈中。遍历结束后,栈顶的元素即为后缀表达式的结果。
代码实现
//后缀表达式
int expression(){
SeqStack S;
int i;
int op1, op2;
int x;
char exp[20]; /*后缀表达式*/
initStack(&S, 10);
printf("请输入一个后缀表达式:");
scanf("%s", exp);
for(i=0;i<strlen(exp);i++)
{
switch(exp[i])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/*入栈*/
push(&S, exp[i]-48);
break;
case '+':
/*出2个*/
pop(&S, &op1);
pop(&S, &op2);
x = op1 + op2;
push(&S, x);
break;
case '*':
pop(&S, &op1);
pop(&S, &op2);
x = op1 * op2;
push(&S, x);
break;
case '/':
pop(&S, &op1);
pop(&S, &op2);
x = op1 / op2;
push(&S, x);
break;
case '-':
pop(&S, &op1);
pop(&S, &op2);
x = op1 - op2;
push(&S, x);
break;
}
}
pop(&S, &x);
printf("计算结果为:%s = %d\n", exp, x);
destroy(&S);
}
10.获取栈元素个数
实现逻辑很简单,直接返回指针top+1因为指针初始化是从-1开始,因为之前添加了222,333,444,555,666,777,888,所以会显示还有7个元素
代码实现
int getStackNum(SeqStack *S){
return S->top+1;
}
11.帮助
本次作业是实现栈,作者丁俊冉
整体代码
main.c
#include <stdio.h>
#include <stdlib.h>
//图案文件
#include "tree.h"
//栈功能函数文件
#include "seqStack.h"
#include <windows.h>
int main(int argc, char *argv[]) {
//window库的颜色api
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, 0xb);
tree();
SeqStack S;
int cmd;
int num;
DataType x;
int maxsize;
char yn;
do
{
printf("---------顺序栈演示程序-----------\n");
printf(" 1. 初始化\n");
printf(" 2. 入栈\n");
printf(" 3. 出栈\n");
printf(" 4. 取栈顶元素\n");
printf(" 5. 栈是否空?\n");
printf(" 6. 栈是否满?\n");
printf(" 7. 销毁栈\n");
printf(" 8. 遍历栈\n");
printf(" 9. 栈的应用\n");
printf(" 10.获取栈元素的个数\n");
printf(" 11.帮助\n");
printf("请选择(0~9,0退出):");
scanf("%d", &cmd);
switch(cmd)
{
case 1:
printf("请输入栈的最大存储空间(MaxSize):");
scanf("%d", &maxsize);
if(!initStack(&S, maxsize))
{
printf("栈已初始化!\n");
}
break;
case 2:
printf("请输入入栈元素:x=");
scanf("%d", &x);
if(!push(&S, x))
{
printf("元素【%d】已入栈!\n", x);
}
break;
case 3:
printf("确定要出栈(出栈后数据不可恢复,y|n,n)?");
// flushall();
scanf("%s", &yn);
if(yn == 'y' || yn == 'Y')
{
if(!pop(&S, &x))
{
printf("栈顶元素【%d】已出栈!\n", x);
}
}
break;
case 4:
// flushall();
if(getTop(&S,&x)==0){
printf("栈顶元素为:%d\n",x);
}
break;
case 5: if(stackIsEmpty(&S)){
SetConsoleTextAttribute(hConsole, 0xc);
printf("栈为空 10002\n");
}else{
SetConsoleTextAttribute(hConsole, 0xA);
printf("栈还有数据\n");
}
break;
case 6:
if(full(&S)){
SetConsoleTextAttribute(hConsole, 0xC);
printf("栈已满\n");
}else{
SetConsoleTextAttribute(hConsole, 0xA);
int stackNum=S.maxSize;
printf("栈还有%d的容量\n",(stackNum-getStackNum(&S)));
}
break;
case 7:
printf("是否要销毁栈(y是n取消)\n:");
scanf("%s",&yn);
if(yn=='y' || yn=='Y'){
int stackNum=getStackNum(&S);
if(destroy(&S)==0){
SetConsoleTextAttribute(hConsole, 0xC);
printf("成功销毁栈和%d个元素\n",stackNum);
}
}
break;
case 8:printStack(&S);break;
case 9:
do
{
printf("----------8.栈的应用------------\n");
printf(" 1. 十进制转换为二进制\n");
printf(" 2. 后缀表达式计算\n");
printf(" 0. 返回\n");
printf("请选择:");
scanf("%d", &cmd);
if(cmd == 1)
{
printf("请输入一个十进制数:");
scanf("%d", &num);
printf("二进制为:");
getBinary(num);
printf("\n");
}
if(cmd == 2)
{
expression();
}
}while(cmd!=0);
cmd = -1;
break;
case 10 : printf("栈有%d个元素\n",getStackNum(&S)); break;
case 11:printf("本次作业是栈的实现与使用作者丁俊冉\n");break;
}
}while(cmd!=0);
return 0;
}
seqStack.h
typedef int DataType;
typedef struct{
//堆空间
DataType *data;
//栈容量
int maxSize;
//栈顶指针
int top;
} SeqStack;
//初始化栈
int initStack(SeqStack *S,int maxSize);
// 进栈
int push(SeqStack *S, DataType value);
//出栈
int pop(SeqStack *S,DataType *value);
//取出栈顶元素
int getTop(SeqStack *S,DataType *value);
//判断栈是否为null
int stackIsEmpty(SeqStack *S);
//判断栈是否已满
int full(SeqStack *S);
//遍历栈元素
int printStack(SeqStack *S);
//销毁栈
int destroy(SeqStack *S);
//计算二进制
int getBinary(int num);
//获取栈元素的个数
int getStackNum(SeqStack *S);
//后缀表达式
int expression();
seqStack.c
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "seqStack.h"
int getStackNum(SeqStack *S){
return S->top+1;
}
//销毁栈
int destroy(SeqStack *S){
free(S->data);
S->top=-1;
return 0;
}
//后缀表达式
int expression(){
SeqStack S;
int i;
int op1, op2;
int x;
char exp[20]; /*后缀表达式*/
initStack(&S, 10);
printf("请输入一个后缀表达式:");
scanf("%s", exp);
for(i=0;i<strlen(exp);i++)
{
switch(exp[i])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
/*入栈*/
push(&S, exp[i]-48);
break;
case '+':
/*出2个*/
pop(&S, &op1);
pop(&S, &op2);
x = op1 + op2;
push(&S, x);
break;
case '*':
pop(&S, &op1);
pop(&S, &op2);
x = op1 * op2;
push(&S, x);
break;
case '/':
pop(&S, &op1);
pop(&S, &op2);
x = op1 / op2;
push(&S, x);
break;
case '-':
pop(&S, &op1);
pop(&S, &op2);
x = op1 - op2;
push(&S, x);
break;
}
}
pop(&S, &x);
printf("计算结果为:%s = %d\n", exp, x);
destroy(&S);
}
//计算二进制
int getBinary(int num){
SeqStack S;
int b;
/*初始化栈*/
initStack(&S,32);
/*num不为0,余数进栈*/
while(num)
{
push(&S, num % 2);
num /= 2;
}
/*依次出栈*/
while(!stackIsEmpty(&S))
{
pop(&S,&b);
printf("%d", b);
}
/*销毁栈*/
destroy(&S);
}
//初始化栈
int initStack(SeqStack *S,int maxSize){
//申请内存空间
S->data=(DataType*)malloc(sizeof(DataType)*maxSize);
if(!S){
printf("初始化失败,错误[10001]\n");
return 10001;
}
S->maxSize=maxSize;
S->top=-1;
return 0;
}
//进栈
int push(SeqStack *S, DataType value){
//判断栈是否已满
if(full(S)){
printf("栈已满,请不要再次放元素 [10002]\n");
return 10002;
}
S->top++;
S->data[S->top]=value;
return 0;
}
//出栈
int pop(SeqStack *S,DataType *value){
//先判断栈是否为null
if(stackIsEmpty(S)){
printf("栈为null,啥也不出来 [10003]\n");
return 10003;
}
*value=S->data[S->top];
S->top--;
return 0;
}
//取出栈顶元素
int getTop(SeqStack *S,DataType *value){
if(stackIsEmpty(S)){
printf("栈为null,无法往栈顶取出元素 [10004]\n");
return 10004;
}
*value=S->data[S->top];
return 0;
}
//遍历栈
int printStack(SeqStack *S){
if(stackIsEmpty(S)){
printf("栈为null,无法遍历元素 [10005]\n");
return 10005;
}
int i;
printf("元素是先进后出的顺序\n");
for(i=0;i<=S->top;i++){
printf("第%d个元素是%d\n",i+1,S->data[i]);
}
return 0;
}
//判断栈是否已满
int full(SeqStack *S){
return (S->top==S->maxSize-1)?1:0;
}
//判断栈是否为空
int stackIsEmpty(SeqStack *S){
return S->top==-1?1:0;
}
tree.h
int tree(){
int i, j, n, b, s;
printf("请输入树的层数:");
scanf("%d", &n);
for (i = 1; i <= n; i++)
{
for (j = 1; j <= i + 1; j++)
{
for (s = 1; s <= n + 1 - j; s++)
{
printf(" ");
}
for (s = 1; s <= 2 * j - 1; s++)
{
if(s==1){
printf("丁");
}else if(s==2){
printf("俊");
}else{
printf("冉");
}
}
printf("\n");
}
}
for (b = 1; b <= n * 2; b++)
for (s = 1; s <= n * 2; s++)
{
if (s == n * 2)
{
printf("|三|\n");
continue;
}
printf(" ");
}
printf("\n");
}
小结
栈是一种限定仅在一端(通常是表尾)进行插入和删除操作的线性表。它是一种特殊的线性表,其逻辑结构与线性表相同,仍为一对一的关系。栈的基本操作有Push(进栈)和Pop(出栈),前者相当于插入元素,后者则是删除最后插入的元素。
总之,栈是一种具有特定操作方式和用途的数据结构,在计算机科学中有着广泛的应用。
参考文献
百度:文心一言
B站