栈的类型定义
栈:限定在表的一端(栈顶)进行插入和删除操作的线性表
表中没有元素时:空栈
栈的存储表示和操作的实现
顺序存储/链式存储
堆栈的顺序存储实现
要事先为它分配一个可以容纳最多元素的存储空间
栈顶指针:指示栈顶元素在栈中的位置,值是栈中元素的个数
栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成
栈的顺序存储实现代码如下
struct SNode {
int *Data; /* 存储元素的数组 */
int Top; /* 栈顶指针 */
int MaxSize; /* 堆栈最大容量 */
};
typedef struct SNode *Stack;
//构造一个最大存储容量为MaxSize的空栈s
Stack CreateStack( int MaxSize ){
Stack S = (Stack)malloc(sizeof(struct SNode));//s是指向结构体的指针
S->Data = (int *)malloc(MaxSize * sizeof(int));
S->Top = -1;//因为是空栈
S->MaxSize = MaxSize;
return S;
}
bool IsFull( Stack S ){
return (S->Top == S->MaxSize-1);//top可以表示非空元素个数,MaxSize表示栈的最大容量
}
bool IsEmpty( Stack S ){
return (S->Top == -1);
}
//压数据入栈
bool Push( Stack S, int X ){
if ( IsFull(S) ) {
printf("堆栈满");
return false;
}
else {
S->Data[++(S->Top)] = X;
return true;
}
}
//弹数据出栈
int Pop( Stack S ){
if ( IsEmpty(S) ) {
printf("堆栈空");
return 0; /* ERROR是ElementType的特殊值,标志错误 */
}
else return ( S->Data[(S->Top)--] );
}
堆栈的链式存储实现
链栈中指针方向从栈顶指向栈底
不需要事先分配空间,不需要顾忌栈的空间是否已经被填满
堆栈链式存储实现的代码如下:
#include<stdio.h>
#include<stdlib.h>
typedef struct SNode *PtrToSNode;
struct SNode {
int Data;
PtrToSNode Next;
};
typedef PtrToSNode Stack;
//建立空栈
Stack CreateStack( ) { /* 构建一个堆栈的头结点,返回该结点指针 */
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
/* 判断堆栈S是否为空,若是返回true;否则返回false */
bool IsEmpty ( Stack S ){
return ( S->Next == NULL );
}
/* 将元素X压入堆栈S */
bool Push( Stack S, int X ){
PtrToSNode TmpCell;
TmpCell = (PtrToSNode)malloc(sizeof(struct SNode));
TmpCell->Data = X;
TmpCell->Next = S->Next;//S -> Next 表示指向链栈的头结点
S->Next = TmpCell;
return true;
}
/* 删除并返回堆栈S的栈顶元素 */
int Pop( Stack S ) {
PtrToSNode FirstCell;
int TopElem;
if( IsEmpty(S) ) {
printf("堆栈空");
return 0;
}
else {
FirstCell = S->Next;
TopElem = FirstCell->Data;
S->Next = FirstCell->Next;
free(FirstCell);//释放被删除元素的空间
return TopElem;
}
}
栈的应用举例
数制转换
数制转换过程中,各数位产生的顺序是从低位到高位,打印顺序是从高位到低位,按“后进后出”的规律进行,所以适合用堆栈进行
#include<stdio.h>
#include<stdlib.h>
typedef struct SNode *PtrToSNode;
struct SNode {
int Data;
PtrToSNode Next;
};
typedef PtrToSNode Stack;
Stack S;
//建立空栈
Stack CreateStack( Stack s ) { /* 构建一个堆栈的头结点,返回该结点指针 */
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
/* 判断堆栈S是否为空,若是返回true;否则返回false */
bool IsEmpty ( Stack S ){
return ( S->Next == NULL );
}
/* 将元素X压入堆栈S */
bool Push( Stack S, int X ){
PtrToSNode TmpCell;
TmpCell = (PtrToSNode)malloc(sizeof(struct SNode));
TmpCell->Data = X;
TmpCell->Next = S->Next;//S -> Next 表示指向链栈的头结点
S->Next = TmpCell;
return true;
}
/* 删除并返回堆栈S的栈顶元素 */
int Pop( Stack S ) {
PtrToSNode FirstCell;
int TopElem;
if( IsEmpty(S) ) {
printf("堆栈空");
return 0;
}
else {
FirstCell = S->Next;
TopElem = FirstCell->Data;
S->Next = FirstCell->Next;
free(FirstCell);//释放被删除元素的空间
return TopElem;
}
}
void converse(int num, int d) {
Stack s = (Stack)malloc(sizeof(struct SNode));
CreateStack(s);
while(num) {
Push(s,num%d);
num /= d;
}
while( !IsEmpty(s) ) {
printf("%d",Pop(s));
}
return;
}
int main() {
int num,d;
scanf("%d %d",&num,&d);
converse(num,d);
}
括号检验
主要思路:利用堆栈,将先后出现的左括弧一次保存,对出现的右括弧与栈顶的左括弧进行匹配,若正好匹配,则将改左括弧从栈顶删除
表达式求值
后缀式运算规则:
运算符在式中出现的顺序恰为表达式的运算顺序
每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式
将表达式转化为后缀式
①实例运算符栈
②设表达式结束符为#,预设运算符栈的栈底为#
③若当前字符是操作数,则直接发给后缀式
④若当前字符为运算符且优先数大于栈顶运算符,则进栈;否则退出栈顶运算符发送给后缀式
⑤若当前字符是结束符,则自栈顶至栈底依次将栈中所有运算符发送给后缀式
如何按后缀式进行计算
对后缀式从左到右扫描,遇到操作数暂时保存,遇到运算符即可进行运算,且先出现的是第一操作数,后出现的是第二操作数
要点:
不把所有内容存放在一个数组中,直接输出,通过len判断是否加空格
注意正负号不一定表示运算
不要遗漏小数点
直接上表达式程序转换C语言代码:
#include<stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int top=0,count=0;
char a[21],b[42];
scanf("%s",a);
int c=strlen(a);
int len=0;
for(int i=0;a[i]!='\0';i++) {
if(a[i]!='('&&a[i]!=')')
len++;
}
for(int i=0;i<c;i++) {
if(a[i]==')') {
while(b[top]!='(') {
printf("%c",b[top--]);
count++;
if(count!=len) printf(" ");
}
top--;
}
else if(a[i]=='*'||a[i]=='/'){
while(top>0&&(b[top]=='*'||b[top]=='/')){
printf("%c",b[top]);
top--;
count++;
if(count<len) printf(" ");
}
b[++top]=a[i];
}
else if(a[i]=='(') b[++top]=a[i];
else if(a[i]=='+'||a[i]=='-'){
if(i==0||(i>0&&a[i-1]=='(')){
if(a[i]=='+') count++;
else{
printf("%c",a[i]);
count++;
}
continue;
}
while(top>0&&b[top]!='('){
printf("%c",b[top--]);
count++;
if(count!=len) printf(" ");
}
b[++top]=a[i];
}
else{
printf("%c",a[i]);
count++;
if(count!=len&&(a[i+1]<'0'||a[i+1]>'9')&&a[i+1]!='.') printf(" ");
}
}
while(top!=0){
printf("%c",b[top--]);
count++;
if(count<len) printf(" ");
}
}
递归函数的实现
先调用后返回,实行栈式管理
执行递归函数需要递归工作栈——保证每层的递归调用都是对本层的数据进行操作