21年王道数据结构--栈与队列

顺序栈的实现:
栈的初始操作
代码:

#define MaxSize 10//定义元素最大的个数 
typedef struct{
	ElemType data[MaxSize];//静态数组存放栈中的元素 
	int top;//栈顶指针 
}SqStack;
//初始化栈
void InitStack(SqStack &S){
	S.top=-1;
} 
void testStack(){
	SqStack S;//声明一个顺序栈
	InitStack(S); 
	//。。。后续操作。。。 
} 
bool StackEmpty(SqStack S){
	if(S.top==-1)//栈空 
	return true;
	else//不空 
	return false;
}

顺序栈的出栈和入栈的操作:
代码:

#define MaxSize 10//定义元素的最大个数 
typedef struct {
	ElemType data[MaxSize];//静态数组存放栈中元素 
	int top; //栈顶指针 
}SqStack;
//新元素入栈
bool Push(SqStack &S,ElemType x){
	if(S.top==MaxSize-1)//栈满,报错 
	return false;
	S.top=S.top+1;//指针先加一 
	S.data[S.top]=x;//新元素入栈 
	return true;
} 
//出栈操作 
bool pop(SqStack &S,ElemType &x){
	if(S.top==-1)  //栈空,报错 
	return false;
	x=S.data[S.top];//栈顶元素先出栈 
	S.top=S.top-1;//指针再减1 
	return true;
}
 

顺序栈的读栈顶元素操作:
代码:

#define MaxSize 10//定义栈中元素的最大个数 
typedef struct{
	ElemType data[MaxSize];//静态数组存放栈中元素 
	int top;//栈顶指针 
}SqStack;
//出栈操作 
bool Pop(SqStack &S,ElemType &x){
	if(S.top==-1)//栈空,报错 
	return false;
	x=S.data[S.top--];//先出栈,指针再减1 
	return true;
} 
//读栈顶元素 
bool GetTop(SqStack S,ElemType &x){
	if(S.top==-1)//栈空,报错 
	return false;
	x=S.data[S.top];//k记录栈顶元素 
	return true; 
}

另一种方式读取栈顶元素
代码:

#define MaxSize 10
typedef struct{
	ElemType data[MaxSize];
	int top;
}SqStack;
//初始化栈
void InitStack(SqStack &S){
	S.top==0;
} 

void testStack(){
	SqStack S;//声明一个顺序栈(分配空间) 
	InitStack(S);
	//...后续操作... 
}

共享栈:
代码:

#define MaxSize 10//定义栈中元素的最大个数 
typedef struct{
	ElemType data[MaxSize];//静态数组存放栈中元素 
	int top0;//0号栈栈顶指针 
	int top1;//1号栈栈顶指针 
}ShStack;
//初始化栈
void InitStack(ShStack &S){
	S.top0=-1;//初始化栈顶指针 
	S.top1=MaxSize;
} 

链栈:
代码:

#include<malloc.h>
#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef int status;
using namespace std;
typedef struct Node{
	ElemType data;
	struct Node* next; 
}Node;
typedef struct{
	Node* top;
	ElemType count;
}SqLStack;
void InitStack(SqLStack *S){
	S->top=(Node*)malloc(sizeof(Node));
	S->top=NULL;
	S->count=0;
}
void PushStack(SqLStack *S,ElemType e){
	Node *temp=(Node*)malloc(sizeof(Node));
	temp->data=e;
	temp->next=S->top;
	S->top=temp;
	S->count++;
}
void PopStack(SqLStack *S){
	Node* p=(Node *)malloc(sizeof(Node));
	p=S->top;
	S->top=S->top->next;
	free(p);
	S->count--;	
}
void  PrintStack(SqLStack *S){
		printf("该链栈的元素如下:\n");
		Node* p=(Node*)malloc(sizeof(Node));
		p=S->top;
		while(p->next!=NULL){
			printf("%d->",p->data);
			p=p->next;
		}
		printf("%d\n",p->data);	
}
int main(){
	SqLStack S;
	InitStack(&S);
	PushStack(&S,1);
	PushStack(&S,2);
	PushStack(&S,3);
	PushStack(&S,4);	
	printf("初始化成功了!\n");
	PrintStack(&S);
	printf("该链栈已经启动成功!\n");
	printf("开始在该链栈中进行元素的删除\n");
	PopStack(&S);
    PrintStack(&S);
	printf("该链栈中元素显示详情如上\n");
	printf("再次在该链栈中进行元素的删除\n");
	PopStack(&S);
    PrintStack(&S);
	printf("该链栈中元素显示详情如上\n");
	printf("再次在该链栈中进行元素的删除\n");
	PopStack(&S);
    PrintStack(&S);
	printf("该链栈中元素显示详情如上\n");
	return 0; 
} 

代码运行截图:
在这里插入图片描述
队列的顺序实现:
队列的结构体定义:
代码:

#define MaxSize 10//定义队列中元素的最大个数 
typedef struct{
	ElemType data[MaxSize];//用静态数组存放队列元素 
	int front,rear;//队头指针和队尾指针 
}SqQueue; 

入队实现:
代码:

//入队
bool EnQueue(SqQueue &Q,ElemType x){
	if(队列已满)
	return false;//队满则报错 
	Q.data[Q.rear]=x;//将x插入队尾 
	Q.rear=Q.rear+1;//队尾指针后移 
	return true; 
}

//改进之后入队的循环队列
bool EnQueue(SqQueue &Q,ElemType x){
	if(队列已满)
	return false;//队满则报错 
	Q.data[Q.rear]=x;//将x插入队尾 
	Q.rear=(Q.rear+1)%MaxSize;//队尾指针后移 
	return true; 
} 

出队操作:
代码:

