LeetCode Mini Parse and Flatten Nested List Iterator
LeetCode Mini Parse and Flatten Nested List Iterator
Mini Parse
这道题和我在面试阿里云时遇到的算法题很相似。本题是解析嵌套的整数,当时的题是解析嵌套的HashMap
懒得说题目细节了,直接把LeetCode原文抄过来。
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.
这道题两个做法,一个是递归,另一个非递归。很明显了,使用非递归的话肯定会用到栈。
算法方面其实没啥好说的,就是递归思想的实际使用。
非递归使用栈
以一个输入为例:输入为[123,[456,[789]],211,985]
那么我们最后得到的NestedInteger应该包含三个整数,分别为123,211和985,同时还有一个子NestedInteger,该对象内有一个整数456,和一个孙子NestedInteger,孙子NestedInteger内包含一个整数为789。
分析这个输入:
- 只要我们遇到一个
[
,就说明我们开始遇到一个NestedInteger - 如果遇到一个
,
,说明此处可能是数字的中断,也可能是NestedInteger的中断,但是有一点可以确认:,
之后一定是新“过程”的开始 - 如果遇到一个
]
,说明这是一个NestedInteger的结束
首先使用stack非递归来做。很明显,stack中保存的元素应该为NestedInteger,以上述输入为例,stack中元素从栈底到栈顶元素应该依次为:[123,211,985],[456],[789]
。栈中靠下的元素应该包含之上的元素。
所以我们很明显会需要一个过程:
NestedInteger ni;
while(!stk.empty()){
ni = stk.top();
stk.pop();
stk.top().add(ni);
}
这样的到的最后的ni就是结果。
但是我们会发现,我们很难按照上述方式构造栈!因为当我们遇到第二个[
时,需要把123压栈,当我们遇到第三个[
时,需要把456压栈,而当我们遇到211时,211实际上是最外层Integer的元素,而此时该Integer已经被压入栈底。
所以这给了我们一个提示,即我们需要用栈顶元素来保存当前应该插入的NestedInteger。比如:当我们遇到123时,栈顶元素应为一个[]
,当我们遇到456时,栈顶元素应该为[]
,当我们遇到789时,栈顶元素应该为[456]
,当我们遇到211时,栈顶元素应该为[123,[456,[789]]]
集合之前的输入分析,可以得出:
- 遇到一个
[
时,我们需要往栈中压入一个空NestedInteger - 当遇到一个
,
时,我们需要往栈顶元素中add一个整数 - 当遇到
]
时,说明一个NestedInteger结束,需要将该NestedInteger添加到它的父NestedInteger中,实现如下:
NestedInteger ni = stk.top();
if(!stk.empty())
stk.top().add(ni);
else
stk.push(ni);
最终代码实现如下:
/**
* // 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) {
stack<NestedInteger> stk;
auto isDigit = [](const char& c) { return (c == '-') || isdigit(c); };
if (isDigit(s.front())) {
return stoi(string(s.begin(), s.end()));
}
auto itr = s.begin();
while (itr < s.end()) {
if (isDigit(*itr)) {
auto valueEnd = find_if_not(itr, s.end(), isDigit);
if (stk.empty()) // 用于处理输入为单个整数的情况,这种情况下没有[]
stk.push(NestedInteger(stoi(string(itr, valueEnd))));
else
stk.top().add(stoi(string(itr, valueEnd)));
itr = valueEnd;
} else {
if (*itr == '[') {
stk.push(NestedInteger());
} else if (*itr == ']') {
NestedInteger ni = stk.top();
stk.pop();
if (stk.empty()) // 用于处理输入为[]的情况
stk.push(ni);
else
stk.top().add(ni);
}
++itr;
}
}
return stk.top();
}
};
递归
本题实际上解析只有两个对象,一个是对以[
开头的NestedInteger解析,另一个是对数字的解析。解析NestedInteger时需要用到对数字的解析。实现如下:
class Solution {
NestedInteger parse(string& s, int& pos) {
if (s[pos] == '[') return parseNestedList(s, pos);
return parseInteger(s, pos);
}
NestedInteger parseInteger(string& s, int& pos) {
int sign = s[pos] == '-' ? -1 : 1;
if (s[pos] == '+' || s[pos] == '-') ++pos;
int num = 0;
while (isdigit(s[pos])) {
num = num * 10 + s[pos++] - '0';
}
num *= sign;
return NestedInteger(num);
}
NestedInteger parseNestedList(string& s, int& pos) {
NestedInteger ni;
// pos++;
while (s[pos] != ']') {
pos++; // skip , and first [
if (s[pos] == ']') break;
ni.add(parse(s, pos));
}
pos++;
return ni;
}
public:
NestedInteger deserialize(string s) {
int pos = 0;
return parse(s, pos);
}
};
Flatten Nested List Iterator
本题要求对一个嵌套的 Integer List 进行 flatten。
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:
Input: [[1,1],2,[1,1]]
Output: [1,1,2,1,1]
Explanation: By calling next repeatedly until hasNext returns false,
the order of elements returned by next should be: [1,1,2,1,1].
Example 2:
Input: [1,[4,[6]]]
Output: [1,4,6]
Explanation: By calling next repeatedly until hasNext returns false,
the order of elements returned by next should be: [1,4,6].
需要实现三个函数,NestedInteger构造函数,hasNext()用来判断是否还有可供打印的整数,next()则返回一个可供打印的整数,同时将迭代器向后移动一位。
以输入
[123,456,[789,211],985]
[345]
[398,[921,347]]
为例来进行分析
题目提供了NestedInteger对象的三个接口
- bool isInteger() const; 判断当前NestedInteger是否为sigle integer
- int getInteger() const; 如果只有single integer则返回该值
- const vector
&getList() const; 将NestedInteger中的元素以一个vector的形式返回
从提供的接口来看,唯一可以获得NestedInteger中整数的情况是:NestedInteger(之后简称NI)中只有single integer。所以我们需要把嵌套结构的NI进行flatten,将它分解为一个个包含single integer的NI。
下面的代码展示了采用递归如何对一个NestedInteget进行flatten
void flatten(const NestedInteger& nestedInteger){
if(nestedInteger.isInteger()){
cout << nestedInteger.getInteger() << ' ';
return;
}
vector<NestedInteger>& vNI = nestedList.getList();
for(auto& ni : vNI){
flatten(ni);
}
return;
}
如果使用栈+非递归:
void flatten(const NestedInteger& nestedInteger){
stack<NestedInteger> stk;
if(nestedInteger.isInteger())
cout << nestedInteger.getInteger();
vector<NestedInteger>& vNI = nestedList.getList();
for(auto rItr = vNI.rbegin(); rIte < vNI.rend(); ++rItr)
stk.push(*rItr);
while(!stk.empty()){
if(stk.top().isInteger()){
cout << stk.top().getInteger() << ' ';
stk.pop();
}
vector<NestedInteger>& vNI = nestedList.getList();
for(auto rItr = vNI.rbegin(); rIte < vNI.rend(); ++rItr)
stk.push(*rItr);
}
return;
}
OK,我们现在知道如何处理一个NestedInteger的情况了。那么应该如何处理vector<NestedInteger>
呢?
显而易见:对vector<NestedInteger>
中的每个NI单独进行flatten即可。
void flattenVectorNI(const vector<NestedInteger>& nestedList){
for(auto& ni : nestedList)
flatten(ni);
}
回到本题!额额额额额,本题要求是实现NestedIterator,而不是简单地直接flatten。
当然了,如果想直接套用之前的代码也是可以的,暴力flatten,把所有输出保存在queue中,NestedIterator一次遍历一个输出即可。这种实现好处在于NestedIterator.next()和NestedIterator.hasNext()的效率会很高。缺点在于NestedIterator构造函数的运行时间会很长(暴力flatten肯定是要在构造函数中进行的),而且没有做到“按需”遍历。
为了做到按需,我们需要一个栈。每次调用hasNext时,我们对栈顶的NI进行flatten,这是一个迭代的过程,当栈顶NI中只包含single integer时,返回true。栈顶之下的NI保持不动,当迭代器遍历到它时再进行flatten。如果stk为空,说明所有的NI已经遍历过了,返回false
bool hasNext() {
while (!stk.empty()) {
if (stk.top()->isInteger()) return true;
// stk.top() is not a single Integer
// maybe another NestedInteger,or a []
// need flatten
auto& vNI = stk.top()->getList();
stk.pop();
if (vNI.empty()) // precess []
continue;
for (auto i = vNI.end() - 1; i >= vNI.begin(); --i) stk.push(i);
}
return false;
}
对于next函数,它只会在hasNext返回为true时才会调用,所以直接返回stk.top().getInteget()
即可,返回之前需要将栈顶元素pop。
int next() {
int ret = stk.top()->getInteger();
stk.pop();
return ret;
}
为了提高效率,我在栈中保存了迭代器而不是NI本身。完整代码如下:
/**
* // 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 {
public:
NestedIterator(vector<NestedInteger>& nestedList) {
if (nestedList.empty()) // process []
return;
for (auto i = nestedList.end() - 1; i >= nestedList.begin(); --i)
stk.push(i);
}
int next() {
int ret = stk.top()->getInteger();
stk.pop();
return ret;
}
bool hasNext() {
while (!stk.empty()) {
if (stk.top()->isInteger()) return true;
// stk.top() is not a single Integer
// maybe another NestedInteger,or a []
// need flatten
auto& vNI = stk.top()->getList();
stk.pop();
if (vNI.empty()) // precess []
continue;
for (auto i = vNI.end() - 1; i >= vNI.begin(); --i) stk.push(i);
}
return false;
}
private:
stack<vector<NestedInteger>::iterator> stk;
};
/**
* Your NestedIterator object will be instantiated and called as such:
* NestedIterator i(nestedList);
* while (i.hasNext()) cout << i.next();
*/