leetcode试题总结<6>

还是栈与队列的题目

385. Mini Parser

Given a nested list of integers represented as a string, implement a parser to deserialize it.

Each element is either an integer, or a list -- whose elements may also be integers or other lists.

Note: You may assume that the string is well-formed:

  • String is non-empty.
  • String does not contain white spaces.
  • String contains only digits 0-9[- ,].

Example 1:

Given s = "324",

You should return a NestedInteger object which contains a single integer 324.

Example 2:

Given s = "[123,[456,[789]]]",

Return a NestedInteger object containing a nested list with 2 elements:

1. An integer containing value 123.
2. A nested list containing two elements:
    i.  An integer containing value 456.
    ii. A nested list with one element:
         a. An integer containing value 789.
试题大意:这道题让我们实现一个迷你解析器用来把一个字符串解析成NestInteger类。

<span style="font-size:18px;">/** 
 * // This is the interface that allows for creating nested lists. 
 * // You should not implement it, or speculate about its implementation 
 * class NestedInteger { 
 *   public: 
 *     // Constructor initializes an empty nested list. 
 *     NestedInteger(); 
 * 
 *     // Constructor initializes a single integer. 
 *     NestedInteger(int value); 
 * 
 *     // Return true if this NestedInteger holds a single integer, rather than a nested list. 
 *     bool isInteger() const; 
 * 
 *     // Return the single integer that this NestedInteger holds, if it holds a single integer 
 *     // The result is undefined if this NestedInteger holds a nested list 
 *     int getInteger() const; 
 * 
 *     // Set this NestedInteger to hold a single integer. 
 *     void setInteger(int value); 
 * 
 *     // Set this NestedInteger to hold a nested list and adds a nested integer to it. 
 *     void add(const NestedInteger &ni); 
 * 
 *     // Return the nested list that this NestedInteger holds, if it holds a nested list 
 *     // The result is undefined if this NestedInteger holds a single integer 
 *     const vector<NestedInteger> &getList() const; 
 * }; 
 */  
class Solution {  
public:  
    NestedInteger deserialize(string s) {  
        if(s[0] != '[') return NestedInteger(stoi(s));  
        stack<NestedInteger*> stk;  
        NestedInteger* ret = NULL;  
        int idx = 0;  
          
        for(int i=0; i<s.size(); ++i) {  
            if(s[i] == '[') {  
                stk.push(new NestedInteger());  
                if(!ret) ret = stk.top();  
                idx = i + 1;  
            }  
            else if(s[i] == ']' || s[i] == ',') {  
                if(idx != i)  
                    stk.top()->add(NestedInteger(stoi(s.substr(idx, i-idx))));  
                if(s[i] == ']') {  
                    NestedInteger* cur = stk.top();  
                    stk.pop();  
                    if(!stk.empty()) stk.top()->add(*cur);  
                }  
                idx = i + 1;  
            }  
        }  
          
        return *ret;  
    }  
};</span>
总结:这道题字符串中括号存在层层嵌套的情况,所以需要借助栈来记录遍历路径上的每层NestedInteger。大致思路是:遇到'['时,表明即将进入新一层链表,此时构造新的NestedInteger并进栈;遇到']'时,表明该层链表结束,此时出栈并将该层链表通过add函数加入上层链表中。数字在遍历到']'或','时结束,此时将数字构造的NestedInteger通过add函数加入栈顶链表中,处理过程中需要记录数字的起始位置。另外,栈中最好存储指针,能够降低空间复杂度。


341. Flatten Nested List Iterator

Given a nested list of integers, implement an iterator to flatten it.

Each element is either an integer, or a list -- whose elements may also be integers or other lists.

Example 1:
Given the list [[1,1],2,[1,1]],

By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1]

Example 2:
Given the list [1,[4,[6]]],

By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,4,6]

<span style="font-size:18px;">/**
 * // This is the interface that allows for creating nested lists.
 * // You should not implement it, or speculate about its implementation
 * class NestedInteger {
 *   public:
 *     // Return true if this NestedInteger holds a single integer, rather than a nested list.
 *     bool isInteger() const;
 *
 *     // Return the single integer that this NestedInteger holds, if it holds a single integer
 *     // The result is undefined if this NestedInteger holds a nested list
 *     int getInteger() const;
 *
 *     // Return the nested list that this NestedInteger holds, if it holds a nested list
 *     // The result is undefined if this NestedInteger holds a single integer
 *     const vector<NestedInteger> &getList() const;
 * };
 */
