数据结构--栈(C语言代码模板及其应用)

1、什么是栈

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
栈作为一种数据结构,是一种只能在一端进行插入和删除操作的特殊线性表。它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。
栈是允许在同一端进行插入和删除操作的特殊线性表。允许进行插入和删除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈。插入一般称为进栈(PUSH),删除则称为退栈(POP)。栈也称为先进后出表。
栈的模型
理论来源于百度百科!嘻嘻嘻(总之栈的特点就是先进后出,底端是封闭的只能从上面出去和进来—理解为一个有很多隔层的桶吧!)

2、c语言代码模板–指针栈

栈主要有栈的初始化返回栈顶元素压栈出栈判断是否为空以及销毁栈这几个基本功能板块。
上代码(根据需求改变返回类型,可以自行在功能板块中增加输出语句,用来判断执行情况(比如:是否进栈等等)):

 
/*链表栈*/
#include<stdio.h>
#include<cstdlib>
#define INIT_SIZE 100 //初始化容量
#define ADD_SIZE 10//每次增加的容量 
#define type int 
using namespace std;
typedef struct {
	type *top;//栈顶 
	type *basic; //栈基
	int size;//容量 
}stack;//构造栈的结构体
int INIT_stack(stack *s);//初始化
int Gettop(stack *s);//返回栈顶元素
void in_stack(stack *s, int n);//压栈
int pop(stack *s);//出栈 
int StackEmpty(stack *s) ;//判断是否为空
int Destory(stack *s);

/*int main()
{
	stack *s;
	s = (stack *)malloc(sizeof(stack));
	INIT_stack(s);
	int n, t;
	scanf("%d", &n);
	while (n > 0)
	{
		in_stack(s, n);
		scanf("%d", &n);
	}
	printf("栈顶元素%d\n", Gettop(s));
	printf("出栈\n");
	while ((t = pop(s))>0)
	{
		printf("%d\n", t);
	}
	Destory(s);
	return 0;
}*/

int INIT_stack(stack *s)//初始化 
{
	s->top = (type *)malloc(sizeof(type)*INIT_SIZE);//分配空间
	if (!s->top) exit(1);
	s->basic = s->top;
	s->size = INIT_SIZE;
	return 1; 
}
//返回栈顶元素
int Gettop(stack *s)
{
	if (s->top == s->basic)
		return 0;
	int e = *(s->top - 1);
	return e;
}
//压栈
void in_stack(stack *s, int n)
{
	if (s->top - s->basic >= s->size)
	{
		type *newbasic = (type *)realloc(s->basic, (s->size + ADD_SIZE) * sizeof(type));
		if (!newbasic)
			exit(1);//exit(1)表示异常退出,在退出前可以给出一些提示信息,或在调试程序中察看出错原因exit(0);//exit(0)表示正常退出
		s->basic = newbasic;
		s->top = s->basic + s->size;
		s->size += ADD_SIZE;
	}
	*s->top++ = n;
}
//出栈
int pop(stack *s)
{
	if (s->basic == s->top)
		 return  -1;
	int e = *--s->top;
	return e;
}
int StackEmpty(stack *s) //为空返回1,否则返回0
{
	if (s->top == s->base)
		return 1;
	else
		return 0;
}
 int Destory(stack *s)
 {
 	free(stack->base);
	stack->stacksize=0;
	printf("销毁栈成功\n");
	return 0;  
  } 

3、栈应用(1)–十进制转八进制

这里应用的方法为辗转相除法(转十六进制等都一样)。
图解详情
即,将求解数m的余数(1)t=m%n压入栈中,
再执行(2)m=m/n,
然后再执行(1),
直到m=0时退出,再将栈中元素弹出来(满足一个先进后出的一个规律)!
直接上代码:

//十进制转换八进制 
 #include<stdio.h>
 #include<stdlib.h>
 #define  INIT_SIZE 100
 #define ADD_SIZE 10 
 typedef struct{
 	int *top;
	int *basic; 
 	int size; 
 }stack; 
 void init_stack(stack *p)
 {
 	p->top=(int *)malloc(sizeof(int)*INIT_SIZE);
 	if(!p->top) exit(1); 
	p->basic=p->top;
	p->size=INIT_SIZE; 
 } 
 //压栈
 void in_stack(stack *p,int n)
 {
 	if(p->top-p->basic>=p->size)
	 {
	 	int *newbasic=(int *)malloc(sizeof(int)*(INIT_SIZE+ADD_SIZE));
		 p->basic=newbasic;
		 p->top=p->basic+p->size;
		 p->size+=ADD_SIZE; 
	  } 
  	*p->top++=n;
 } 
 //出栈
 int pop(stack *p)
 {
 	if(p->top==p->basic)
 	return -1;
	int e=*--p->top;
	return e;   
 } 
 int main()
 {
 	stack *p;
 	int n; 
	p=(stack *)malloc(sizeof(stack));
	init_stack(p);
	printf("请输入需要转换成八进制的十进制:\n");
	scanf("%d",&n);
	while(n)
	{
		in_stack(p,n%8);
		n/=8; 
	} 	
	int t; 
 	while((t=pop(p))!=-1)
	 {
		printf("%d\t",t); 
	 } 
 	return 0; 
  } 

4、栈应用(2)–括号匹配的检验

  • 流程

图片描述
上代码:

  • 代码中读取字符串使用的是fgets因为gcc中 gets puts函数不能用!! stdin 键盘输入(fgets会读取回车符).

函数原型
char *fgets(char *str, int n, FILE *stream);
参数 str-- 这是指向一个字符数组的指针,该数组存储了要读取的字符串。
n-- 这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str 传递的数组长度。
stream-- 这是指向 FILE 对象的指针,该 FILE 对象标识了要从中读取字符的流。

注意:请输入英文字符,不然一个中文括号会占两个字节

//
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#define Stack_Init_Size 100
#define Stack_Increment 10//increment 增量 

//栈的定义
typedef struct {
	char *base;
	char *top;
	int stacksize;
}sqstack;
//初始化栈
int INIT(sqstack *stack)
{
	stack->base = NULL;
	if (!stack->base)
	{
		stack->base = (char *)malloc(Stack_Init_Size * sizeof(char));
		stack->top = stack->base;
		stack->stacksize = Stack_Init_Size;
		printf("初始化成功\n");
		return 0;
	}
	else return -1;//表示无法初始化已初始化栈 
}
//获取栈顶数据
char Gettop(sqstack *stack)
{
	if (stack->base == stack->top)
	{
		printf("栈中无数据\n");
		return '#';
	}
	printf("获取数据成功\n");
	return *(stack->top - 1);

}
//压栈
int push(sqstack *stack, char ch)
{
	if (stack->top - stack->base >= stack->stacksize)
	{
		stack->base = (char *)realloc(stack->base, Stack_Increment * sizeof(char));
		stack->top = stack->base + stack->stacksize;
		stack->stacksize += Stack_Increment;
	}
	*stack->top++ = ch;
	printf("插入数据成功\n");
	return 0;
}
//删除栈顶元素(出栈) 
char pop(sqstack *stack)
{
	if (stack->base == stack->top)
	{
		printf("栈空\n");
		return '#';
	}
	printf("出栈成功\n");
	return *--stack->top;
}
//释放栈空间
int Destory(sqstack *stack)
{
	free(stack->base);
	stack->stacksize = 0;
	printf("销毁栈成功\n");
	return 0;
}
//数据处理
int ExecuteData(sqstack *stack, char *data)
{
	push(stack, data[0]);
	int i;
	for (i = 1; i < strlen(data)-1; i++)
	{
		char top = Gettop(stack);
		switch (top)
		{
		case '(':
			if (data[i] == ')') pop(stack);
			else push(stack, data[i]);
			break;
		case '[':
			if (data[i] == ']') pop(stack);
			else push(stack, data[i]);
			break;
		case '#':
			if (data[i] == '(' || data[i] == '[')
			{
				push(stack, data[i]);
				break;
			}
		
		return -1; break;
		}
	}

	if (stack->top == stack->base)
	{
		Destory(stack);
		return 0;
	}
	else {
		Destory(stack);
		return -1;
	}
}