//出队(删除一个队头元素,并用x返回)
bool DeQueue(SqQueue &Q,ElemType &x){
	if(Q.rear=Q.front) 
	return false;//队空则报错 
	x=Q.data[Q.front];
	Q.front=(Q.front+1)%MaxSize;
	return true;
} 

方案一:判断队列已满/已空
队列元素个数:
(rear+MaxSize-front)%MaxSize
初始化时
rear=front=0
队列已满的条件:队尾指针的再下一个位置是队头,即(Q.rear+1)%MaxSize=Q.front
队空的条件:
Q.rear=Q.front

方案二:判断队列已满/已空
初始化时
rear=front=0;
size=0;
插入成功size++;
删除成功size–;
队空条件:
size0;
队满条件:
size
MaxSize;

方案三:判断队列已满/已空
初始化时
rear=front=0;tag=0;
每次删除操作成功时,都令tag=0;
每次插入操作成功时,都令tag=1;
队满条件:
frontrear&&tag1
队空条件:
frontrear&&tag0

队列的链式实现:
代码:

typedef struct LinkNode{ //链式队列结点 
	ElemType data;
	struct LinkNode *next;
}LinkNode;
 typedef struct{  //链式队列 
 	LinkNode *front,*rear; //队列的队头和队尾的指针 
 }LinkQueue; 

链式队列的初始化(带头结点)
代码:

 //初始化队列(带头结点) 
 void InitQueue(LinkQueue &Q){
 	//初始化时front、rear都指向头结点
	 Q.front=Q.rear=(LinkNode*)malloc(sizeof(LinkNode));
	 Q.front->next=NULL;
 }
 void testLinkQueue(){
 	LinkQueue Q;//声明一个队列 
 	InitQueue(Q);//初始化队列 
 	//。。。后续操作。。。 
 }
 //判断队列是否为空
 bool IsEmpty(LinkQueue Q){
 	if(Q.front==Q.rear)
 	return true;
 	else
 	return false;
 } 

链式队列的入队(带头结点和不带头结点)
代码:

 //新元素入队(带头结点) 
 void EnQueue(LinkQueue &Q,ElemType x){
 	LinkNode* s=(LinkNode*)malloc(sizeof(LinkNode));
 	s->data=x;
 	s->next=NULL;
 	Q.rear->next=s;//新结点插入到rear之后 
 	Q.rear=s;//修改表尾指针 
 }
 //新元素入队(不带头结点)
 void EnQueue(LinkQueue &Q,ElemType x){
 	LinkNode *s=(LinkNode*)malloc(sizeof(LinkNode));
 	s->data=x;
 	s->next=NULL;
 	if(Q.front==NULL){//在空队列中插入第一个元素 
 		Q.front=s;//修改队头队尾指针 
 		Q.rear=s;
	 }else{
	 	Q.rear->next=s;//新结点插入到rear结点之后 
		Q.rear=s; //修改rear指针 
	 }
 } 

链式队列的出队(带头结点):
代码:

 
 //队列元素出队(不带头结点)
 bool DeQueue(LinkQueue &Q,ElemType &x){
 	if(Q.front==Q.rear)
 	return false;//空队 
 	LinkNode *p=Q.front->next;
 	x=p->data;//用变量x返回队头元素 
 	Q.front->next=p->next;//修改头结点的next指针 
 	if(Q.rear==p){//此次是最后一个结点出队 
 		free(p);//释放节点空间 
 		return true;
	 }
 } 

双端队列:
双端队列不考具体实现,只考关于对于双端队列的判断输出队列的合法性:
双端队列:允许从两端插入、两端删除的队列。
输入受限的双端队列:允许两端删除、从一端插入的队列。
输出受限的双端队列:允许从两端插入、从一端删除的队列。

卡特兰数:
大佬总结的链接:
https://blog.csdn.net/wookaikaiko/article/details/81105031

栈的应用:
括号匹配问题:
算法思想:
依次扫描所有字符,遇到左括号入栈,遇到右括号则弹出栈顶元素检查是否匹配。
匹配失败情况:
①、左括号单身②、右括号单身③、左右括号不匹配。

代码:

//本代码对于栈的链式存储结构进行说明栈的括号匹配的问题 
#include<iostream>
#include<malloc.h>
using namespace std;
char str[7]={'(','[','{','}',']',')'};
char str1[7]={'(',']','{','}',']',')'};
typedef struct Node{
	struct Node* next;
	char data;
}Node;
typedef struct{
	Node *top;
	int count; 
}LinkStack;
bool InitStack(LinkStack *s){
	s->top=(Node*)malloc(sizeof(Node));
	if(s==NULL)
	return false;
	else{
	  s->top=NULL;
	  s->count=0;
	  return true;	
	} 
}
bool StackEmpty(LinkStack *s){
	if(s->top==NULL)
	return true;
	else
	return false;
}
bool Push(LinkStack *s,char x){
	if(s==NULL)
	return false;
	else{
	Node* p=(Node*)malloc(sizeof(Node));
	p->data=x;
	p->next=s->top;
	s->top=p;
	s->count++;
	return true;	
	}
}
bool Pop(LinkStack *s,char &x){
	if(s->top==NULL)
	return false;
	else{
	Node *p=(Node*)malloc(sizeof(Node));
	p=s->top;
	s->top=s->top->next;
	s->count--;
	x=p->data;
	free(p);
	return true; 	
	} 
}
bool printStack(LinkStack *s){
	if(s->top==NULL){
		printf("该链栈已空,不能打印数据元素!\n");
		return false;
	}
	else{
		printf("该链栈的数据元素的详情如下:\n");
		printf("栈顶->");
		Node* p=(Node*)malloc(sizeof(Node));
		p=s->top;
		while(p!=NULL){
			printf("%c ",p->data);
			p=p->next;
		} 
		printf("\n\n");
		return true;
	}
}
bool bracketCheck(LinkStack &S,char strr[],int length){
	InitStack(&S);
	for(int i=0;i<length;i++){
		printf("插入的元素到链栈之前先判断该元素的值\n");
		printf("要判断的字符值是%c\n\n",strr[i]);
		if(strr[i]=='('||strr[i]=='['||strr[i]=='{'){
			Push(&S,strr[i]);
			printStack(&S); 
		}else{
			if(StackEmpty(&S))
			return false;			
			char TopElem;
			if(Pop(&S,TopElem)){
				if(TopElem!='('&&strr[i]==')'){
					return false;
				}else if(TopElem!='{'&&strr[i]=='}'){
					return false;
				}else if(TopElem!='['&&strr[i]==']'){
					return false;
				}
			}
		}
	} 	
	return	 StackEmpty(&S);
}
void printStr(char str[]){
	printf("该链栈的元素如下:\n");
	for(int i=0;i<6;i++){
		printf("%c ",str[i]);
	}
	printf("\n");
}
void print1(bool value){
	if(value)
		printf("该链栈的括号匹配成功!\n");
	else
        printf("该链栈的括号匹配失败!\n");
} 

