20150322_栈
注:参考《大话数据结构》
栈(stack)是限定仅在表尾进行插入和删除操作的线性表
我们把允许插入和删除的一端称为栈顶(top),另一端称为端底(bottom),不含任何数据元素的栈称为空栈。
栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构。
栈的存储结构和实现
1.栈的顺序存储结构
栈是线性表的特例,那么栈的顺序结构其实也是线性表顺序结构的简化,我们简称顺序栈。线性表使用数组实现。
以下标为0的一端作为栈底。
栈的结构定义
typedef int SElemType ;
typedef struct
{
SElemType data[MAXSIZE];
int top; // 栈顶指针,标识栈顶位置
}Sqstack;
进栈
Status push(Sqstack *S,SElemTpye e)
{
if(S->top==MAXSIZE-1)
return ERROR; //栈满,不能进栈
S->top++;
S->data[S->top]=e;
return OK;
}
出栈
Status pop(Sqstack *S,SElemTpye *e)
{
if(S->top==-1)
return ERROR; //栈空
*e=S->data[S->top]; //e出栈使用,通过形参返回
S->top--;
return OK;
}
2.栈的链式存储结构(链栈)
栈只在栈顶做插入和删除。既然使用链表来表示栈,那么考虑到单链表需要头指针,栈顶指针也不可缺少,所以可以把栈顶放在单链表的头部,合二为一。有了栈顶指针,头指针也就不是必须的了。通常,对于链栈来说,是不需要头指针的。
链栈的结构定义
typedef struct stacknode //链表结点
{
SElemType data;
struct stacknode *next;
}stacknode, *linkstackptr;
typedef struct linkstack
{
linkstackptr top; //指向栈顶,不是链表结点
int len;
}linkstack;
进栈
Status push(linkstack *S,SElemType e)
{
linkstackptr p=(linkstackptr)malloc(sizeof(stacknode));
p->data=e;
p->next=S->top;
S->top=p;
S->len++;
return OK;
}
出栈
Status pop(linkstack *S,SElemType *e)
{
linkstackptr p=NULL;
if(S->top==NULL)
return ERROR;
*e=S->top->data;
p=S->top;
S->top=S->top->next;
free(p);
S->len--;
return OK;
}
总结:两者在时间复杂度上是一样的,均为O(1)。
如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好使用链栈,反之,如果它的变化在可控范围你,建议使用顺序栈。
栈的应用—四则运算表达式
实现:
- 将中缀表达式转化为后缀表达式(栈用来进出运算的符号)(符号栈)
- 将后缀表达式进行运算得出结果(栈用来进出运算的数字)(数字栈)
STEP:
1. 从左到右遍历中缀表达式的每个数字和符号,当是数字时就输出,即成为后缀表达式的一部分,若是符号,则判断其与当时栈顶的符号的优先级,是右括号或优先级(不包括括号)低于(等于)栈顶符号,则以此栈顶元素依次出栈并输出,直至当前栈顶元素优先级低于入栈符号,将当前符号入栈,直到最后最终输出后缀表达式为止。
2. 从左到右遍历后缀表达式的每个数字和符号,若是遇到数字就进栈,若是遇到符号,就将栈顶两个数字出栈,进行运算,运行结果进栈,一直到得到最终结果。
一点小问题:在代码中如何进行优先级的比较?
解决方案:构造一个优先级函数,将可能用到的符号设置数字优先级,当传入某符号时,通过该函数返回它的数字优先级,通过数字来进行比较。
初始版本
代码实现
int priority(char ch)
{
if(ch=='+'||ch=='-')
return 1;
else if(ch=='*'||ch=='/')
return 2;// 括号不设置优先级
}
#include "myfunc.h"
void myoperate(char ch,int *sec_top,int top)
{
switch (ch)
{
case '+':
*sec_top=top+(*sec_top);
break;
case '-':
*sec_top=(*sec_top)-top;
break;
case '*':
*sec_top=(*sec_top)*top;
break;
case '/':
*sec_top=(*sec_top)/top;
break;
default:
break;
}
}
#include "myfunc.h"
void stack_cal(char *s) //,s为字符串数组,将屏幕输入以字符串的形式读入。另外,只能实现10以内的计算
{
int num_stack[MAXSIZE];
char ch_stack[MAXSIZE];
int i=0;
int j=-1;
int k=-1;
int cur_prio=0; //标志当前栈顶元素的优先级,优先级均为自然数,大于0。
while(s[i])
{
if(s[i]<='9'&&s[i]>='0')
{
num_stack[++j]=s[i]-'0';
i++;
continue;
}
if(s[i]=='(')
{
ch_stack[++k]=s[i];
i++;
continue;
}
if(s[i]=='*'||s[i]=='/')
{
ch_stack[++k]=s[i];
cur_prio=priority(ch_stack[k]);
i++;
continue;
}
if(s[i]==')')
{
while(ch_stack[k]!='(')
{
myoperate(ch_stack[k],&num_stack[j-1],num_stack[j]);
j--;
k--;
}
k--;
cur_prio=ch_stack[k];
i++;
continue;
}
while(priority(s[i])<=cur_prio&&k>=0&&ch_stack[k]!='(') //优先级(不包括括号)低于(等于)栈顶符号,则以此栈顶元素依次出栈并输出,直至当前栈顶元素优先级低于入栈符号,将当前符号入栈,直到最后最终输出后缀表达式为止
{
myoperate(ch_stack[k],&num_stack[j-1],num_stack[j]);
j--;
k--;
cur_prio=priority(ch_stack[k]);
}
ch_stack[++k]=s[i];
cur_prio=priority(ch_stack[k]);
i++;
}
while(k>=0)
{
myoperate(ch_stack[k],&num_stack[j-1],num_stack[j]);
k--;
j--;
}
printf("%d\n",num_stack[0]);
}
关于如何实现10以上的数据计算,有空再写。