使用C语言实现栈数据结构以及栈的应用

栈的定义

       栈(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站

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值