int main(){
    LinkStack S1;
    LinkStack S2;
	printStr(str);
	printStr(str1);
    print1(bracketCheck(S1,str,6));
    printf("-----------------------------------\n");
    print1(bracketCheck(S2,str1,6));
	return 0;
} 

代码运行截图:
在这里插入图片描述
栈在表达式的求值:
各种表达式的概念:
中缀表达式:
运算符在操作数中间
后缀表达式:
运算符在两个操作数后面
前缀表达式:
运算符在两个操作数前面
中缀表达式转后缀表达式(手算)
中缀转后缀的手算方法:
①、确定中缀表达式中各个运算符的运算顺序
②、选择下一个运算符,按照【左操作数 右操作数 运算符】的方式组合成一个新的操作数
③、如果还有运算符没被处理,就继续②

注意:
运算顺序不唯一,因此对应的后缀表达式也不唯一,用咸鱼学长想到的”左优先“原则,只要左边的运算符能先计算,就优先算左边的。
中缀表达式转化为后缀表达式:

起初想用stl库的栈的stack的头文件来实现的栈的功能,并运行其转换表达式的功能,最后因为超时却没成功!也是蛮辛苦的。
代码:

//中缀表达式转后缀表达式
#include<iostream>
#include<stack>
#include<string>
#include<cctype>
using namespace std;
stack<char> S;//符号栈 
stack<char> T;//数值栈 
stack<char> R; //结果栈 
//A+B*(C-D)-E/F#  -------->>>>>>> ABCD-*+EF/-或ABCD-*EF/-+ 
//首先使用c++的方法实现中缀表达式转后缀表达式 
int prior(char a){
	if(a==')'){
	    return 0;	
	}else if(a=='+'||a=='-'){
		return 1;
	}else if(a=='*'||a=='/'){
		return 2;
	}else if(a=='('){
		return 3;
	}
}
void Exchange(char a){
	if(isalpha(a)){
		if(prior(a)<=prior(S.top())){
			if(T.size()>=2){
			char x,y;
			x=T.top();
			T.pop();
			y=T.top();
			T.pop();
			R.push(y);
			R.push(x);	
			}
			char x=S.top();
			R.push(x);
			S.pop();
		}
		if(a!=')'){
		S.push(a);	
		}else{
			S.pop();
		}
	}else{	
		T.push(a);
	}
	
//		for(auto it=top(S);!S.empty();it++,pop(S)){
//			if(*it)
//		}
	}
void finish(){
	if(!S.empty()&&!T.empty()){
		while(!S.empty()&&!T.empty()){
			if(T.size()>=2){
			char x,y;
			x=T.top();
			T.pop();
			y=T.top();
			T.pop();
			R.push(y);
			R.push(x);	
			}
			char x=S.top();
			R.push(x);
			S.pop();
		}
}
}
int main(){
	char a;
	scanf("%c",&a); 
	while(a!='#'){
	Exchange(a);
	scanf("%c",&a);	
	}
	finish();	
    while(!S.empty()){
    	char x=S.top();
    	printf("%c",x);
    	S.pop(); 
}
	printf("\n");
	return 0;
}

代码运行超时!!!!
之后老老实实用链栈来实现上述未运行的代码。

代码:

#include<iostream>
#include<vector>
#include<malloc.h>
using namespace std;
//中缀转后缀代码实现 
//以一个中缀表达式为例来转化为后缀表达式:
//如A+B*(C-D)-E/F# 其结果为    ABCD-*+EF/- 

