还是栈与队列的题目
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:
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类。
/**
* // 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 &getList() const;
* };
*/
class Solution {
public:
NestedInteger deserialize(string s) {
if(s[0] != '[') return NestedInteger(stoi(s));
stack stk;
NestedInteger* ret = NULL;
int idx = 0;
for(int i=0; iadd(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;
}
};
总结:这道题字符串中括号存在层层嵌套的情况,所以需要借助栈来记录遍历路径上的每层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]
/**
* // 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 &getList() const;
* };
*/
class NestedIterator {
stack s;
public:
NestedIterator(vector &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();
*/
总结:首先搞懂题目的意思很重要,题目要求将一个具有内嵌的整数序列平坦化(即去掉嵌套结构,输出一个个的整数)。这里主要借助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 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< 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的同时还要看看最后一个字符是不是#号。
题意:构造一个二叉搜索树的迭代器的类,以二叉搜索树的根节点初始化迭代器。调用迭代器类中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 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栈顶结点的右子树的所有左子树结点入栈。
题意:计算逆波兰表达式(后缀表达式)的结果
class Solution {
public:
int evalRPN(vector& tokens) {
stack 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();
}
};
总结:
计算逆波兰是栈结构最为基础的应用。具体思路如下:将表达式中的数据从前到后输入到栈结构中去,这里需要对输入的数据进行判断,如果是运算符,则弹出栈顶的两个元素,并将两个元素与运算符的运算结果push进栈,注意运算顺序,栈顶的元素在运算符的后面。如果是数据,则直接push进栈。