class NestedIterator {
    
    stack <NestedInteger> s;
public:
    NestedIterator(vector<NestedInteger> &nestedList) 
    {
        for (int i = nestedList.size() - 1; i >= 0; --i) 
        {
            s.push(nestedList[i]);
        }
    }

    int next() {
        NestedInteger t = s.top(); 
        s.pop();
        return t.getInteger();
    }

    bool hasNext() {
        while (!s.empty())
        {
            NestedInteger temp = s.top(); 
            if (temp.isInteger()) 
                return true;
            s.pop();
            for (int i = temp.getList().size() - 1; i >= 0; --i) {
                s.push(temp.getList()[i]);
            }
        }
        return false;
    }
};

/**
 * Your NestedIterator object will be instantiated and called as such:
 * NestedIterator i(nestedList);
 * while (i.hasNext()) cout << i.next();
 */</span>
总结:首先搞懂题目的意思很重要,题目要求将一个具有内嵌的整数序列平坦化(即去掉嵌套结构,输出一个个的整数)。这里主要借助next和hasNext两个函数实现遍历。主要思路如下:

1.通过类的构造函数将NestedInteger类型的向量里的元素从后往前保存到类的一个栈结构中去。

        2.hasNext首先判断栈是否为空,为空返回false,若不为空,取出栈顶的NestedInteger元素,并判断是否为整数,若为整数,则表示next函数可直接输出该整数,若不为整数,即为一个嵌套序列,那么先保存栈顶嵌套序列,然后pop出栈顶元素,取出这个嵌套结构里的各个元素,再push到栈中去,注意,每一次push的顺序都是从尾到头的顺序。如此反复,即可解析全部嵌套结构,并由next输出。

331. Verify Preorder Serialization of a Binary Tree

题意:前序遍历中,当遇到非空的结点时,记录下结点的值,当结点为空时,用一个特殊值#标记结点。


例如,上例中,二叉树用中序遍历可序列化为字符串"9,3,4,#,#,1,#,#,2,#,6,#,#"。这里的#即代表空节点。

给一串用逗号分开的字符串值,检查它是否是正确的二叉树前序遍历的序列,字符串中每个逗号分隔的值只能是整数或者#,而且假设输入的字符串是合法的,及不会出现"1,,3"的类似情况。

class Solution {
public:
    bool isValidSerialization(string preorder) {
        if(preorder.empty())
            return false;
        istringstream in(preorder);
        vector<string> v;
        string str;
        int d = 0;
        while(getline(in,str,','))
            v.push_back(str);
            
        for(int i = 0; i < v.size()-1; i++)
        {
            if(v[i] == "#")
            {
                if(d == 0)
                    return false;
                --d;
            }
            else
                ++d;
        }
        
        return d != 0 ? false : v.back() == "#";
    }
};
class Solution {
public:
    bool isValidSerialization(string preorder) {
        
        string token;
        stringstream ss;
        ss<<preorder;
        stack<string> st;
        while(getline(ss,token,','))
        {
            if(token=="#")
            {
                while(!st.empty() && st.top() == "#")
                {
                    st.pop();
                    if(st.empty()) return false;
                    st.pop();
                }
                st.push(token);
            }else{
                st.push(token);
            }
        }
        return st.size()==1 && st.top()=="#";
        
    }
};
总结:判断给出的序列是否是二叉树的前序遍历,有两种算法思路:

方法1:

观察先序遍历的结果,当出现叶节点时,就会出现两个"#",因为叶节点没有子节点。此时可以将该叶节点消除,即用一个"#"代替,一层层向上归并消除直至根节点,最终只剩一个"#"。可以用栈来实现该过程。以样例为例来演示下该"归并"过程:

9 <- (入栈)

9,3 <-

9,3,4 <-

9,3,4,# <-

9,3,4,#   # ->  9,3,#

9,3,#,1 <-

9,3,#,1,# <-

9,3,#,1,#  #  ->  9,3,#,#   -> 9,#

9,#,2 <-

9,#,2,#  <-

9,#,2,#,6  <-

9,#,2,#,6,#  <-