/*
①、从左到右扫描到操作数则压入栈,直到处理完所有元素
②、若扫描到操作数则压入栈,并回到①;否则执行③
③、若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①*/
typedef struct Node{
	struct Node* next;
	char data;
}Node;
typedef struct{
	struct Node *top;
	int count; 
}LinkStack;
LinkStack L;
LinkStack S;
vector<char> v;
bool InitStack(LinkStack *s){
	s->top=(Node*)malloc(sizeof(Node));
	if(s==NULL)
	return false;
	else{
	  s->top=NULL;
	  s->count=0;
	  return true;	
	} 
}
bool StackEmpty(LinkStack *s){
	if(s->top==NULL)
	return true;
	else
	return false;
}
bool Push(LinkStack *s,char x){
	if(s==NULL)
	return false;
	else{
	Node* p=(Node*)malloc(sizeof(Node));
	p->data=x;
	p->next=s->top;
	s->top=p;
	s->count++;
	return true;	
	}
}
bool Pop(LinkStack *s,char &x){
	if(s->top==NULL)
	return false;
	else{
	Node *p=(Node*)malloc(sizeof(Node));
	p=s->top;
	s->top=s->top->next;
	s->count--;
	x=p->data;
	free(p);
	return true; 	
	} 
}
bool PrintStack(LinkStack *S){
	if(S->top==NULL){
	printf("该链栈为空!\n");
	return false; 
	}else{
	Node *p=(Node*)malloc(sizeof(Node));
	p=S->top;
	printf("栈顶-->");
	while(p!=NULL){
		printf("%c ",p->data);
		p=p->next;
	}
	printf("\n");
	return true;	
	}
}
void printStr(char str[]){
	printf("该链栈的元素如下:\n");
	for(int i=0;i<6;i++){
		printf("%c ",str[i]);
	}
	printf("\n");
}
bool isOp(char s){
	if(s=='-'||s=='+'||s=='*'||s=='/'||s=='('||s==')')
	return true;
	else
	return false;
}
bool Catch(LinkStack *s,char x){
	if(s->top->data=='('&&x==')')
	return true;
	else
	return false;
}
int  StackPriority(char y){
	switch(y){
		case '(':
			return 6;
		case ')':
			return 1;	
		case '*':
			return 5;
		case '/': 
			return 5;
		case '+':
			return 4;	
		case '-':
			return 4;	
	}
}
void ChangeStr(char a){		
	//对于可识别的运算符将其入符号栈
	if(isOp(a)){
		if(L.top==NULL){
		 //当符号栈为空时直接插入
		Push(&L,a);
		}else if(StackPriority(a)<=StackPriority(L.top->data)&&a!='('){
		//插入的字符优先级较符号栈的栈顶元素的优先级低 将栈顶元素弹出,若操作数栈的元素不为空且大于等于则进行两次出栈操作 
		//注意还有一点就是对于括号的运算要区别对待 		
       if(S.count>=2){ 
		char x,y;
		Pop(&S,x);	
		Pop(&S,y);
		v.push_back(y);	
		v.push_back(x);	
		}
		char y;
		Pop(&L,y);
		if(y!='(')
		v.push_back(y);
		if(a!=')')	
		Push(&L,a);
		}else{//反之字符优先级不高直接进栈
		if(a!=')')//将右括号直接不进栈 
		Push(&L,a);	
		} 
	}else{//这里只设置两种可能,一个是符号,一个是操作数 所以将其入操作数栈 		
	    Push(&S,a);
	    if(S.count==2&&L.count==1){//S栈不空,且L栈只有一个符号时,弹出所有操作数 
		    char x,y;
		    Pop(&S,x);
		    Pop(&S,y);
		    v.push_back(y);	
		    v.push_back(x);
	 }  		
	}
	//A+B*(C-D)-E/F# 其结果为    ABCD-*+EF/-或者 ABCD-*EF/-+  A+B*C-D-E/F#
}
void stackChange(){
	while(S.top!=NULL||L.top!=NULL){
		if(S.count>=2){
			char x,y;
		    Pop(&S,x);
		    Pop(&S,y);
		    v.push_back(y);	
		    v.push_back(x);
		    
		}
		if(L.top!=NULL){
			char x;
			Pop(&L,x);
		    v.push_back(x);
		}
		
	}
}
int main(){
	//全局建立两个栈,一个是符号栈一个是操作数栈。
	//进行对栈的初始化
	InitStack(&L);//符号栈 
    InitStack(&S);//操作数栈 
    printf("请您输入您想转化的中缀表达式:(以输入#号结束)\n");
    char str;
    scanf("%c",&str);
    while(str!='#'){
     ChangeStr(str);
     scanf("%c",&str);
	}
	stackChange();
	printf("打印已经完成的后缀表达式的链栈!显示如下:\n");
	for(auto it=v.begin();it!=v.end();it++){
		printf("%c",*it);
	}
	return 0; 
}

代码运行截图:
在这里插入图片描述

上述的代码初步实现了计算所具有的功能,但是该代码并具有普遍性,所以借鉴了网上的大佬的算法重新写出较为准确的代码,如下:
代码:

#include<iostream> 
#include<cstring>
#include<cstdlib>
using namespace std;
#define size 50
typedef struct LinkNode{
	char data;
	struct LinkNode* next;	
}*LiStack;
bool InitStack(LiStack &S){
	S=NULL;
	return true;
}
bool StackEmpty(LiStack S){
	return S==NULL;
}
bool Push(LiStack  &S,char x){
	LinkNode *s=(LinkNode*)malloc(sizeof(LinkNode));
	if(s==NULL){
		return false;
	}
	s->data=x;
	s->next=S;
	S=s;
	return false; 
}
bool Pop(LiStack &S,char &x){
	if(S==NULL){
		return false;
	}
	x=S->data;
	LinkNode *p=S;
	S=S->next;
	free(p);
	return true; 
}
int main(){
	char temp,a[size],b[size];
    printf("请输入你要转换的中缀表达式\n");
	scanf("%s",&a);
	LiStack S;
	InitStack(S);
	int i,j,length=strlen(a);
	for(int i=j=0;i<length;i++){
		if(a[i]>=48&&a[i]<=57){
			b[j++]=a[i];
		}else if(a[i]=='('){
			Push(S,a[i]);
		}else if(a[i]==')'){
			while(StackEmpty(S)==0){
				Pop(S,temp);
				if(temp=='(')
				break;
				b[j++]=temp;
			}
		}else switch(a[i]){
			case '*':case '/':{
				while(StackEmpty(S)==0){
					Pop(S,temp);
					if(temp=='/'||temp=='*'){
						b[j++]=temp;
					}
					else if(temp=='('||temp=='-'||temp=='+'){
						Push(S,temp);
						break;
					}
				}
				Push(S,a[i]);
				break;
			}
		    case '-':case '+':{
		    	while(StackEmpty(S)==0){
		    		Pop(S,temp);
		    		if(temp=='('){
		    			Push(S,temp);
		    			break;
					}
					else if(temp=='/'||temp=='*'||temp=='+'||temp=='-'){
						b[j++]=temp;
					}
				}
				Push(S,a[i]);
				break;
			}
		}
	}
	while(StackEmpty(S)==0){
		Pop(S,temp);
		b[j++]=temp;		
	}
	for(i=0;i<j;i++){
		printf("%c",b[i]);
	}
	printf("\n");
	return 0;
}
 

