转换过程
下面的 输出 一词表示将该值存起来(保存到要求的结果中),压栈 就是入栈的意思。
①遇到操作数直接输出
②遇到操作符:分以下几种情况
1.栈为空时: 无论操作符是什么,直接压栈
2.新的操作符优先级比栈顶的高: 直接压栈(优先级: 括号 > * / > + -)
3.新操作符是左括号: 直接压栈,且左括号只有在有右括号操作符时才出栈。
4.新操作符是右括号: 右括号不入栈,从栈顶开始出栈并输出,直到碰到左括号才停下来(包括左括号也出栈,但是不输出),且一个右括号只能对应一个左括号,不能多出。
5.新的操作符优先级比栈顶的低或者相同: 栈顶开始出栈并输出,直到出现比新操作符优先级低的位置停止(不包括该位置)或者栈空了,或者遇到了左括号停止。
③最后将栈的数据一次输出。
本人思路
这过程中有一个重要的概念就是优先级,所以我定义了操作符的优先级数据:
设操作数优先级为-1, + - 优先级为 0, * / 优先级为 1,左括号优先级为2, 右括号优先级为3(因为右括号优先级如果也为2的话,就没法区分是左括号还是右括号,这是个有影响的),函数如下:
// 返回优先级(+-为0,*/为1, ()为2, 3)
int retPriority(char c)
{
int temp = -1;
switch (c)
{
case '+': case '-':
temp = 0;
break;
case '*': case '/':
temp = 1;
break;
case '(':
temp = 2;
break;
case ')':
temp = 3;
break;
default:
temp = -1;
}
return temp;
}
然后有了优先级之后,直接按照上面的转换过程,直接撸代码就哦了。
准备下基本数据
1.先准备下结点的结构。
struct ListNode
{
char data;
ListNode* next;
};
我下面的操作都是带头结点的链表。
2.1栈的基本操作(初始化)
// 初始化 栈
void InitStack(ListNode* &head)
{
ListNode* start = new ListNode; // 创建一个头结点
start->data = '#';
start->next = nullptr;
head = start;
}
2.2栈的基本操作(入栈)
// 入栈(带头结点的链表)
bool PushStack(ListNode* &head, char &data)
{
if (!head) return false;
ListNode* temp = new ListNode;
temp->data = data;
temp->next = head->next;
head->next = temp;
return true;
}
2.3栈的基本操作(出栈)
// 出栈(带头结点的链表)
bool PopStack(ListNode* &head, char &data)
{
if (!head || !head->next) return false;
ListNode *p = head->next;
data = p->data;
head->next = p->next;
delete p;
return true;
}
2.4栈的基本操作(是否为空)
// 栈是否为空
bool isEmpty(ListNode* &head)
{
if(!head->next) return true;
return false;
}
2.5栈的基本操作(查看栈顶值)
// 查看栈顶值
bool TopStack(ListNode* &head, char &data)
{
if (!head || !head->next) return false;
data = head->next->data;
return true;
}
2.6链表的销毁
// 销毁链表
void DestroyListNode(ListNode* &L)
{
if (!L) return;
ListNode* pNext = L->next;
while (pNext != nullptr)
{
delete L;
L = pNext;
pNext = L->next;
}
delete L;
L = nullptr;
}
3.main函数的测试数据
int main()
{
/* *****************************************************************
* 构造一个 链表
*/
cout << "请输入中缀表达式(结尾加$):"; // $作为输入的结束符号,不会保存或参与运算
char tmp;
ListNode* head = new ListNode;
head->data = '#';
head->next = nullptr; // 这个是保存 中缀表达式 链表的头结点
ListNode* p = head;
while (cin >> tmp && tmp != '$')
{
ListNode* ac = new ListNode;
ac->data = tmp;
ac->next = nullptr;
p->next = ac;
p = p->next;
} // 把输入存入链表
vector<char> result; // 结果保存到这里
/*
* midToBehind 只是实现了表达式的处理例如:A * (B-C) + D
* 每一个操作数都是一个字符表示的,不能输入具体的超过9的具体值
* 比如: 12 + 123 * (45 - 2 * 63) 这种是不行的
*/
midToBehind(head, result); // 这里是我们要编写的转换函数
cout << "计算结果为:";
if (!result.empty())
{
for(const auto &c : result)
{
cout << c << " ";
}
} // 这里输出结果
return 0;
}
编写 midToBehind函数
/*
* midToBehind 只是实现了表达式的处理例如:A * (B-C) + D
* 每一个操作数都是一个字符表示的,不能输入具体的超过9的具体值
* 比如: 12 + 123 * (45 - 2 * 63) 这种是不行的
*/
下面的代码逻辑,就是上面的转换过程(注释写的比较详细):
// 中缀表达式转后缀表达式
bool midToBehind(ListNode* head, vector<char> &data)
{
if (!head || !head->next) return false;
int maxLength = -1; // 曾经用过的最大栈深度
char temp; // 临时存储栈顶元素的值
// 新建一个临时栈用来 保存操作符
ListNode* saveOperator = nullptr;
InitStack(saveOperator); // 初始化栈
ListNode *start = head->next;
while (start)
{
maxLength = maxLength < StackLength(saveOperator) ? StackLength(saveOperator) : maxLength;
if (retPriority(start->data) == -1) data.push_back(start->data); // 操作数直接输出
else if (!isEmpty(saveOperator)) // 栈是否为空
{
TopStack(saveOperator, temp); // 查看栈顶元素是啥
int priStarck = retPriority(temp); // 栈顶元素优先级
int newDataPri = retPriority(start->data); // 新来操作符的优先级
if (newDataPri > priStarck) // 新来的优先级高的话
{
if (newDataPri == 3) // 看是不是右括号来了,如果是,出栈一直出到遇到第一个左括号
{
bool isRun = true;
while (isRun)
{
if (retPriority(temp) == 2) isRun = false; // 遇到了第一个左括号
PopStack(saveOperator, temp);
if (temp != '(') data.push_back(temp); // 出栈并输出,左括号也出栈,但是不输出
TopStack(saveOperator, temp); // 重新查看栈顶元素
}
}
else PushStack(saveOperator, start->data); // 如果新来的不是右括号直接压栈
}
else if (newDataPri == priStarck) // 如果和栈顶拥有相同的优先级
{
// 优先级相同,看是不是左括号(左括号比较特殊,因为上面还有左括号的话是不出栈的,但是假如新来的操作符是+,栈顶也是+,就得出栈)
if (newDataPri == 2) PushStack(saveOperator, start->data); // 是的话,直接压栈
else
{
PopStack(saveOperator, temp);
data.push_back(temp);
PushStack(saveOperator, start->data);
} // 不是,就栈顶出栈, 新操作符压栈
}
else // 新来的优先级小,这个要一直出栈,出到更小的优先级或者空栈为止
{
if (priStarck != 2) // 看栈顶是不是左括号,不是的话才出栈,是的话就直接入栈
{
while (1) // 一直出栈,出到更小的优先级或者空栈为止
{
// 如果栈顶优先级小了、栈空了,栈顶是左括号(因为左括号必须有右括号才能出栈)则停止出栈
if (newDataPri > retPriority(temp) || isEmpty(saveOperator) || retPriority(temp) == 2) break;
PopStack(saveOperator, temp);
data.push_back(temp);
if (!TopStack(saveOperator, temp)) break; // 如果栈顶没有值了就表示,栈空了。
}
}
PushStack(saveOperator, start->data); // 最后把别人弹出去了,不能忘了把新操作符自己压进去
}
}
// 栈为空时,直接入栈
else PushStack(saveOperator, start->data);
start = start->next;
}
// 最后把栈里面的数据都弹出来
while (!isEmpty(saveOperator))
{
PopStack(saveOperator, temp);
data.push_back(temp);
if (!TopStack(saveOperator, temp)) break;
}
DestroyListNode(saveOperator); // 清除栈
cout << "曾经用过最大的栈的深度为:" << maxLength << endl;
return true;
}
完整代码
下面这是直接可以跑的C++完整代码:
#include <vector>
#include <iostream>
using namespace std;
struct ListNode
{
char data;
ListNode* next;
};
// 栈是否为空
bool isEmpty(ListNode* &head)
{
if(!head->next) return true;
return false;
}
// 初始化 栈
void InitStack(ListNode* &head)
{
ListNode* start = new ListNode; // 创建一个头结点
start->data = '#';
start->next = nullptr;
head = start;
}
// 压栈(带头结点)
bool PushStack(ListNode* &head, char &data)
{
if (!head) return false;
ListNode* temp = new ListNode;
temp->data = data;
temp->next = head->next;
head->next = temp;
return true;
}
// 出栈(带头结点)
bool PopStack(ListNode* &head, char &data)
{
if (!head || !head->next) return false;
ListNode *p = head->next;
data = p->data;
head->next = p->next;
delete p;
return true;
}
// 查看栈顶值
bool TopStack(ListNode* &head, char &data)
{
if (!head || !head->next) return false;
data = head->next->data;
return true;
}
// 栈深度
int StackLength(ListNode* &head)
{
if (!head || !head->next) return -1;
int length = 0;
ListNode* start = head->next;
while (start)
{
length++;
start = start->next;
}
return length;
}
// 销毁链表
void DestroyListNode(ListNode* &L)
{
if (!L) return;
ListNode* pNext = L->next;
while (pNext != nullptr)
{
delete L;
L = pNext;
pNext = L->next;
}
delete L;
L = nullptr;
}
// 返回优先级(+-为0,*/为1, ()为2, 3)
int retPriority(char c)
{
int temp = -1;
switch (c)
{
case '+': case '-':
temp = 0;
break;
case '*': case '/':
temp = 1;
break;
case '(':
temp = 2;
break;
case ')':
temp = 3;
break;
default:
temp = -1;
}
return temp;
}
// 中缀表达式转后缀表达式
bool midToBehind(ListNode* head, vector<char> &data)
{
if (!head || !head->next) return false;
int maxLength = -1; // 曾经用过的最大栈深度
char temp; // 临时存储栈顶元素的值
// 新建一个临时栈用来 保存操作符
ListNode* saveOperator = nullptr;
InitStack(saveOperator); // 初始化栈
ListNode *start = head->next;
while (start)
{
maxLength = maxLength < StackLength(saveOperator) ? StackLength(saveOperator) : maxLength;
if (retPriority(start->data) == -1) data.push_back(start->data); // 操作数直接输出
else if (!isEmpty(saveOperator)) // 栈是否为空
{
TopStack(saveOperator, temp); // 查看栈顶元素是啥
int priStarck = retPriority(temp); // 栈顶元素优先级
int newDataPri = retPriority(start->data); // 新来操作符的优先级
if (newDataPri > priStarck) // 新来的优先级高的话
{
if (newDataPri == 3) // 看是不是右括号来了,如果是,出栈一直出到遇到第一个左括号
{
bool isRun = true;
while (isRun)
{
if (retPriority(temp) == 2) isRun = false; // 遇到了第一个左括号
PopStack(saveOperator, temp);
if (temp != '(') data.push_back(temp); // 出栈并输出,左括号也出栈,但是不输出
TopStack(saveOperator, temp); // 重新查看栈顶元素
}
}
else PushStack(saveOperator, start->data); // 如果新来的不是右括号直接压栈
}
else if (newDataPri == priStarck) // 如果和栈顶拥有相同的优先级
{
// 优先级相同,看是不是左括号(左括号比较特殊,因为上面还有左括号的话是不出栈的,但是假如新来的操作符是+,栈顶也是+,就得出栈)
if (newDataPri == 2) PushStack(saveOperator, start->data); // 是的话,直接压栈
else
{
PopStack(saveOperator, temp);
data.push_back(temp);
PushStack(saveOperator, start->data);
} // 不是,就栈顶出栈, 新操作符压栈
}
else // 新来的优先级小,这个要一直出栈,出到更小的优先级或者空栈为止
{
if (priStarck != 2) // 看栈顶是不是左括号,不是的话才出栈,是的话就直接入栈
{
while (1) // 一直出栈,出到更小的优先级或者空栈为止
{
// 如果栈顶优先级小了、栈空了,栈顶是左括号(因为左括号必须有右括号才能出栈)则停止出栈
if (newDataPri > retPriority(temp) || isEmpty(saveOperator) || retPriority(temp) == 2) break;
PopStack(saveOperator, temp);
data.push_back(temp);
if (!TopStack(saveOperator, temp)) break; // 如果栈顶没有值了就表示,栈空了。
}
}
PushStack(saveOperator, start->data); // 最后把别人弹出去了,不能忘了把新操作符自己压进去
}
}
// 栈为空时,直接入栈
else PushStack(saveOperator, start->data);
start = start->next;
}
// 最后把栈里面的数据都弹出来
while (!isEmpty(saveOperator))
{
PopStack(saveOperator, temp);
data.push_back(temp);
if (!TopStack(saveOperator, temp)) break;
}
DestroyListNode(saveOperator); // 清除栈
cout << "曾经用过最大的栈的深度为:" << maxLength << endl;
return true;
}
int main()
{
/* *****************************************************************
* 构造一个 链表
*/
cout << "请输入中缀表达式(结尾加$):"; // $作为输入的结束符号,不会保存或参与运算
char tmp;
ListNode* head = new ListNode;
head->data = '#';
head->next = nullptr; // 这个是保存原表达式链表的头结点
ListNode* p = head;
while (cin >> tmp && tmp != '$')
{
ListNode* ac = new ListNode;
ac->data = tmp;
ac->next = nullptr;
p->next = ac;
p = p->next;
} // 把输入存入 链表
// PrintListNode(head);
vector<char> result;
/*
* midToBehind 只是实现了表达式的处理例如:A * (B-C) + D
* 每一个操作数都是一个字符表示的,不能输入具体的超过9的具体值
* 比如: 12 + 123 * (45 - 2 * 63) 这种是不行的
*/
midToBehind(head, result); // 这里是我们要编写的转换函数
cout << "计算结果为:";
if (!result.empty())
{
for(const auto &c : result)
{
cout << c << " ";
}
} // 这里输出结果
return 0;
}