刷题计划——栈算法(三)

150. 逆波兰表达式求值(中等)

题目:

根据逆波兰表示法,求表达式的值。

有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:

  • 整数除法只保留整数部分;
  • 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:

输入: ["2", "1", "+", "3", "*"]
输出: 9
解释: ((2 + 1) * 3) = 9
示例 2:

输入: ["4", "13", "5", "/", "+"]
输出: 6
解释: (4 + (13 / 5)) = 6
示例 3:

输入: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"]
输出: 22
解释: 
  ((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

题目中给定的字符串为有效的逆波兰表达式, 在进行计算之前首先要解决两个问题:

  • 判断字符串是否为数字字符串,若为数字字符串,将其转化为数字,压入数字栈
  • 若字符串不为数字字符串,则从数字栈中弹出两个元素,进行计算后将结果重新压入栈中

通过分析之后,将主要解决问题划分为3个子问题进行处理:

  1. 判断字符串是否为数字
    此问题不能简单的通过调用isalnum来判断,因为一旦出现两位数“10”,或者负数“-1”,将不能判断
  2. 数字字符串转为int类型数字
  3. 计算

在解决这些问题后,就可以进行对逆波兰表达式的计算过程

class Solution {
public:
	//	判断是否为数字字符串
    bool isNum(string &str){
        for(auto ch: str){
            if(isalnum(ch)){
                return true;
            }
        }

        return false;
    }

	//	数字字符串转int数字
    int strToNum(string &str){
        int res = 0;
        bool negative = false;
        if(str[0] == '-'){
            negative = true;
            for(int i = 1; i < str.size(); i++){
                res = res*10 + str[i]-'0';
            }
        }
        else{
            for(int i = 0; i < str.size(); i++){
                res = res*10 + str[i]-'0';
            }
        }

        return negative? -res: res;
    }

	//	计算
    int caculate(int &a, int &b, char operat){
        int res = 0;
        switch(operat){
        	//	注意,由于除法中除数与被除数的位置关系,将从数字栈中弹出的第一个元素作为被除数
        	//	第二个元素为除数, 其它运算同样遵循此法则
            case'+': res = b + a; break;
            case'-': res = b - a; break;
            case'*': res = b * a; break;
            case'/': res = b / a; break;
            default: break;   
        }

        return res;
    }

    int evalRPN(vector<string>& tokens) {
        int target = 0;
        if(tokens.empty()){
            return target;
        }

        stack<int> st;

        for(int i = 0; i < tokens.size(); i++){
            if(isNum(tokens[i])){
                st.push(strToNum(tokens[i]));
            }
            else{
                int a = st.top(); st.pop();
                int b = st.top(); st.pop();
                st.push(caculate(a, b, tokens[i][0]));
            }
        }

        return st.top();
    }
};

144. 二叉树的前序遍历(中等)

题目:

给定一个二叉树,返回它的 前序 遍历。

 示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [1,2,3]

作为一道基础的二叉树问题,递归的解法自然不同多说。

从题目本质的性质来说,二叉树的前序遍历,中序遍历以及后序遍历都属于深度优先搜索算法的一种变形形式,正因如此,才要一定使用的结构来存储父节点的数据,以此达到类似于路径搜索的目的。

另外,前序遍历的顺序为根->左子树->右子树,这就意味着栈首先压入根节点,再压入左子树节点,再压入右子树节点。

至此,迭代方式来完成二叉树的前序遍历的准备工作已经完成。

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;
        while(root || st.size()){
            while(root){
            	//	节点非空,将节点的左子树压入栈中,作为下一个将要搜索的节点
            	//	通俗来说,这个while 循环的作用在于,遍历完一个节点左子树的的所有左节点
            	//	在保证左子树的左节点全部遍历过后,“最左节点”的左节点一定为NULL, 此时循环跳出
                res.push_back(root->val);
                st.push(root);
                root = root->left;
            }
			
			//	左子树的“左节点”遍历结束,之后开始遍历左子树的“右节点”
			//	同理,当左子树遍历结束,开始遍历右子树;
			//	之后,右子树的“最左节点”,每一个左节点的左右子树,以此类推
            root = st.top();
            st.pop();
            root = root->right;
        }

        return res;
    }
};

94. 二叉树的中序遍历(中等)

题目:

给定一个二叉树,返回它的中序 遍历。

示例:

输入: [1,null,2,3]
   1
    \
     2
    /
   3

输出: [1,3,2]

中序遍历的顺序为左子树->根节点->右子树,从前序遍历的方法中可以大致推导出基本框架为:

while(root || st.size()){
	while(root){
		//	这个while循环保证最终到达的二叉树“最左”的节点
		st.push(root);
		root = root->left;
	}

	//	while结束,表明已经抵达该子树的“最左”节点,目前栈顶元素就是“最左”节点的地址
	//	毫无疑问,“最左节点”的左右子树均为NULL
	root = st.top();
	st.pop();
	res.push_back(root->val);
	//	正因“最左节点”的左右子树均为NULL,且“最左节点”已经确定,因此“最左节点”的右子树,就成了“回溯”的关键条件
	root = root->right;
}

一句话来总结,就是不断确定目前节点的“最左节点”,并以“最左节点”的右子树是否为NULL为回溯的条件。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;
        while(root || st.size()){
            while(root){
                st.push(root);
                root = root->left;
            }
            root = st.top();
            st.pop();
            res.push_back(root->val);
            root = root->right;
        }
        return res;
    }
};

你可能感兴趣的:(C++,算法)