它的代码直接将链栈简化为一个链表,操作起来很方便,但是基本原理和我的差不多。最后的结果和我的有些许不同,但是它的输入是数字不是字符。

代码截图:
在这里插入图片描述
上述两种方法已经实现了王道视频中所说的两个案例两种不同的结果,只是一个输入的是字符一个输入的是数字。

最后又查阅几种不同的方法。
最后让人比较满意的代码综合:
代码:

#include<iostream>
#include<stack>
#include<string>
using namespace std;
string priority="#(+-*/)";
int locate(char c,string str){
	int locate=str.find(c);
	if(locate==2||locate==3){
		return 2;
	}else{
		if(locate==4||locate==5){
			return 4;
		}else{
			return locate;
		}
	}
}
string MidToBack(string str){
	string temp="";
	stack<char> s;
	s.push('#');
	for(int i=0;i<str.length();i++){
		switch(str[i]){
			case '(':
				s.push('(');
				break;
			case ')':
				while(!s.empty()&&s.top()!='('){
					temp=temp+s.top();
					s.pop();
				}
				s.pop();
				break;
			default:
				if(isalpha(str[i])){
					temp=temp+str[i];
				}
				else{
					if((s.top()=='(')||(s.top()!='('&&locate(str[i],priority)>locate(s.top(),priority))){
						s.push(str[i]);
					}else{
						while(locate(str[i],priority)<=locate(s.top(),priority)){
							temp=temp+s.top();
							s.pop();
						}
						s.push(str[i]);
					}
					break;
				}
		}
	}
	while(!s.empty()){
		temp=temp+s.top();
		s.pop();
	}
	return temp;
}
int main(){
	string str;
	cin>>str;
	cout<<MidToBack(str)<<endl;
	system("pause");
	return 0; 
}

这个就很c++,就非常完美!
代码截图:
在这里插入图片描述

后缀表达式的计算(手算)

后缀表达式的手算方法
从左往右扫描,每遇到一个运算符,就让运算符前面最近的两个操作数执行对应运算,合体为操作数

后缀表达式的计算(机算)
用栈实现后缀表达式的计算:
①、从左到右扫描到操作数则压入栈,直到处理完所有元素
②、若扫描到操作数则压入栈,并回到①;否则执行③
③、若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①
注意:先出栈的是”右操作数“

后缀表达式的计算:
以先前的c++的代码为例来计算后缀表达式。
代码:

#include<iostream>
#include<stack>
#include<string>
using namespace std;
string priority="#(+-*/)";//1+2*(3-4)+6/8
int locate(char c,string str){
	int locate=str.find(c);
	if(locate==2||locate==3){
		return 2;
	}else{
		if(locate==4||locate==5){
			return 4;
		}else{
			return locate;
		}
	}
}
string MidToBack(string str){
	string temp="";
	stack<char> s;
	s.push('#');
	for(int i=0;i<str.length();i++){
		switch(str[i]){
			case '(':
				s.push('(');
				break;
			case ')':
				while(!s.empty()&&s.top()!='('){
					temp=temp+s.top();
					s.pop();
				}
				s.pop();
				break;
			default:
				if(isalpha(str[i])||isdigit(str[i])){
					temp=temp+str[i];
				}
				else{
					if((s.top()=='(')||(s.top()!='('&&locate(str[i],priority)>locate(s.top(),priority))){
						s.push(str[i]);
					}else{
						while(locate(str[i],priority)<=locate(s.top(),priority)){
							temp=temp+s.top();
							s.pop();
						}
						s.push(str[i]);
					}
					break;
				}
		}
	}
	while(!s.empty()){
		temp=temp+s.top();
		s.pop();
	}
	return temp;
}
float calculate(string str){
	stack<float> s;
	for(int i=0;i<str.length()-1;i++){
		if(isdigit(str[i])){
			float t=float(str[i]-'0');
			s.push(t);
		}else{
			float x,y;
			x=s.top();
			s.pop();
			y=s.top();
			s.pop();
			float r;
			switch(str[i]){
				case '+':
					r=y+x;
					break;
				case '-':
					r=y-x;
					break;
				case '*':
					r=y*x;
					break;
				case '/':
					r=y/x;
					break;
				default:
					break;
			}
			s.push(r);
		}
	}
	return s.top();
}
int main(){
	string str;
	cin>>str;
	printf("中缀转后缀表达式的结果如下\n");
	string s=MidToBack(str);
	cout<<s<<endl;	
	printf("计算结果如下\n");
	printf("%.6f",calculate(s));
	return 0; 
}

代码运行截图:
在这里插入图片描述

中缀转前缀的手算方法:
①、确定中缀表达式中各个运算符的运算顺序
②、选择下一个运算符,按照【运算符 左操作数 右操作数】的方式组合成一个新的操作数
③、如果还有运算符没被处理,就继续②
右优先原则:只要右边的运算符能先运算,就优先算右边的
依旧以原来的代码为例,研究中缀转前缀的效果:
代码:

#include<iostream>
#include<stack>
#include<string>
using namespace std;
string priority="#(+-*/)";//1+2*(3-4)+6/8 8/6+)4-3(*2+1
string change(string  s){
	string   str="";
	for(int i=s.length()-2;i>=0;i--){
		str+=s[i];
	}
	return str;
}
int locate(char c,string str){
	int locate=str.find(c);
	if(locate==2||locate==3){
		return 2;
	}else{
		if(locate==4||locate==5){
			return 4;
		}else{
			return locate;
		}
	}
}
int locate1(char c,string str){
	int locate=str.find(c);
	if(locate==2||locate==3){
		return 2;
	}else{
		if(locate==4||locate==5){
			return 4;
		}else if(locate==0){
			return 0;
		}else{
			return (6-locate);
		}
	}
}
string MidToBack(string str){
	string temp="";
	stack<char> s;
	s.push('#');
	for(int i=0;i<str.length();i++){
		switch(str[i]){
			case '(':
				s.push('(');
				break;
			case ')':
				while(!s.empty()&&s.top()!='('){
					temp=temp+s.top();
					s.pop();
				}
				s.pop();
				break;
			default:
				if(isalpha(str[i])||isdigit(str[i])){
					temp=temp+str[i];
				}
				else{
					if((s.top()=='(')||(s.top()!='('&&locate(str[i],priority)>locate(s.top(),priority))){
						s.push(str[i]);
					}else{
						while(locate(str[i],priority)<=locate(s.top(),priority)){
							temp=temp+s.top();
							s.pop();
						}
						s.push(str[i]);
					}
					break;
				}
		}
	}
	while(!s.empty()){
		temp=temp+s.top();
		s.pop();
	}
	return temp;
}
string MidToPre(string str){
	string temp="";
	stack<char> s;
	s.push('#');
	for(int i=str.length()-1;i>=0;i--){
		switch(str[i]){
			case ')':
				s.push(')');
				break;
			case '(':
				while(!s.empty()&&s.top()!=')'){
					temp=temp+s.top();
					s.pop();
				}
				s.pop();
				break;
			default:
				if(isalpha(str[i])||isdigit(str[i])){
					temp=temp+str[i];
				}
				else{
					if((s.top()==')')||(s.top()!=')'&&locate1(str[i],priority)>locate1(s.top(),priority))){
						s.push(str[i]);
					}else{
						while(locate1(str[i],priority)<=locate1(s.top(),priority)){
							temp=temp+s.top();
							s.pop();
						}
						s.push(str[i]);
					}
					break;
				}
		}
	}
	while(!s.empty()){
		temp=temp+s.top();
		s.pop();
	}
	return change(temp);
}
double calculate(string str){
	stack<double> s;
	for(int i=0;i<str.length()-1;i++){
		if(isdigit(str[i])){
			double t=double(str[i]-'0');
			s.push(t);
		}else{
			double x,y;
			x=s.top();
			s.pop();
			y=s.top();
			s.pop();
			double r;
			switch(str[i]){
				case '+':
					r=y+x;
					break;
				case '-':
					r=y-x;
					break;
				case '*':
					r=y*x;
					break;
				case '/':
					r=y/x;
					break;
				default:
					break;
			}
			s.push(r);
		}
	}
	return s.top();
}
int main(){
	string str;
	cin>>str;
//	printf("中缀转后缀表达式的结果如下\n");
//	string s=MidToBack(str);
//	cout<<s<<endl;	
	printf("中缀转前缀表达式的结果如下\n");
	string t=MidToPre(str);
	cout<<t<<endl;
//	printf("计算结果如下\n");
//	printf("%.6f",calculate(s));
	return 0; 
}

代码运行截图:
在这里插入图片描述
再用传统的自己定义链栈的方式来运行一遍。
代码:

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cctype>
using namespace std;
typedef struct LNode{
	struct LNode* next;
	char data;
}*LinkStack;
string priority="#(+-*/)";
string change(string  s){
	string   str="";
	for(int i=s.length()-2;i>=0;i--){
		str+=s[i];
	}
	return str;
}
int Prior(char a,string str){
	int locate=str.find(a);
	if(locate==2||locate==3){
		return 2;
	}else if(locate==4||locate==5){
		return 4;
	}else if(locate==1||locate==6){
		return  (7-locate);
	}else{
		return locate;
	}
}
bool InitStack(LinkStack &S){
	S=NULL;
	return true;
}
bool StackEmpty(LinkStack S){
	if(S==NULL){
		return true;
	}else{
		return false;
	}
}
bool Pop(LinkStack &S){
	if(StackEmpty(S)){
		return false;
	}else{
		LNode* p=S;
		S=S->next;
		free(p);
		return true;
	}
}
bool Push(LinkStack &S,char a){
	LNode* p=(LNode*)malloc(sizeof(LNode));
	p->data=a;
	p->next=S;
	S=p;
	return true;
}
string  MidToPre(string str){
	string temp="";
	LinkStack T;//符号栈 
	InitStack(T);
    LinkStack S;//数栈 
	InitStack(S);
	Push(T,'#');
	for(int i=str.length()-1;i>=0;i--){
		if(isdigit(str[i])||isalpha(str[i])){
			Push(S,str[i]);
		}else{
			if(((Prior(str[i],priority)>Prior(S->data,priority))&&str[i]!=')')||str[i]==')'){
				Push(T,str[i]);
			}else if(str[i]=='('){
			while(!StackEmpty(S)&&S->data!=')'&&(Prior(str[i],priority)<=Prior(S->data,priority))){					
					char x,y;
					y=S->data;
					Pop(S);
					x=S->data;
					Pop(S);
					temp+=y;
					temp+=x;
					char j;
				    j=T->data;
				    Pop(T);
				    temp+=j; 					 
				}
				Pop(T);
			}else if((Prior(str[i],priority)<=Prior(S->data,priority))&&str[i]!='('){
				if(!StackEmpty(S)){					
					char x,y;
					y=S->data;
					Pop(S);
					x=S->data;
					Pop(S);
					temp+=y;
					temp+=x;
					char j;
				    j=T->data;
				    Pop(T);
				    temp+=j; 					 
				}
				
			}	
		}
	}
	while(!StackEmpty(S)&&!StackEmpty(T)){
	                char x,y;
					y=S->data;
					Pop(S);
					x=S->data;
					Pop(S);
					temp+=y;
					temp+=x;
					char j;
				    j=T->data;
				    Pop(T);
				    temp+=j; 	
	}
	while(!StackEmpty(T)&&StackEmpty(S)){
		            char j;
				    j=T->data;
				    Pop(T);
				    temp+=j; 
	}
}
int main(){
	string  str;
	printf("请输入你想要转换的中缀表达式\n");
	cin>>str;
	string y=MidToPre(str);
	string result=change(y);
	printf("你的前缀表达式已经转换如下\n");
	cout<<result<<endl;
	return 0;
}

