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;
}
*/