9,#,2,#,6,#  #  ->  9,#,2,#,# ->9,#,# ->#

每个元素入栈一次,出栈一次,故时间复杂度为O(n),空间复杂度为O(n)。

方法2:

观察二叉树的前序遍历可以得出如下两个规律:

1. 数字的个数总是比#号少一个

2. 最后一个一定是#号

那么我们加入先不考虑最后一个#号,那么此时数字和#号的个数应该相同,如果我们初始化一个为0的计数器,遇到数字,计数器加1,遇到#号,计数器减1,那么到最后计数器应该还是0。下面我们再来看两个返回False的例子,"#,7,6,9,#,#,#"和"7,2,#,2,#,#,#,6,#",那么通过这两个反例我们可以看出,如果根节点为空的话,后面不能再有节点,而且不能有三个连续的#号出现。所以我们再加减计数器的时候,如果遇到#号,且此时计数器已经为0了,再减就成负数了,就直接返回False了,因为正确的序列里,任何一个位置i,在[0, i]范围内的#号数都不大于数字的个数的。当循环完成后,我们检测计数器是否为0的同时还要看看最后一个字符是不是#号。


173. Binary Search Tree Iterator

题意:构造一个二叉搜索树的迭代器的类,以二叉搜索树的根节点初始化迭代器。调用迭代器类中next函数将返回二叉搜索树中值最小的结点。

/**
 * Definition for binary tree
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class BSTIterator {
    stack<TreeNode*> stk;
public:
    BSTIterator(TreeNode *root) {
        while(!stk.empty())
            stk.pop();
        while(root)
        {
            stk.push(root);
            root = root->left;
        }
    }

    /** @return whether we have a next smallest number */
    bool hasNext() {
        return !stk.empty();
    }

    /** @return the next smallest number */
    int next() {
        TreeNode* temp = stk.top();
        stk.pop();
        int val = temp->val;
        temp = temp->right;
        while(temp)
        {
            stk.push(temp);
            temp = temp->left;
        }
        return val;
    }
};

/**
 * Your BSTIterator will be called like this:
 * BSTIterator i = BSTIterator(root);
 * while (i.hasNext()) cout << i.next();
 */
思路:返回树中最小结点的值,可以使用栈来实现,初始化迭代器类时,将包括根节点在内的所有左子树的结点push入栈。这样,栈顶元素为二叉搜索树中最小的元素值,next函数中,弹出栈顶元素值并返回,需要注意的是,需要push栈顶结点的右子树的所有左子树结点入栈。

150. Evaluate Reverse Polish Notation

题意:计算逆波兰表达式(后缀表达式)的结果

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> stk;
        for(auto a : tokens)
        {
            if(a.size() == 1 && !isdigit(a[0])) //该字符为运算符
            {
                int num2 = stk.top();
                stk.pop();
                int num1 = stk.top();
                stk.pop();
                switch(a[0])
                {
                    case '+':
                        stk.push(num1 + num2);
                        break;
                    case '-':
                        stk.push(num1 - num2);
                        break;
                    case '*':
                        stk.push(num1 * num2);
                        break;
                    case '/':
                        stk.push(num1 / num2);
                        break;
                }
            }
            else
                stk.push(atoi(a.c_str()));
        }
        return stk.top();
    }
};
总结:

表达式一般由操作数(Operand)、运算符(Operator)组成,例如算术表达式中,通常把运算符放在两个操作数的中间, 这称为中缀表达式(Infix Expression),如A+B。 波兰数学家Jan Lukasiewicz提出了另一种数学表示法,它有两种表示形式: 把运算符写在操作数之前,称为波兰表达式(Polish Expression)或前缀表达式(Prefix Expression),如+AB; 把运算符写在操作数之后,称为逆波兰表达式(Reverse Polish Expression)或后缀表达式(Suffix Expression),如AB+; 其中,逆波兰表达式在编译技术中有着普遍的应用。

计算逆波兰是栈结构最为基础的应用。具体思路如下:将表达式中的数据从前到后输入到栈结构中去,这里需要对输入的数据进行判断,如果是运算符,则弹出栈顶的两个元素,并将两个元素与运算符的运算结果push进栈,注意运算顺序,栈顶的元素在运算符的后面。如果是数据,则直接push进栈。


猜你喜欢

转载自blog.csdn.net/dby3579/article/details/52266879