代码运行崩溃:
在这里插入图片描述
后来通过检查初始化栈的各种操作代码,发现没有问题就是跑崩溃了:
代码:

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cctype>
using namespace std;
typedef struct LNode{
	struct LNode* next;
	char data;
}*LinkStack;
string priority="#(+-*/)";
string change(string  s){
	string   str="";
	for(int i=s.length()-2;i>=0;i--){
		str+=s[i];
	}
	return str;
}
int Prior(char a,string str){
	int locate=str.find(a);
	if(locate==2||locate==3){
		return 2;
	}else if(locate==4||locate==5){
		return 4;
	}else if(locate==1||locate==6){
		return  (7-locate);
	}else{
		return locate;
	}
}
bool InitStack(LinkStack &S){
	S=NULL;
	return true;
}
bool StackEmpty(LinkStack S){
	if(S==NULL){
		return true;
	}else{
		return false;
	}
}
bool Pop(LinkStack &S){
	if(StackEmpty(S)){
		return false;
	}else{
		LNode* p=S;
		S=S->next;
		free(p);
		return true;
	}
}
bool Push(LinkStack &S,char a){
	LNode* p=(LNode*)malloc(sizeof(LNode));
	p->data=a;
	p->next=S;
	S=p;
	return true;
}
string  MidToPre(string str){
	string temp="";
	LinkStack T;//符号栈 
	InitStack(T);
    LinkStack S;//数栈 
	InitStack(S);
	Push(T,'#');
	for(int i=str.length()-1;i>=0;i--){
		if(isdigit(str[i])||isalpha(str[i])){
			Push(S,str[i]);
		}else{
			if(((Prior(str[i],priority)>Prior(S->data,priority))&&str[i]!=')')||str[i]==')'){
				Push(T,str[i]);
			}else if(str[i]=='('){
			while(!StackEmpty(S)&&S->data!=')'&&(Prior(str[i],priority)<=Prior(S->data,priority))){					
					char x,y;
					y=S->data;
					Pop(S);
					x=S->data;
					Pop(S);
					temp+=y;
					temp+=x;
					char j;
				    j=T->data;
				    Pop(T);
				    temp+=j; 					 
				}
				Pop(T);
			}else if((Prior(str[i],priority)<=Prior(S->data,priority))&&str[i]!='('){
				if(!StackEmpty(S)){					
					char x,y;
					y=S->data;
					Pop(S);
					x=S->data;
					Pop(S);
					temp+=y;
					temp+=x;
					char j;
				    j=T->data;
				    Pop(T);
				    temp+=j; 					 
				}
				
			}	
		}
	}
	while(!StackEmpty(S)&&!StackEmpty(T)){
	                char x,y;
					y=S->data;
					Pop(S);
					x=S->data;
					Pop(S);
					temp+=y;
					temp+=x;
					char j;
				    j=T->data;
				    Pop(T);
				    temp+=j; 	
	}
	while(!StackEmpty(T)&&StackEmpty(S)){
		            char j;
				    j=T->data;
				    Pop(T);
				    temp+=j; 
	}
}
void PrintStack(LinkStack S){
	while(S!=NULL){
		printf("%c",S->data);
		Pop(S);
	}
	printf("\n");
} 
int main(){
//	string  str;
//	printf("请输入你想要转换的中缀表达式\n");
//	cin>>str;
//	string y=MidToPre(str);
//	string result=change(y);
//	printf("你的前缀表达式已经转换如下\n");
//	cout<<result<<endl;
    LinkStack S;
	InitStack(S);
	Push(S,'1');
	Push(S,'2');
	Push(S,'3');
	Push(S,'4');
	PrintStack(S);
    Pop(S);
	PrintStack(S); 
	return 0;
}

代码运行截图:
在这里插入图片描述
可能是资源空间不足吧,于是就改进了上述其中的代码:

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cctype>
#define size 50
using namespace std;
typedef struct LNode{
	struct LNode* next;
	char data;
}*LinkStack;
bool InitStack(LinkStack &S){
	S=NULL;
	return true;
}
bool StackEmpty(LinkStack S){
	if(S==NULL){
		return true;
	}else{
		return false;
	}
}
bool Pop(LinkStack &S,char &x){
	if(StackEmpty(S)){
		return false;
	}else{
		LNode* p=S;
		x=S->data;
		S=S->next;
		free(p);
		return true;
	}
}
bool Push(LinkStack &S,char a){
	LNode* p=(LNode*)malloc(sizeof(LNode));
	p->data=a;
	p->next=S;
	S=p;
	return true;
}

int main(){
char temp,a[size],b[size];
    printf("请输入你要转换的中缀表达式\n");
	scanf("%s",&a);
	LinkStack S;
	InitStack(S);
	int i,j=0,length=strlen(a);
	for(int i=length-1;i>=0;i--){
		if(isdigit(a[i])||isalpha(a[i])){
			b[j++]=a[i];
		}else if(a[i]==')'){
			Push(S,a[i]);
		}else if(a[i]=='('){
			while(StackEmpty(S)==0){
				Pop(S,temp);
				if(temp==')')
				break;
				b[j++]=temp;
			}
		}else switch(a[i]){
			case '*':case '/':{
				while(StackEmpty(S)==0){
					Pop(S,temp);
					if(temp=='/'||temp=='*'){
						b[j++]=temp;
					}
					else if(temp==')'||temp=='-'||temp=='+'){
						Push(S,temp);
						break;
					}
				}
				Push(S,a[i]);
				break;
			}
		    case '-':case '+':{
		    	while(StackEmpty(S)==0){
		    		Pop(S,temp);
		    		if(temp==')'){
		    			Push(S,temp);
		    			break;
					}
					else if(temp=='/'||temp=='*'||temp=='+'||temp=='-'){
						b[j++]=temp;
					}
				}
				Push(S,a[i]);
				break;
			}
		}
	}
	while(StackEmpty(S)==0){
		Pop(S,temp);
		b[j++]=temp;		
	}
	for(int i=j-1;i>=0;i--){
		printf("%c",b[i]);
	}
	printf("\n");
	return 0;
}