int main()
{
	sqstack *stack;
	stack = (sqstack *)malloc(sizeof(sqstack));
	INIT(stack);
	char data[100];
	fgets(data, 100, stdin);  // gcc中 gets puts函数不能用!! stdin 键盘输入.
	//printf("%d\n\n",strlen(data)); 	
	int result = ExecuteData(stack, data);
	if (result == 0)
		printf("匹配正确\n");
	else
		printf("匹配错误\n");
	getchar();
	getchar();
	return 0;
}

5、栈应用(3)–中缀表达式求值

(这些都是俺在网上找的,希望可以帮助大家理解思路)

  • 中缀表达式 :指运算符在两个运算对象之间,比如 23+(34*45)/(5+6+7)
  • 思路详解:
    在这里插入图片描述
  • 流程图:


上代码(输入运算字符串以#结尾)
(我是结合其他人的代码写出来的,最开始虽然会写各个功能板块,但是不太了解优先级之间的运算(可以看看上面图片的思路,看完就基本晓得怎么写了),所以写了好久的没写出来,这个算法不能实现1+(-1)这样的运算比较low,将就将就):

#include<stdio.h>
#include<ctype.h>//判断是否属于特定的字符类别 
/*
isalpha
函数名称: isalpha
函数原型: int isalpha(char ch);
函数功能: 检查ch是否是字母.
函数返回: 是字母返回非0(在vs2015中为2) ,否则返回 0.
isdigit
函数名称: isdigit
函数原型: int isdigit(char ch);
函数功能: 检查ch是否是数字(0-9)
函数返回: 是返回非0,否则返回0.......
*/
#include<stdlib.h>
#include<string.h>
#define INIT_SIZE 100
#define ADD_SIZE 10 
typedef int SelemType;

typedef struct
{
	int *base;
	int *top;
	int size;
}stack_number; //数字栈 

typedef struct
{
	char *base;
	char *top;
	int size;
}stack_char; //运算符栈 

/*初始化栈*/
void INIT_stacknumber(stack_number *s)
{
	s->base = (int *)malloc(sizeof(int)*INIT_SIZE);
	if (!s->base) exit(1);
	s->top = s->base;
	s->size = INIT_SIZE;
}
void INIT_stackChar(stack_char *s)
{
	s->base = (char *)malloc(sizeof(char)*INIT_SIZE);
	if (!s->base) exit(1);
	s->top = s->base;
	s->size = INIT_SIZE;
}
/*数字进栈 */
void push_number(stack_number *L, int n)
{
	if (L->top - L->base >= L->size)
	{
		int *newbasic = (int *)realloc(L->base, (L->size + ADD_SIZE) * sizeof(int));
		if (!newbasic) exit(1);//exit(1)表示异常退出,在退出前可以给出一些提示信息,或在调试程序中察看出错原因exit(0);//exit(0)表示正常退出
		L->base = newbasic;
		L->top = L->base + L->size;
		L->size += ADD_SIZE;
	}
	*L->top++ = n;
}
/*运算符进栈*/
void push_char(stack_char *L, char n)
{
	if (L->top - L->base >= L->size)
	{
		char *newbasic = (char *)realloc(L->base, (L->size + ADD_SIZE) * sizeof(char));
		if (!newbasic) exit(1);
		L->base = newbasic;
		L->top = L->base + L->size;
		L->size += ADD_SIZE;
	}
	*L->top++ = n;
}
/*返回运算符栈顶*/
char gettop(stack_char *L, char *x)
{
	if (L->base == L->top)
		return 0;
	else
		*x = *(L->top - 1);
	return *x;
}
/*返回运算数栈顶*/
int gettop_number(stack_number *L, int *x)
{
	if (L->base == L->top)
		return 0;
	else
		*x = *(L->top - 1);
	return *x;
}

/*数字出栈*/
int pop_number(stack_number *L,int *e)
{
	if (L->base == L->top)
		return -1;
	*e = *--L->top;
	return *e;
}
/*运算符出栈*/
char pop_char(stack_char *L,char *e)
{
	if (L->base == L->top)
		return 0;
	*e= *--L->top;
	return *e;
}
/*判断是否为空栈*/
int StackEmpty(stack_number S) {
	if (S.top == S.base)
		return 1;
	else
		return 0;
}


 /*比较两个运算符的优先级

  *a,b中存放待比较的运算符·

  *'>'表示a>b

  *'0'表示不可能出现的比较

  */
char Precede(char a, char b)
{
	int i=0, j=0;
	char pre[][7] = {
		//运算符之间的优先级制成一张表格 
		 {'>','>','<','<','<','>','>'},
		 {'>','>','<','<','<','>','>'},
		 {'>','>','>','>','<','>','>'},
		 {'>','>','>','>','<','>','>'},
		 {'<','<','<','<','<','=','0'},
		 {'>','>','>','>','0','>','>'},
		 {'<','<','<','<','<','0','='} };
	switch (a)
	{
	case '+': i = 0; break;
	case '-': i = 1; break;
	case '*': i = 2; break;
	case '/': i = 3; break;
	case '(': i = 4; break;
	case ')': i = 5; break;
	case '#': i = 6; break;
	}
	switch (b)
	{
	case '+': j = 0; break;
	case '-': j = 1; break;
	case '*': j = 2; break;
	case '/': j = 3; break;
	case '(': j = 4; break;
	case ')': j = 5; break;
	case '#': j = 6; break;
	}
	return pre[i][j];
}

/*进行实际的运算
 * a,b中分别以整数的形式存放两个待运算的操作数
 * theta中存放代表操作符的字符
 * 结果以整数的形式返回
 */
int operate(int a, char theta, int b)
{

	int result;
	switch (theta)
	{
	case '+': result = a + b; break;
	case '-': result = a - b; break;
	case '*': result = a * b; break;
	case '/': result = a / b; break;
	}
	return result;
}

/*从输入缓冲区中获得下一个整数或运算符,并通过n带回到主调函数
 *返回值为1表示获得的是运算符
 *返回值为0表示获得的是整形操作数
*/
int getnext(int *n)
{
	char c;
	*n = 0;
	while ((c = getchar()) == ' ');//跳过一个或多个空格
	if (!isdigit(c))//不是0-9数字,直接返回运算符 
	{
		*n = c;
		return 1;
	}
	do {
		*n = *n * 10 + (c - '0');
		c = getchar();
	} while (isdigit(c));
	ungetc(c, stdin);//函数ungetc()把字符c放回到stream(流)中.  
	return 0;
}


int EvaluateExpression(stack_number *OPND,stack_char *OPTR) {
	int n;
	int flag;
	int c;
	char x, theta;
	int a, b;
	INIT_stackChar(OPTR);
	push_char(OPTR, '#');//先将#放入运算符栈中 
	INIT_stacknumber(OPND);
	flag = getnext(&c);
	gettop(OPTR, &x);
	while (c != '#' || x != '#')
	{
		if (flag == 0)
		{
			push_number(OPND, c);
			flag = getnext(&c);
		}
		else
		{
			gettop(OPTR, &x);
			switch (Precede(x, (char)c))
			{
				case '<'://栈顶元素优先级低                    
					push_char(OPTR, (char)c);
					flag = getnext(&c);
					break;
				case '='://脱括号并接受下一字符 
					pop_char(OPTR, &x);
					flag = getnext(&c);
					break;
				case '>':// 退栈并将运算结果入栈                                       
						pop_char(OPTR, &theta);
						pop_number(OPND, &b);
						pop_number(OPND, &a);
						push_number(OPND, operate(a, theta, b));
						break;
			}
		}
			gettop(OPTR, &x);
	}
		gettop_number(OPND, &c);
		return c;
	}
		

int main()
{
	stack_number *OPND;
	stack_char *OPTR;
	OPND = (stack_number *)malloc(sizeof(stack_number));
	OPTR = (stack_char *)malloc(sizeof(stack_char));
	printf("%d\n", EvaluateExpression(OPND,OPTR));
	getchar();
	getchar();
	return 0;
}

/*判断输入的某个字符是否是运算符*/
/*int in(char c,char op[])
{
	char *p;
	p=op;
	while(*p!='\0')
	{
		if(c==*p)
		{
			return TRUE;
		}
		p++;
	 }
	 return FLASE;
 }
 */
发布了22 篇原创文章 · 获赞 6 · 访问量 1724

猜你喜欢

转载自blog.csdn.net/qq_44759750/article/details/101545807