堆栈存储及相关操作
堆栈是具有一定操作约束的线性表,是一种特殊的线性表,只能在一端(即栈顶Top)做插入、删除,具有先入后出的特点。其中插入为入栈push,删除为出栈pop
- 顺序存储
struct SNode {
ElementType Data[maxsize]; /* 存储元素的数组 */
int Top; /* 栈顶元素下标,empty时为-1 */
};
typedef struct SNode *Stack;
建栈
Stack CreateStack( )
{
Stack S = (Stack)malloc(sizeof(struct SNode));
S->Top = -1;
return S;
}
入栈和判断栈是否已满
void Push( Stack S, ElementType X )
{
if ( IsFull(S) ) printf("堆栈满");
else
{
s->Top++;
S->Data[S->Top] = X;
}
}
bool IsFull( Stack S )
{
return (S->Top == S->MaxSize-1);
}
出栈和判断栈是否为空
ElementType Pop( Stack S )
{
if ( IsEmpty(S) )
{
printf("堆栈空");
return ERROR; /* ERROR是ElementType的特殊值,标志错误 */
}
else
return ( S->Data[(S->Top)--] );
}
bool IsEmpty( Stack S )
{
return (S->Top == -1);
}
- 链式存储
栈的链式存储结构实际上只是一个单链表,叫做链栈,插入和删除操作只能在栈顶进行其中栈顶指针Top一端只能是链表的头结点这一头,否则删除操作的时候,找不到前一个结点
struct SNode {
ElementType Data;
struct SNode *Next;
};
typedef struct SNode *Stack;**
建栈
Stack CreateStack( )
{ /* 构建一个堆栈的头结点,返回该结点指针 */
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
入栈(插入头结点之后)
void Push( Stack PtrS, ElementType X )
{
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Data = X;
S->Next = PtrS->Next;
PtrS->Next = S;
}
出栈和判断栈是否为空
ElementType Pop( Stack PtrS )
{ /* 删除并返回堆栈S的栈顶元素 */
Stack S;
ElementType X;
if( IsEmpty(PtrS) )
{
printf("堆栈空");
return ERROR; /* ERROR是ElementType的特殊值,标志错误 */
}
else
{
S = PtrS->Next;
X = S->Data;
PtrS->Next =S->Next;
free(S);
return X;
}
}
bool IsEmpty ( Stack S )
{ /* 判断堆栈S是否为空,即判断头结点的指针指向是否为空*/
return ( S->Next == NULL );
}
堆栈应用
- 数制的转换
将一个十进制数转换为八进制数时,在计算过程中,把 N 与 8 求余得到的八进制数的各位依次进栈,计算完毕后将栈中的八进制数依次出栈输出,输出结果就是待求得的八进制数
具体实现的时候,可以用顺序存储也可以用链式存储表示
算法步骤
①初始化一个空栈 S
②当十进制数 N 非零时,循环执行以下操作:
- 把 N 与 8 求余得到的八进制数压入栈 S
- N 更新为 N 与 8 的商
③当栈 S 不为空时,循环执行以下操作:
- 弹出栈顶元素 e
- 输出 e
void conversion( int N )
{/*对于一个任意的非负十进制数,转换输出与其值对应的八进制数*/
InitStack(S);
while(N)
{
Push(S, N % 8); /*N与8的余数压栈*/
N = N/8;
}
while(!IsEmpty(S));
{
e = Pop(S); /*弹出栈顶元素e*/
cout<<e;
}
}
- 括号匹配的检验
算法步骤
①初始化一个空栈 S
②设置一个标记性变量 flag,用来标记匹配结果,以控制循环及返回结果,初值为1
③依次读入表达式字符 ch,若表达式没有读入完毕且 flag 非零,则循环执行以下步骤:
- 若 ch 为左括号 ‘(’ 或 ‘[’,则将其压栈
- 若为 ch 右括号 ‘)’,则根据当前栈顶元素值分情况考虑:若栈顶非空且栈顶元素是 ‘(’,则正确匹配,否则匹配错误,flag 置为0
- 若为 ch 右括号 ‘]’,则根据当前栈顶元素值分情况考虑:若栈顶非空且栈顶元素是 ‘[’,则正确匹配,否则匹配错误,flag 置为0
④推出循环后,若栈空且 flag 值为1,则匹配成功,返回 true,否则返回 false
bool Matching()
{/*检验表达式中所含括号是否正确匹配,如果匹配,则返回true,否则返回false*/
InitStack(S);
int flag=1; /*标记匹配结果*/
scanf("%c",&ch); /*读入第一个字符*/
while(ch!='#'&&flag) /*假设表达式以#结尾*/
{
switch(ch)
{
case '[':
case '(':
Push(S,ch); /*左括号则压栈*/
break;
case ')':
if(!IsEmpty(S)&&GetTop(S)=='(')
Pop(S);
else
flag=0; /*匹配错误*/
break;
case ']':
if(!IsEmpty(S)&&GetTop(S)=='[')
Pop(S);
else
flag=0; /*匹配错误*/
break;
}
cin>>ch; /*继续读入下一个字符*/
}
if(IsEmpty(S)&&flag) return true; /*Empty说明全部匹配完成*/
else return false;
}
- 表达式的求值
中缀表达式:运算符号位于两个运算数之间。如:a + b * c - d / e
后缀表达式:运算符号位于两个运算数之后。如:a b c * + d e / -
后缀表达式:
对于后缀表达式,可以比较容易处理。大致过程为:将后缀表达式从左到右扫描,遇到数字就将数字压如堆栈中,遇到运算符就将运算符前面的两个数字出栈,将运算符置于两数字之间运算,将运算结果入栈。最后栈中剩余的最后一个元素就是运算结果
例如求:6 2 / 3 - 4 2 * +
①从左到右扫描,遇到6和2,分别将其压进堆栈中
②遇到除号,将6和2出栈,计算6/2=3,将3入栈;继续扫描,后面遇见一个3,继续入栈
③ 遇到减号,将两个3出栈,计算3-3=0,将0入栈;继续扫描,后面遇见4与2,将其入栈
④遇到乘号,将4和2出栈,计算4*2=8,将8入栈
⑤遇到加号,将0和8出栈,计算0+8=8,将8入栈,扫描之后发现已经没有字符,而堆栈中只有8这个元素,故结果为8
中缀表达式
对于中缀表达式,我们要做的就是将其变成后缀表达式。
观察两种表达式的转换:2 + 9 / 3 - / 5 —> 2 9 3 / + 5 -
①运算数相对顺序不变
②运算符号顺序发生改变
- 需要存储“等待中”得运算符号
- 也要将当前运算符号与“等待中”的运算符号比较
转换过程
从头到尾读取中缀表达式的每个对象,对不同的对象按照不同的情况处理
①运算数:直接输出;
②左括号:压入堆栈;
③右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
④运算符:
- 若优先级大于栈顶运算符时,则把它压入栈中;
- 若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶元素运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压入栈中。
⑤若各对象处理完毕,则将堆栈中存留的运算符一并输出。
/* main */
#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
void Read_Expression(char str[]); /*读取键盘输入的中缀表达式*/
void Conversion(char Infix[], char Suffix[]); /*将中缀表达式转换为后缀表达式*/
int Compare(char ch, StackofChar SPtr); /*运算符的比较*/
float Calculate(char Suffix[]); /*由后缀表达式求出表达式结果*/
void Push(StackofChar, char);
void PushFloat(StackOfFloat, float); /* 出栈、入栈的基本操作 */
char Pop(StackofChar);
float PopFloat(StackOfFloat);
struct SNodeofChar {
char Data[MAXSIZE]; /* 存放字符的堆栈(用于转换中存储运算符,进行优先级比较) */
int Top;
};
typedef struct SNodeofChar* StackofChar;
struct SNodeOfFloat {
float Data[MAXSIZE]; /* 存放浮点数的堆栈(用于转换后的求值运算,只需要运算数入栈) */
int Top;
};
typedef struct SNodeOfFloat* StackOfFloat;
int main()
{
char InfixStr[MAXSIZE] = {0}, SuffixStr[MAXSIZE] = {0};
/*将在键盘上输入的中缀表达式保存在字符串InfixStr中*/
Read(InfixStr);
/*将中缀表达式抓换成后缀表达式保存在字符串SuffixStr中*/
Conversion(InfixStr,SuffixStr);
printf("\n结果是:%f\n", Calculate(SuffixStr));
return 0;
}
/* Read_Expression */
void Read_Expression(char str[])
{
printf("请输入所需求值的中缀表达式(英文输入法):\n");
gets(str);
}
/* Conversion*/
void Conversion(char Infix[], char Suffix[])
{
int i=0,j=0,Tag=0;
struct SNodeofChar SChar = {{'\0'},-1}; /* 建立一个空字符栈,并初始化 */
StackofChar SPtr = &SChar;
/* 扫描中缀表达式的所有字符,根据字符的值做出不同操作 */
while (Infix[i] != '\0')
{
/*当扫描到数字的时候,直接输出到字符串Suffix中*/
if (Infix[i] <= '9'&&Infix[i] >= '0')
{
Suffix[j] = Infix[i];
j++;
}
/*当扫描到空格的时候,直接输出到字符串Suffix中*/
else if (Infix[i] == ' ')
{
Suffix[j] = Infix[i];
j++;
}
/*当扫描到加减乘除运算符,将其与堆栈顶元素比较,根据优先级决定进栈还是栈顶元素出栈输出*/
else if(Infix[i] == '+' || Infix[i] == '-' || Infix[i] == '*' || Infix[i] == '/')
{
Suffix[j] =' ';
j++;
Tag = 0 /*扫描到的元素要循环与栈顶元素比较优先级,直至扫描到的元素入栈*/
while (Tag == 0)
{
Tag = Compare(Infix[i], SPtr);
if (Tag == 0)
Suffix[j++] = Pop(SPtr);
else
Push(SPtr, Infix[i]);
}
}
/*如果扫描到左括号,直接进栈*/
else if(Infix[i]=='(')
{
Push(SPtr, Infix[i]);
}
/*如果扫描到右括号,一直出栈输出直到栈顶元素是左括号,然后将栈顶的左括号出栈但不输出*/
else if (Infix[i] == ')')
{
Suffix[j] = ' ';
j++;
while (SPtr->Data[SPtr->Top] != '(')
{
Suffix[j] = Pop(SPtr);
j++;
}
Pop(SPtr); /* 左括号不输出 */
}
i++; /* 进行下一个字符的扫描 */
}
/*扫描完之后将堆栈中剩余所有元素出栈*/
while (SPtr->Top != -1)
{
Suffix[j] = Pop(SPtr);
j++;
}
Suffix[j]='\0';
}
/* Compare */
int Compare(char ch, StackofChar SPtr)
{
/*如果堆栈空,将扫描到的运算符号进栈*/
if (SPtr->Top == -1)
return 1;
/*当栈顶是左括号时,将扫描到的运算符号进栈*/
if (SPtr->Data[SPtr->Top] == '(')
return 1;
/*当栈顶是乘号或者除号的情况*/
else if (SPtr->Data[SPtr->Top] == '/' || SPtr->Data[SPtr->Top] == '*')
return 0; /* 一定为栈顶元素出栈,因为括号不调用Compare函数 */
/*当栈顶元素是加号或者减号时*/
else
{
if (ch == '*' || ch == '/')
return 1; /*如果扫描到的是乘号或除号,将栈顶元素进栈*/
else
return 0; /*如果扫描到的是加号或者减号,将栈顶元素出栈*/
}
}
/* Calculate */
float Calculate(char Suffix[])
{
float num = 0, temp = 0, first = 0, last = 0;
int i = 0;
struct SNodeOfFloat SFloat = {{0},-1}; /* 建立一个空浮点数栈存储运算数 */
StackOfFloat SPtr = &SFloat;
while (Suffix[i] != '\0') /* 扫描后缀表达式,计算结果 */
{
/* 当扫描到的是数字字符,将其转换成数字,并将其压入栈中 */
if (Suffix[i] <= '9' && Suffix[i] >= '0')
{
num = 0;
while (Suffix[i] <= '9' && Suffix[i] >= '0')
{
temp = (float)(Suffix[i] - '0');
num = num * 10 + temp; /* 运算数可能为多位数(数字与数字间无空格) */
i++;
}
PushFloat(SPtr, num);
}
/* 当扫描到空格,则忽略 */
else if (Suffix[i] == ' ')
i++;
/*当扫描到加减乘除号,将栈顶两个元素弹出,并将其用扫描到的运算符号计算,将结果压入栈中*/
else if(Suffix[i] == '+'||Suffix[i] == '-'||Suffix[i] == '*'||Suffix[i] == '/')
{
first = PopFloat(SPtr);
last = PopFloat(SPtr);
if (Suffix[i] == '+')
{
PushFloat(SPtr, first + last);
i++;
}
else if (Suffix[i] == '-')
{
PushFloat(SPtr, first - last);
i++;
}
else if (Suffix[i] == '*')
{
PushFloat(SPtr, first*last);
i++;
}
else
{
PushFloat(SPtr, first / last);
i++;
}
}
return PopFloat(SPtr); /* 栈中最后一个数字就是结果,将其返回 */
}
}