代码运行截图:
在这里插入图片描述
前缀表达式的计算:
用栈实现前缀表达式的计算:
①、从右往左扫描下一个元素,直到处理完所有元素
②、若扫描到操作数则压入栈,并回到①;否则执行③
③、若扫描运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①
代码:

#include<iostream>
#include<stack>
#include<string>
using namespace std;
string priority="#(+-*/)";//1+2*(3-4)+6/8 8/6+)4-3(*2+1
string change(string  s){
	string   str="";
	for(int i=s.length()-2;i>=0;i--){
		str+=s[i];
	}
	return str;
}
int locate(char c,string str){
	int locate=str.find(c);
	if(locate==2||locate==3){
		return 2;
	}else{
		if(locate==4||locate==5){
			return 4;
		}else{
			return locate;
		}
	}
}
int locate1(char c,string str){
	int locate=str.find(c);
	if(locate==2||locate==3){
		return 2;
	}else{
		if(locate==4||locate==5){
			return 4;
		}else if(locate==0){
			return 0;
		}else{
			return (6-locate);
		}
	}
}
string MidToBack(string str){
	string temp="";
	stack<char> s;
	s.push('#');
	for(int i=0;i<str.length();i++){
		switch(str[i]){
			case '(':
				s.push('(');
				break;
			case ')':
				while(!s.empty()&&s.top()!='('){
					temp=temp+s.top();
					s.pop();
				}
				s.pop();
				break;
			default:
				if(isalpha(str[i])||isdigit(str[i])){
					temp=temp+str[i];
				}
				else{
					if((s.top()=='(')||(s.top()!='('&&locate(str[i],priority)>locate(s.top(),priority))){
						s.push(str[i]);
					}else{
						while(locate(str[i],priority)<=locate(s.top(),priority)){
							temp=temp+s.top();
							s.pop();
						}
						s.push(str[i]);
					}
					break;
				}
		}
	}
	while(!s.empty()){
		temp=temp+s.top();
		s.pop();
	}
	return temp;
}
string MidToPre(string str){
	string temp="";
	stack<char> s;
	s.push('#');
	for(int i=str.length()-1;i>=0;i--){
		switch(str[i]){
			case ')':
				s.push(')');
				break;
			case '(':
				while(!s.empty()&&s.top()!=')'){
					temp=temp+s.top();
					s.pop();
				}
				s.pop();
				break;
			default:
				if(isalpha(str[i])||isdigit(str[i])){
					temp=temp+str[i];
				}
				else{
					if((s.top()==')')||(s.top()!=')'&&locate1(str[i],priority)>locate1(s.top(),priority))){
						s.push(str[i]);
					}else{
						while(locate1(str[i],priority)<=locate1(s.top(),priority)){
							temp=temp+s.top();
							s.pop();
						}
						s.push(str[i]);
					}
					break;
				}
		}
	}
	while(!s.empty()){
		temp=temp+s.top();
		s.pop();
	}
	return change(temp);
}
double calculate(string str){
	stack<double> s;
	for(int i=0;i<str.length()-1;i++){
		if(isdigit(str[i])){
			double t=double(str[i]-'0');
			s.push(t);
		}else{
			double x,y;
			x=s.top();
			s.pop();
			y=s.top();
			s.pop();
			double r;
			switch(str[i]){
				case '+':
					r=y+x;
					break;
				case '-':
					r=y-x;
					break;
				case '*':
					r=y*x;
					break;
				case '/':
					r=y/x;
					break;
				default:
					break;
			}
			s.push(r);
		}
	}
	return s.top();
}
double calculate2(string str){
	stack<double> s;
	for(int i=str.length()-1;i>=0;i--){
		if(isdigit(str[i])){
			double t=double(str[i]-'0');
			s.push(t);
		}else{
			double x,y;
			y=s.top();
			s.pop();
			x=s.top();
			s.pop();
			double r;
			switch(str[i]){
				case '+':
					r=y+x;
					break;
				case '-':
					r=y-x;
					break;
				case '*':
					r=y*x;
					break;
				case '/':
					r=y/x;
					break;
				default:
					break;
			}
			s.push(r);
		}
	}
	return s.top();
}
int main(){
	string str;
	cin>>str;
//	printf("中缀转后缀表达式的结果如下\n");
//	string s=MidToBack(str);
//	cout<<s<<endl;	
//	printf("计算结果如下\n");
//	printf("%.6f",calculate(s));
	printf("中缀转前缀表达式的结果如下\n");
	string t=MidToPre(str);
	cout<<t<<endl;
	printf("计算结果如下\n");
	printf("%.6f",calculate2(t));
	return 0; 
}

代码运行截图;
在这里插入图片描述

栈的应用:
函数调用的特点:最后被调用的函数最先执行结束(LIFO)
函数调用时,需要用一个”函数调用栈“存储:
①、调用返回地址
②、实参
③、局部变量

递归调用时,函数调用栈可称为”递归工作栈“
每进入一层递归,就将调用所需的信息压入栈顶
每退出一层递归,就从栈顶弹出相应的信息

队列的应用:
广度优先遍历、二叉树的层次遍历。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值