今天看了思成“栈”的视频,花了1个半小时研究了下课后习题,感触颇深,来跟大家做个分享:
首先是栈的结构:
栈包括一个栈底指针、一个栈顶指针和它现在可容纳数据的大小。
当然为了通用性,这里通过类型定义ElemType可以为任意型。
stack.h
//条件定义,避免相同头文件重复导入
#ifndef _STACK_H
#define _STACK_H
#include"data.h"
#define STACK_INIT_SIZE 10
#define STACK_INCREME 10
typedef struct
{
ElemType * base;
ElemType * top;
int size;
}STACK;
STACK * InitStack();
void DestroyStack(STACK *s);
int Push(STACK *s,ElemType *e);
int Pop(STACK *s,ElemType *e);
int IsEmpty(STACK *s);
#endif
接着是入栈(Push)和出栈(Pop)操作
栈底指向整个栈首地址,栈顶指向的是现有数据区域下一块空间的首地址.
当需要入栈(Push)时,先判断栈是否已满?如果已满,则申请新的内存.
接着将数据元素放到栈顶指向的内存空间中,栈顶指针随之上移一格,指向下一个空区域的首地址
出栈(Pop)时,先判断栈是否为空,若非空,则让栈顶指针向下走一格,取出元素即可.
stack.c
#include<stdio.h>
#include<stdlib.h>
#include"stack.h"
STACK * InitStack()
{
STACK *s = (STACK *)malloc(sizeof(STACK));
if(s == NULL)
exit(0);
s->base = (ElemType *)malloc(STACK_INIT_SIZE *sizeof(ElemType));
if(s->base == NULL) exit(0);
s->top = s->base;
s->size = STACK_INIT_SIZE;
return s;
}
void DestroyStack(STACK *s)
{
free(s->base);
free(s);
}
int Push(STACK *s,ElemType *e)
{
if(s == NULL || e == NULL)
return 0;
if(s->top - s->base >= s->size)
{
s->base = (ElemType *)realloc(s->base,
(s->size + STACK_INCREME)*sizeof(ElemType));
if(s->base == NULL)
return 0;
s->top = s->base + s->size;
s->size = s->size + STACK_INCREME;
}
*s->top++ = *e;
return 1;
}
int Pop(STACK *s,ElemType *e)
{
if(s == NULL || e == NULL)
return 0;
if(s->base == s->top) return 0;
*e = *--s->top;
return 1;
}
int IsEmpty(STACK *s)
{
return s->top == s->base ? 1 : 0;
}
为了利用栈,我们最好再定义一个头文件,里面确定一下栈里存放的数据类型
data.h
#ifndef _DATA_H
#define _DATA_H
//typedef int ElemType;
typedef char ElemType;
#endif
然后就是思成的例子:
可以看到要存储的是整形,所以上面data.h头文件中有ElemType定义为int
存储的是余数,然后倒序输出,所以要用到栈的LIFO结构.
main.c
#include<stdio.h>
#include<stdlib.h>
#include"stack.h"
void main()
{
int num = 1348,temp;
STACK *s = InitStack();
while(num)
{
temp = num % 8;
Push(s,&temp);
num /= 8;
}
printf("result is");
while(!IsEmpty(s))
{
Pop(s,&temp);
printf("%d",temp);
}
DestroyStack(s);
}
作业:
代码:
testStack.c
#include<stdio.h>
#include<stdlib.h>
#include"stack.h"
double calculate(double left,char opt,double right){
switch(opt){
case '+':
return left + right;
case '-':
return left - right;
case '*':
return left * right;
case '/':
return left / right;
}
}
void main(){
char str[20],opt;
double result[10],left=0,right=0;
int i,flag = 0,level=0;
STACK *s = InitStack();
gets(str);
for(i=0;str[i];i++){
if(str[i] == '+'||str[i] == '-'||str[i] == '*'||str[i] == '/'){
if(flag){
Pop(s,&opt);
result[level] = left = calculate(left,opt,right);
right = 0;
}
flag = 1;
Push(s,&str[i]);
}else if(str[i] == '('){
result[level++] = left;
flag = left = right = 0;
}else if(str[i] == ')'){
Pop(s,&opt);
result[level] = right = calculate(left,opt,right);
level --;
left = result[level];
flag = 1;
}else if(str[i] == '='){
Pop(s,&opt);
result[level] = calculate(left,opt,right);
}else{
if(!flag){
left *= 10;
left += str[i] - 48;
}else{
right *= 10;
right += str[i] - 48;
}
}
}
printf("%s%lf",str,result[0]);
DestroyStack(s);
}
测试画面:
我的思路:
首先,输入一行,遍历每个字符,计算算式的值。
当碰到运算符时,说明左值存在,就让运算符入栈,等待右值输入。
当碰到运算符或者=时,如果标记(flag为真)说明左值和栈中运算符已经就位,我们需要做的就只是拿出运算符让左值和右值做以此运算即可。然后获得的值继续作为下一个左值。
括号的引入我们可以简单的把它理解成在一个新的层上面进行同样的操作,我们需关心的只是如何在层之间切换。
当遇到(时,首先将先前算出来的左值保存起来,然后对对层进行加操作,进入新的一层。注意,此时flag标记(识别是否有左值),左值、右值都要返回初始状态0.
当遇到)时,只要计算括号内的算式的值保存为右值,同时恢复前一层保存的左值,并且置flag标志为1,并对层做减操作,这样就切换回了上一层。
最后说下,数字位数就直接用前值后面加0然后加上读入的数搞定。
收获在于思考和自己的练习哈,几天没做ACM也生疏了不少,希望以后能多练练。。呵呵