当前位置: 首页 > news >正文

leetcode刷题--栈与递归

文章目录

      • 1. 682 棒球比赛
      • 2. 71 简化路径
      • 3. 388 文件的最长绝对路径
      • 4. 150 逆波兰表达式求值
      • 5. 227. 基本计算器II
      • 6. 224. 基本计算器
      • 7. 20. 有效的括号
      • 8. 636. 函数的独占时间
      • 9. 591. 标签验证器
      • 10. 32.最长有效括号
      • 12. 341. 扁平化嵌套列表迭代器
      • 13. 394.字符串解码

1. 682 棒球比赛

image-20230815161054212 image-20230815161118457

image-20230815161206904

解法:

使用变长数组对栈进行模拟。

如果操作是 +,那么访问数组的后两个得分,将两个得分之和加到总得分,并且将两个得分之和入栈。

如果操作是D,那么访问数组的最后一个得分,将得分乘以 2 加到总得分,并且将得分乘以 2 入栈。

如果操作是C,那么访问数组的最后一个得分,将总得分减去该得分,并且将该得分出栈。

如果操作是整数,那么将该整数加到总得分,并且将该整数入栈。

时间复杂度:O(n),其中 n为数组 ops 的大小。遍历整个ops 需要 O(n)。

空间复杂度:O(n)。变长数组最多保存O(n) 个元素。

class Solution {
public:int calPoints(vector<string>& operations) {int sum=0;int tmp=0;vector<int>top;for(int i=0;i< operations.size();i++){if(operations[i]=="+"){int n=top.size();tmp= top[n-1]+top[n-2];top.emplace_back(tmp);}else if(operations[i]=="C"){top.pop_back();}else if(operations[i]=="D"){int n=top.size();tmp=top[n-1]*2;top.emplace_back(tmp);}else{tmp=atoi(operations[i].c_str());top.emplace_back(tmp);}}for(auto item:top){sum+=item;}return sum;}
};

2. 71 简化路径

image-20230815170146584

image-20230815170206026

解法:使用栈来解决,首先将path根据/分隔为由若干个字符串组成的列表,因为多个/最终也表示/。但是由于c++没有split函数,因此要自己手动实现一个split方法。之后对于vector内的元素,如果是一个点,保持不变,两个点目录切换到上一级,对应弹栈,若都不是,代表目录名,则入栈。

最后将目录名用“/”连接输出即可

class solution62 {public:string simplifyPath(string path) {vector<string>result;int n=path.length();//split path by /for(int i=0;i<n;){if(path[i]=='/'){i++;if(i==n)break;string tmp="";while(path[i]!='/'&&i<n){tmp+=path[i];i++;}if(!tmp.empty()){result.emplace_back(tmp);}}}vector<string>last;for(auto r:result){if(r==".")continue;else if(r==".."){if(!last.empty())last.pop_back();}elselast.emplace_back(r);}string lastreuslt="/";int m=last.size();for(int i=0;i<m;i++){lastreuslt+=last[i];if(i!=m-1){lastreuslt+="/";}}return lastreuslt;}
};

时间复杂度:O(n),其中 n 是字符串path 的长度。

空间复杂度:O(n)。我们需要 O(n) 的空间存储names 中的所有字符串。

3. 388 文件的最长绝对路径

image-20230816164505649

image-20230816164554679

image-20230816164609306

解法:hash表+前缀和

文件系统可以的最长绝对路径,即文件系统按照层次便利的最长路径,遍历的终点为一个文件。

题目中的制表符\t的个数为深度,根目录的深度为0。因此可以以\n来划分文件字符串,设置指针i和j,i指向首位置,j指向下一个\n的位置,因此j-i为两个\n之间的长度,包含了\t,因为文件总长度不包含\t。因此若为文件夹,该L层的长度为

j-i+levelLen[L-1]+1-cnt; 其中levelLen[L-1]是上一层的长度,cnt为\t个数,1表示“/”

若为文件,总长度为:

j-i+levelLen[L-1]-cnt; 其中levelLen[L-1]是上一层的长度,cnt为\t个数

最长长度一直从文件长度中取最大值。

代码:

class solution63 {
public:int lengthLongestPath(string input) {int n=input.size();unordered_map<int,int>leveltoLen;int ans=0;for(int i=0;i<n;i++){int j=i;int cnt=0;bool isFile=false;while(j<n&&input[j]!='\n'){if(input[j]=='\t')cnt++;else if(input[j]=='.'){isFile= true;}j++;}int len;if(isFile){len=j-i+leveltoLen[cnt-1]-cnt;ans=max(ans,len);}else{len=j-i+leveltoLen[cnt-1]+1-cnt;}leveltoLen[cnt]=len;i=j;}return ans;}
};

时间复杂度:O(n) n为字符串长度

空间复杂度:O(n) 哈希表空间

4. 150 逆波兰表达式求值

image-20230816221607412

image-20230816221622984

解法:中缀表达式可以使用栈来维护,首先遍历算数表达式,如果遇到数字入栈,如果遇到符号,则出栈两个数字,并且与符号相作用然后入栈,最后栈中剩余的唯一数字则为最后结果。

代码:

class solution64 {
public:int evalRPN(vector<string>& tokens) {vector<int>stacks;for(auto item:tokens){if(item=="+"){int t1=stacks.back();stacks.pop_back();int t2=stacks.back();stacks.pop_back();int tmp=t2+t1;stacks.emplace_back(tmp);}else if(item=="-"){int t1=stacks.back();stacks.pop_back();int t2=stacks.back();stacks.pop_back();int tmp=t2-t1;stacks.emplace_back(tmp);}else if(item=="*"){int t1=stacks.back();stacks.pop_back();int t2=stacks.back();stacks.pop_back();int tmp=t2*t1;stacks.emplace_back(tmp);}else if(item=="/"){int t1=stacks.back();stacks.pop_back();int t2=stacks.back();stacks.pop_back();int tmp=t2/t1;stacks.emplace_back(tmp);}else{int t= std::atoi(item.c_str());stacks.emplace_back(t);}}return stacks.back();}
};

时间复杂度:O(n),其中 n 是数组 tokens 的长度。需要遍历数组 tokens 一次,计算逆波兰表达式的值。

空间复杂度:O(n),其中 n是数组tokens 的长度。使用栈存储计算过程中的数,栈内元素个数不会超过逆波兰表达式的长度。

5. 227. 基本计算器II

image-20230816224141680

解法:

这题没有括号实现计算器,可以使用双栈的思路,一个栈存储数字,另一个栈存储运算符,注意到*/的运算符优先级相同,±运算符优先级相同,乘除的优先级高于加减。因此当运算符a入栈时,需要判断栈顶元素,如果a为+或者-号,则栈内所有其他运算符必须先和数字占进行结合计算。如果a为乘号或者除号,则栈顶元素为“+”或者“-”可以直接入栈。

遍历完式子之后,如果符号栈为空,返回数字栈中的数字,如果符号栈不为空,则按照符号栈的顺序弹栈并且与数字栈结合计算返回结果。

注意两点:c++的isdigit函数可以判断字符串是否为数字,且数字为非负整数,不止是一位数,因此需要对数字进行转换。

并且s[i]-'0’需要用int进行强制类型转换,因为减法运算可能导致溢出或者不确定的结构,使得ascii为负数。

代码可能比较冗余,但逻辑是清晰的。

代码:

class solution65 {
public:int calculate(string s) {vector<int>num;vector<char>op;int n=s.size();for(int i=0;i<n;i++){if(s[i]==' ')continue;else if(s[i]=='*'){int tmp;while(!op.empty()){if(op.back()=='/'){int t1=num.back();num.pop_back();int t2=num.back();num.pop_back();tmp=t2/t1;num.emplace_back(tmp);}else if(op.back()=='*'){int t1=num.back();num.pop_back();int t2=num.back();num.pop_back();tmp=t2*t1;num.emplace_back(tmp);}else{break;}op.pop_back();}op.emplace_back(s[i]);}else if(s[i]=='/'){int tmp;while(!op.empty()){if(op.back()=='/'){int t1=num.back();num.pop_back();int t2=num.back();num.pop_back();tmp=t2/t1;num.emplace_back(tmp);}else if(op.back()=='*'){int t1=num.back();num.pop_back();int t2=num.back();num.pop_back();tmp=t2*t1;num.emplace_back(tmp);}else{break;}op.pop_back();}op.emplace_back(s[i]);}else if(s[i]=='+'||s[i]=='-'){int t1,t2,tmp;while(!op.empty()){if(op.back()=='+'){t1=num.back();num.pop_back();t2=num.back();num.pop_back();tmp=t2+t1;num.emplace_back(tmp);}else if(op.back()=='-'){t1=num.back();num.pop_back();t2=num.back();num.pop_back();tmp=t2-t1;num.emplace_back(tmp);}else if(op.back()=='*'){t1=num.back();num.pop_back();t2=num.back();num.pop_back();tmp=t2*t1;num.emplace_back(tmp);}else if(op.back()=='/'){t1=num.back();num.pop_back();t2=num.back();num.pop_back();tmp=t2/t1;num.emplace_back(tmp);}op.pop_back();}op.emplace_back(s[i]);}else{int n=0;while(isdigit(s[i])){n=n*10+s[i]-'0';i++;}num.emplace_back(n);i--;}}int t1,t2,tmp;while(!op.empty()){char o=op.back();op.pop_back();t1=num.back();num.pop_back();t2=num.back();num.pop_back();if(o=='*'){tmp=t2*t1;}else if(o=='/'){tmp=t2/t1;}else if(o=='+'){tmp=t2+t1;}else{tmp=t2-t1;}num.emplace_back(tmp);}return num.back();}};

时间复杂度:O(n),其中n 为字符串 s的长度。需要遍历字符串 s 一次,计算表达式的值。

空间复杂度:O(n),其中 n 为字符串 s 的长度。空间复杂度主要取决于栈的空间,栈的元素个数不超过 n。

6. 224. 基本计算器

image-20230817182044196

解法:使用两个栈 num 和 op 。

num: 存放所有的数字 op :存放所有的数字以外的操作。(±)

1.首先预处理字符串,将所有空格去掉。

2.然后对于所有的"("直接放入符号栈

3.如果有新的符号入栈,且不是")“,可以将当前栈内可以计算的符号进行计算,避免在通过”)"进行判断计算的时候,将(-+)操作顺序变换导致出错。

4.如果遇到")“将当前”("前的所有符号都计算,并且弹出左括号,计算结果加入num

时刻注意一些细节:由于第一个数可能是负数,如“-2+1”,为了减少边界判断,先往 nums 添加一个 0;
为防止 () 内出现的首个字符为运算符如(-2)(+2),如果遇到(-,(+,在num中添加0

代码:

class solution66 {
public:int calculate(string s) {vector<int>num;//为了防止第一个数是负数num.emplace_back(0);vector<char>op;bool flag=false;string ns="";for(int i=0;i<s.size();i++){if(s[i]==' ')continue;ns+=s[i];}int n=ns.size();for(int i=0;i<n;i++){if(ns[i]=='('){if(ns[i+1]=='-'||ns[i+1]=='+'){num.emplace_back(0);}op.emplace_back(ns[i]);}else{if(isdigit(ns[i])){int m=0;while(isdigit(ns[i])){m=m*10+int(ns[i]-'0');i++;}num.emplace_back(m);i--;}else if(ns[i]=='+'||ns[i]=='-'){//将栈内能算的先算,避免之后算让如(-+)操作的+号运算比-前int t1,t2,tmp;while(!op.empty()&&op.back()!='('){t1=num.back();num.pop_back();t2=num.back();num.pop_back();char o=op.back();op.pop_back();if(o=='+')tmp=t2+t1;else if(o=='-')tmp=t2-t1;num.emplace_back(tmp);}op.emplace_back(ns[i]);}else if(ns[i]==')'){int t1,t2,tmp;while(op.back()!='('){t1=num.back();num.pop_back();t2=num.back();num.pop_back();char o=op.back();op.pop_back();if(o=='+')tmp=t2+t1;else if(o=='-')tmp=t2-t1;num.emplace_back(tmp);}op.pop_back();}}}int t1,t2,tmp;while(!op.empty()){char o=op.back();op.pop_back();t1=num.back();num.pop_back();t2=num.back();num.pop_back();if(o=='+')tmp=t2+t1;else if(o=='-')tmp=t2-t1;num.emplace_back(tmp);}return num.back();}
};

时间复杂度:o(n)

空间复杂度:o(n)

7. 20. 有效的括号

image-20230817212110654

解法:利用栈来解决,首先字符串为空或者长度为1,一定返回false;

然后便利字符串中的括号,如果是左括号则入栈,如果碰到右括号,如果栈中非空,并且栈顶有对应的左括号与其匹配,则弹栈;否则将右括号入栈;

最后如果栈为空,说明匹配,否则不匹配

class solution67 {
public:bool isValid(string s) {vector<char>stack;if(s.empty()||s.size()==1)return false;for( auto item:s){if(item=='('||item=='['||item=='{')stack.emplace_back(item);else if(item==')'){if(stack.empty()||stack.back()!='(')stack.emplace_back(item);elsestack.pop_back();}else if(item==']'){if(stack.empty()||stack.back()!='[')stack.emplace_back(item);elsestack.pop_back();}else if(item=='}'){if(stack.empty()||stack.back()!='{')stack.emplace_back(item);elsestack.pop_back();}}return stack.empty();}
};

时间复杂度:O(n)

空间复杂度:O(n)

8. 636. 函数的独占时间

image-20230817225942150

image-20230817230013342

image-20230817230027046

解法:因为每个函数都有对应的start和end;即栈中记录函数的id号和开始时间戳;result记录每个函数的累积执行时间,

1.若为起始函数,直接入栈;

2.若有新的函数为start,则入栈,栈顶的函数s的运行时间是当前新函数的时间戳timestamp2-栈顶时间戳timestamp1;累加到result[s]中,且需要将栈顶函数的其实时间改为新函数的其实时间。

3.如果新函数为end,那么会弹出栈顶函数s,栈顶函数的运行时间为当新函数的时间戳timestamps2-栈顶时间戳+1;此时pop栈顶函数;同时若栈顶不为空的话,新的栈顶函数的时间戳为timestamp2+1

最后栈为空,返回result

代码:

class solution68 {
public:vector<int> exclusiveTime(int n, vector<string>& logs) {vector<int>result(n,0);vector<pair<int,int>>stack;for(auto log:logs){int pos1=log.find(":");int pos2=log.rfind(":");int  id=std::atoi(log.substr(0,pos1).c_str());string status=log.substr(pos1+1,pos2-pos1-1);int timestamp=std::atoi(log.substr(pos2+1,log.size()).c_str());pair<int,int>item= make_pair(id, timestamp);if(status=="start"){if(!stack.empty()){result[stack.back().first]+=timestamp-stack.back().second;stack.back().second=timestamp;}stack.emplace_back(item);}else{result[id]+=timestamp-stack.back().second+1;stack.pop_back();if(!stack.empty()){stack.back().second=timestamp+1;}}}return result;}
};

时间复杂度:O(n)),其中 n 为全部日志 logs 的数量,n 条日志信息对应总共 n 次入栈和出栈操作。
空间复杂度:O(n),其中 n 为全部日志logs 的数量,n 条日志信息对应n/2次入栈操作,最坏的情况下全部 n/2 条日志入栈后才会依次弹栈。

  • c++函数使用:注意substr函数的形式为s.substr(pos, n),
    需要两个参数,第一个是开始位置,第二个是获取子串的长度。
    函数可以从一个字符串中获取子串,返回一个string,包含s中从pos开始的n个字符的拷贝(pos的默认值是0,n的默认值是s.size() - pos,即不加参数会默认拷贝整个s)。

  • 官方解法中给出了一个按照冒号分割字符串的新方式:

    char type[10];
    int idx, timestamp;

    image-20230818001523096

    在C++中,你可以使用 sscanf 函数来按照指定的格式从一个C风格的字符串中读取数据。这个函数的原型为:

    int sscanf(const char* str, const char* format, ...);
    

    它的工作方式类似于 scanf 函数,但是不是从标准输入读取,而是从给定的字符串中读取数据。

    在你提供的代码中,sscanf 函数的格式字符串是 "%d:%[^:]:%d"。这个格式字符串指示 sscanflog.c_str() 这个字符串中按照以下规则读取数据:

    • %d:读取一个整数,赋值给 idx
    • ::读取一个冒号,但不存储。
    • %[^:]:读取一个非冒号的字符串,赋值给 type。这个格式说明符 %[^:] 使用了 [^:] 表达式,它表示匹配除冒号外的任意字符,这样可以读取 type 的值。
    • ::读取一个冒号,但不存储。
    • %d:读取一个整数,赋值给 timestamp

    所以,当 sscanf 函数调用时,它会根据给定的格式字符串解析 log.c_str() 中的内容,并将解析出的值存储到相应的变量。

    需要注意的是,虽然在C++中可以使用 sscanf 进行字符串解析,但这通常不是最安全的方法,因为它对于输入数据的格式和边界条件的检查相对较弱。在实际应用中,更推荐使用更安全的字符串解析方法,比如使用 std::istringstream 或者字符串分割库来处理字符串。

9. 591. 标签验证器

image-20230818145311078

image-20230818145353606

image-20230818145413601

解法:栈模拟

字符串模拟,假设字符串 s 长度为 n,当前处理到的位置为 i,根据以下优先级进行检查:

优先尝试检查以 i 为开始的连续段是否为 CDATA,若能匹配到开头,则尝试匹配到 CDATA 的结尾处,并更新 i,若无法找到结尾,返回 False;
1.尝试匹配 s[i] <,若满足,则根据 s[i+1] 是否为 / 来判断当前 TAG_NAME 是处于右边还是左边,然后将 TAG_NAME 取出,记为 tag,判断 tag 长度是否合法,不合法返回 False,合法则根据是左边还是右边的 TAG_NAME 分情况讨论:
2.位于左边的 TAG_NAME:将其加入栈中,等待右边的 TAG_NAME 与其匹配;
位于右边的 TAG_NAME:将其与当前栈顶的元素进行匹配,若栈为空或匹配不上,返回 False.
3.其余情况则为普通字符。
最后由于整个 s 应当被一对 TAG_NAME 所包裹,因此当 i=0时,不能是情况 1和情况 3,需要特判一下。

注意细节:因为题目中说到代码必须被合法的闭合标签包围,因此当字符串未遍历完成,stack不能为空;所以对于pop标签后,若是栈为空,且没有遍历完字符串,则返回false,如“<A></A><B></B>” 情况,此时A标签pop时候,stack已经为空。

同理当为其他字符时,必须保证stack内有标签存在

代码:

class solution69 {
public:string findTagName(string &s,int index,int&end){int i=index;string reuslt="";int n=s.size();while(i<n&&isupper(s[i])){reuslt+=s[i];i++;}end=i;if(i==n){return "";}if(s[i]=='>'){return reuslt;}else{return "";}}bool isCdata(string &s,int index,int &end){int i=index;int n=s.size();string c="[CDATA[";int cnt=0;for( i=index;i<n;i++){if(s[i]!=c[cnt]){return false;}cnt++;if(cnt==c.size()){break;}}i++;while(i<n){if(s[i]==']'){if(i+2<n&&s[i+1]==']'&&s[i+2]=='>'){end=i+2;return true;}}i++;}return false;}bool isValid(string code) {vector<string>stack;int n=code.size();for(int i=0;i<n;i++){int end;if(code[i]=='<'){if(i+1<n&&code[i+1]=='/'){//end标签string s= findTagName(code,i+2,end);if(stack.empty())return false;string start=stack.back();if(s.empty()||s!=start){return false;}stack.pop_back();if(stack.empty()&&end!=n-1)return false;}else if(i+1<n&&code[i+1]=='!'){bool flag= isCdata(code,i+2,end);if(!flag){return false;}if(stack.empty()&&end!=n-1)return false;}else{string s= findTagName(code,i+1,end);if(s.empty()||s.size()>9||s.size()<1)return false;stack.emplace_back(s);}i=end;}else{if(stack.empty())return false;}}return stack.empty();}
};

时间复杂度:O(n)

空间复杂度:O(n)

10. 32.最长有效括号

image-20230818164242810

解法:

我们定义 dp[i] 表示以下标 i 字符结尾的最长有效括号的长度。我们将dp 数组全部初始化为 0 。显然有效的子串一定以 ‘)’ 结尾,因此我们可以知道以 ‘(’ 结尾的子串对应的dp 值必定为 0 ,我们只需要求解 ‘‘)’ 在 dp 数组中对应位置的值。

  1. 我们从前往后遍历字符串求解 dp 值,我们每两个字符检查一次:s[i−1]=‘(’,也就是字符串形如 “……()”,我们可以推出:
    dp[i]=dp[i−2]+2
    我们可以进行这样的转移,是因为结束部分的 “()” 是一个有效子字符串,并且将之前有效子字符串的长度增加了 2 2 2

    image-20230818183750289
  2. s[i−1]== ′)’

    这种情况下,如果前面有和s[i]组成有效括号对的字符,即形如 ((…)),这样的话,就要求s[i−1]位置必然是有效的括号对,否则s[i]s[i]s[i]无法和前面对字符组成有效括号对。

    这时,我们只需要找到和s[i]配对对位置,并判断其是否是 ( 即可。和其配对位置为:i-dp[i-1]+1.

    若:s[i-dp[i-1]-1]==‘(’:

    有效括号长度新增长度 2,i位置对最长有效括号长度为 i-1位置的最长括号长度加上当前位置新增的 2,那么有:

    dp[i]=dp[i−1]+2

    值得注意的是,i−dp[i−1]−1 和 i 组成了有效括号对,这将是一段独立的有效括号序列,如果之前的子序列是形如 (…) 这种序列,那么当前位置的最长有效括号长度还需要加上这一段。所以:

    dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2;

    这个地方很容易遗漏,因为如用例)()(()),如果直接dp[i]=dp[i-1]+2就很容易遗漏。

    image-20230818184301216

    代码:

class solution70 {
public:
int longestValidParentheses(string s) {
int n=s.size();
int *dp=new int[n];
std::fill(dp,dp+n,0);
int result=0;
for(int i=1;i<s.size();i++){
if(s[i]‘)’)
{
if(s[i-1]
‘(’)
{
if(i-2>=0)
dp[i]=dp[i-2]+2;
else
dp[i]=2;
}
else{
if(i-dp[i-1]>0&&s[i-dp[i-1]-1]==‘(’){
dp[i]=dp[i-1]+2;
int pre=i-dp[i-1]-2>=0?dp[i-dp[i-1]-2]:0;
dp[i]+=pre;
}
}
}
result=max(result,dp[i]);
}
delete []dp;
return result;
}
};


时间复杂度: O(n),其中 n 为字符串的长度。我们只需遍历整个字符串一次,即可将 dp 数组求出来。空间复杂度: O(n)】。我们需要一个大小为 n 的 dp 数组。### 11. 385.迷你语法分析器![image-20230818190540865](https://img-blog.csdnimg.cn/img_convert/8b8bbb5264ed4ce70d05a38bad49fa7b.png)解法:这道题用栈来解决,主要是要读懂辅助结构NestedInteger的几个函数的意思。1. `NestedInteger();` :Constructor initializes an empty nested list. (构造函数初始化一个空的嵌套列表。)
2. `NestedInteger(int value);` :Constructor initializes a single integer.(构造函数初始化一个整数。)
3. `void add(const NestedInteger &ni);` :Set this NestedInteger to hold a nested list and adds a nested integer to it.(设置这个NestedInteger保存一个嵌套的列表,并向它添加一个嵌套的整数。)3个方法的具体效果如下:```csharpNestedInteger ans = NestedInteger();    // ans = []ans.add(NestedInteger(789));            // ans = [789]NestedInteger temp = NestedInteger();   // temp = []temp.add(NestedInteger(456));           // temp = [456]temp.add(ans);                          // temp = [456, [789]]NestedInteger res = NestedInteger();    // res = []res.add(NestedInteger(123));            // res = [123]res.add(temp);                          // res = [123, [456, [789]]]

因此利用栈来遍历字符串,如果遇到‘[’,则表示一个新的NestedInteger对象,将其入栈,如果遇到的是“,“或者”]",则表示一个数字,或者一个NestedInteger对象的结束,需要将这个数字添加到栈顶的对象中去。

以下又可以分成两种情况:若"]"或“,”左边是数字,说明是独立的对象,因此将数字加入栈顶对象中;

若“]”的左边是“]”带边对象的嵌入,因此当前栈顶的对象应该嵌入到它的上一个对象中,如789嵌入到456对象中

其中还需要注意一些特殊情况,若不以“[”开头,则说明只包含一个数字对象;而且注意可能有负数,需要判断

代码:

/*** // 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(atoi(s.c_str()));vector<NestedInteger>stack;int num=0;bool flag=false;for(int i=0;i<s.size();i++){char c=s[i];if(c=='-'){flag=true;continue;}else if(isdigit(c)){num=num*10+int(c-'0');}//检测到左括号,同步往栈中添加对象else if(c=='[')stack.emplace_back(NestedInteger());else if (c==','||c==']'){//如果其左边是整数,说明它是一个对象,如果c为逗号,说明其对象有其他嵌套对象,为]说明为完整对象if(isdigit(s[i-1])){if(flag){num*=-1;}stack.back().add(NestedInteger(num));}num=0;flag=false;if(c==']'&&stack.size()>1){//将其和上一个对象合并NestedInteger n=stack.back();stack.pop_back();stack.back().add(n);}}}return stack.back();}
};

时间复杂度:O(n),其中 n是 s 的长度。我们需要遍历 s 的每一位来解析。

空间复杂度:O(n),其中 n 是 s 的长度。栈的深度最多为 O(n)。

12. 341. 扁平化嵌套列表迭代器

image-20230818200521322

解法一:递归

注意,递归是最简单的方法,但是在面试过程中,面试官可能想考察的不会是递归的方法,而是迭代的方法

因为整个nestedList结构可看成树形结构的一种表达形式,树上的叶子结点就是一个正数,而非叶子结点就是一个列表。

因此我们可以对这个nestedList结构进行深搜,在深搜的过程中,将最终结果存入数组。

如上述的例子 n e x t e d L i s t = [ [ 1 , 1 ] , 2 , [ 1 , 1 ] ] nextedList=[[1,1],2,[1,1]] nextedList=[[1,1],2,[1,1]]那么通过dfs将结果存入 r e s u l t result result得到 [ 1 , 1 , 2 , 1 , 1 ] [1,1,2,1,1] [1,1,2,1,1]

所以 h a s n e x t hasnext hasnext即判断 r e s u l t result result中是否有整数, n e x t next next则是返回 r e s u l t result result中的整数.

/*** // 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:vector<int>result;vector<int>::iterator iters;void dfs(const vector<NestedInteger> &nestedList){for(auto &nest:nestedList){if(nest.isInteger()){result.emplace_back(nest.getInteger());}else{dfs(nest.getList());}}}NestedIterator(vector<NestedInteger> &nestedList) {dfs(nestedList);iters=result.begin();}int next() {return *iters++;}bool hasNext() {return iters!=result.end();}
};/*** Your NestedIterator object will be instantiated and called as such:* NestedIterator i(nestedList);* while (i.hasNext()) cout << i.next();*/

时间复杂度:初始化O(n),next 和 hasNext 为O(1)。其中 n 是嵌套的整型列表中的元素个数。

空间复杂度:O(n)。需要一个数组存储嵌套的整型列表中的所有元素。

解法二:迭代

基于迭代的方式,利用栈来模拟递归的过程,

  • 初始化的时候,由于栈是先进先出的,可以利用vector模拟栈,将所有元素逆序加入栈中。

  • 在hasNext()方法中,判断栈顶是否为int

  • 若为ints说明有下一个元素,返回true,next()函数被调用,此时弹出栈顶元素

  • 如果是list就将当前列表的各个元素再放入栈中【逆序】

    使用栈的好处:就是不用一开始就展开所有元素,只在需要展开的时候展开

代码:

/*** // 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:vector<NestedInteger>result;NestedIterator(vector<NestedInteger> &nestedList) {for(int i=nestedList.size()-1;i>=0;i--){result.push_back(nestedList[i]);}}int next() {int next=result.back().getInteger();result.pop_back();return next;}bool hasNext() {if(result.empty()){return false;} auto item=result.back();if(item.isInteger()){return true;}else{//此时展开NestedInteger 利用hasnext判断//注意必须为逆序放入,不然弹出顺序又问题auto num=item.getList();result.pop_back();for(int i=num.size()-1;i>=0;i--){result.push_back(num[i]);}return hasNext();}}
};/*** Your NestedIterator object will be instantiated and called as such:* NestedIterator i(nestedList);* while (i.hasNext()) cout << i.next();*/

时间复杂度:初始化和 =next 为 O(1),hasNext 为均摊 O(1)。

空间复杂度:O(n)。最坏情况下嵌套的整型列表是一条链,我们需要一个O(n) 大小的栈来存储链上的所有元素。

13. 394.字符串解码

image-20230908113651116 image-20230908113719483

解法1:辅助栈

  • 首先创建两个栈,数字栈nums和字符串栈str
  • 遍历该字符串s,对于其中一个字符c,若为数字,则入数字栈;若为字符[a-z,A-Z],则继续遍历,知道该位置不为字符类型,将其拼接成字符串,入str栈;
  • 若c为"[“,则入str栈,若c为”]"。那么str栈不断弹栈,直到遇到“[”。此时栈中的字符串必须按照逆序拼接组成新的字符串。
  • 然后取得数字栈顶数字n,将该字符串重复n次后,加入str栈中“[”必定和数字配对,因此若出现“[”,数字栈顶必有数字
  • 最后遍历结束后,将str栈中元素顺序拼接就能得到结果

代码:

class solution72 {
public:vector<int>nums;vector<string>str;string laststr="";string decodeString(string s) {int i=0;while(i<s.size()){int num=0;int flag=0;while(isdigit(s[i])){num=(s[i]-'0')+10*num;i++;flag=1;}if(flag)nums.emplace_back(num);string c="";flag=0;while(isalpha(s[i])){c+=s[i];i++;flag=1;}if(flag)str.emplace_back(c);if(s[i]=='['){str.emplace_back(string(1,s[i]));i++;}if(s[i]==']'){int num=nums.back();nums.pop_back();string s="";while(str.back()!="["){s.insert(0,str.back());str.pop_back();}str.pop_back();string top="";for(int i=0;i<num;i++){top+=s;}str.emplace_back(top);i++;}}for(auto s:str){laststr+=s;}return laststr;}
};

时间复杂度:O(S)
空间复杂度:O(S)

解法2:递归

见官方题解:[394. 字符串解码 - 力扣(LeetCode)](

相关文章:

leetcode刷题--栈与递归

文章目录 1. 682 棒球比赛2. 71 简化路径3. 388 文件的最长绝对路径4. 150 逆波兰表达式求值5. 227. 基本计算器II6. 224. 基本计算器7. 20. 有效的括号8. 636. 函数的独占时间9. 591. 标签验证器10. 32.最长有效括号12. 341. 扁平化嵌套列表迭代器13. 394.字符串解码 1. 682 棒…...

自然语言处理——数据清洗

一、什么是数据清洗 数据清洗是指发现并纠正数据文件中可识别的错误的最后一道程序&#xff0c;包括检查数据一致性&#xff0c;处理无效值和缺失值等。与问卷审核不同&#xff0c;录入后的数据清理一般是由计算机而不是人工完成。 ——百度百科 二、为什么要数据清洗 现实生…...

MySql学习笔记07——存储引擎介绍

存储引擎 Mysql中特有的术语&#xff0c;Oracle中没有。 存储引擎就是一个表存储/组织数据的方式。不同的存储引擎&#xff0c;表存储数据的方式不同。 指定存储引擎 在建表的时候可以在最后小括号的")"的右边使用&#xff1a; ENGINE来指定存储引擎。 CHARSET来…...

Java基础学习笔记-1

前言 Java 是一门强大而广泛应用的编程语言&#xff0c;它的灵活性和跨平台特性使其成为许多开发者的首选。无论您是刚刚入门编程&#xff0c;还是已经有一些编程经验&#xff0c;掌握 Java 的基础知识都是构建更复杂程序的关键。 本学习笔记旨在帮助您深入了解 Java 编程语言…...

以太坊虚拟机

1.概述 以太坊虚拟机 EVM 是智能合约的运行环境。它不仅是沙盒封装的&#xff0c;而且是完全隔离的&#xff0c;也就是说在 EVM 中运行代码是无法访问网络、文件系统和其他进程的。甚至智能合约之间的访问也是受限的。 2.账户 以太坊中有两类账户&#xff08;它们共用同一个…...

说说BTree和B+Tree

分析&回答 B树索引是B树在数据库中的一种实现&#xff0c;是最常见也是数据库中使用最为频繁的一种索引。B树中的B代表平衡&#xff08;balance&#xff09;&#xff0c;而不是二叉&#xff08;binary&#xff09;&#xff0c;因为B树是从最早的平衡二叉树演化而来的。 接…...

8.1.3 Bit representation and coding - 解读

这段描述定义了一些序列&#xff0c;并规定了它们在编码信息时的使用方式。下面是对每个序列的解析&#xff1a; 1. 序列X&#xff1a;在位持续时间的一半之后&#xff0c;将发生一个“暂停”。这个序列用于表示逻辑“1”。 2. 序列Y&#xff1a;在整个位持续时间内&#xff0c…...

spring 理解

spring容器 程序启动时&#xff0c;会给spring容器一个清单&#xff0c;清单中列出了需要创建的对象以及对象依赖关系&#xff0c;spring容器会创建和组装好清单中的对象&#xff0c;然后将这些对象存放在spring容器中&#xff0c;当程序中需要使用的时候&#xff0c;可以到容…...

实战SpringMVC之CRUD

目录 一、前期准备 1.1 编写页面跳转控制类 二、实现CRUD 2.1 相关依赖 2.2 配置文件 2.3 逆向生成 2.4 后台代码完善 2.4.1 编写切面类 2.4.2 编写工具类 2.4.3 编写biz层 2.4.4 配置mapper.xml 2.4.5 编写相应接口类&#xff08;MusicMapper&#xff09; 2.4.6 处…...

TCP机制之连接管理(三次握手和四次挥手详解)

TCP的连接管理机制描述了连接如何创建以及如何断开! 建立连接(三次握手) 三次握手的过程 所谓建立连接就是通信双方各自要记录对方的信息,彼此之间要相互认同;这里以A B双方确立男女朋友关系为例: 从图中可以看出,通信双方各自向对方发起一个"建立连接"的请求,同时…...

NLP(3)--GAN

目录 一、概述 二、算法过程 三、WGAN 1、GAN的不足 2、JS散度、KL散度、Wasserstein距离 3、WGAN设计 四、Mode Collapse and Mode Dropping 1、Mode Collapse 2、Mode Dropping 3、FID 四、Conditional GAN 一、概述 GAN&#xff08;Generative Adversial Networ…...

无涯教程-JavaScript - IMLOG2函数

描述 IMLOG2函数以x yi或x yj文本格式返回复数的以2为底的对数。可以从自然对数计算复数的以2为底的对数,如下所示- $$\log_2(x yi)(log_2e)\ln(x yi)$$ 语法 IMLOG2 (inumber)争论 Argument描述Required/OptionalInumberA complex number for which you want the bas…...

SpringBoot复习:(61)拦截器(HandlerInterceptor)的用法

一、自定义拦截器&#xff1a; package cn.edu.tju.interceptor;import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRespo…...

【PyQT5教程】-01入门PyQT5

PyQT介绍 1.Qt 1.1 介绍 Qt&#xff08;读作“cute”&#xff09;是一个跨平台的C应用程序开发框架&#xff0c;最初由挪威公司Trolltech&#xff08;现在是Qt公司的一部分&#xff09;开发。Qt提供了一系列工具和类库&#xff0c;用于开发图形界面应用程序、命令行工具和服务…...

判断字符串s是否为字符串t的子序列

题目&#xff1a;给定字符串 s 和 t &#xff0c;判断 s 是否为 t 的子序列。 字符串的一个子序列是原始字符串删除一些&#xff08;也可以不删除&#xff09;字符而不改变剩余字符相对位置形成的新字符串。&#xff08;例如&#xff0c;"ace"是"abcde"的一…...

数据结构之队列的实现(附源码)

目录 一、队列的概念及结构 二、队列的实现 拓展&#xff1a;循环队列 三、初学的队列以及栈和队列结合的练习题 一、队列的概念及结构 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(Fi…...

[A题]2023 年全国大学生数学建模比赛思路、代码更新中.....

&#x1f4a5;1 概述 构建以新能源为主体的新型电力系统&#xff0c;是我国实现“碳达峰”“碳中和”目标的一项重要措施。塔式太阳能光热发电是一种低碳环保的新型清洁能源技术[1]。定日镜是塔式太阳能光热发电站&#xff08;以下简称塔式电站&#xff09;收集太阳能的基本组…...

Tailwind 练手项目

Tailwind 练手项目 用到的技巧 Tailwind CSS 速成 应该都提过了&#xff0c;我不记得这里有什么特别新的知识 整体完成图大概这样&#xff1a; 一个纯静态页面&#xff0c;没有做 JS 之类的特效&#xff0c;不过做了移动端适配&#xff0c;说实话我写到一半的时候改了不少………...

SpringMVC_SSM整合

一、回顾SpringMVC访问接口流程 1.容器加载分析 容器分析 手动注册WebApplicationContext public class ServletConfig extends AbstractDispatcherServletInitializer {Overrideprotected WebApplicationContext createServletApplicationContext() {//获取SpringMVC容器An…...

【操作系统】电脑上没有IIS怎么办

文章目录 前言一、查看二、解决 前言 有的新机刚开始在计算机-管理-服务下没有IIS网络服务怎么办。 一、查看 桌面计算机/此电脑 鼠标右键&#xff1a;管理 服务和应用 发现没有IIS 二、解决 控制面板 程序和功能 启动或关闭Windows功能 IIS相关的所有功能选中&#xff…...

【vue】vue项目中批量下载文件并打压缩包

前言 一开始用的是循环单个文件下载&#xff0c;即从后台获取到文件url列表&#xff0c;循环对每个url单独进行下载&#xff0c;这样的问题是每调用一次下载&#xff0c;浏览器都会进行“另存为”的弹框&#xff0c;很麻烦&#xff01;&#xff01;&#xff01; 关闭“下载前…...

Linux中的软件管家——yum

目录 ​编辑 一&#xff0c;软件安装的方式 二&#xff0c;对yum的介绍 1.yum的作用 2&#xff0c;yum的库 三&#xff0c;yum下载软件的操作 1.yumlist 2.yuminstall 3.yumremove 四&#xff0c;yum源的转换 一&#xff0c;软件安装的方式 软件安装的方式大概分为三种…...

安卓绘制原理概览

绘制原理 Android 程序员都知道 Android 的绘制流程分为 Measure、Layout、Draw 三步骤&#xff0c;其中 Measure 负责测量 View 的大小Layout 负责确定 View 的位置Draw 负责将 View 画在屏幕上 由 ViewRootImpl 实现的 performTraversal 方法是 Measure、layout、draw 的真正…...

接口测试工具开发文档

1 开发规划 1.1 开发人员 角 色 主要职责 负责模块 人员 备注 n xxx模块 xxx 1.2 开发计划 <附开发计划表> 1.3 开发环境和工具 开发工具 工具 作用 Notepad 编辑器 Perl 解释器 2 总体设计 设计思路&#xff1a;因为测试app和server。首先必须…...

面试题速记:JavaScript有哪些数据类型,它们的区别是?

JavaScript有哪些数据类型&#xff0c;它们的区别&#xff1f; JavaScript共有八种数据类型&#xff0c;分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。 其中 Symbol 和 BigInt 是ES6 中新增的数据类型&#xff1a; ●Symbol 代表创建后独一无二…...

Spring Cloud面试题

为什么需要学习Spring Cloud 不论是商业应用还是用户应用&#xff0c;在业务初期都很简单&#xff0c;我们通常会把它实现为单体结构的应用。但是&#xff0c;随着业务逐渐发展&#xff0c;产品思想会变得越来越复杂&#xff0c;单体结构的应用也会越来越复杂。这就会给应用带…...

计算机网络自顶向下-web页面请求历程

1. 准备: DHCP、 UDP、 IP 和以太网 假定 Bob 启动他的便携机&#xff0c;然后将其用一根以太网电缆连接到学校的以太网交换机 &#xff0c; 交换机与学校的路由器相连。学校的路由器与一个 ISP 连接&#xff0c; 本例中 ISP 为 comcast.net &#xff0c;为学校提供了 DNS 服务…...

打造西南交通感知新范式,闪马智能携手首讯科技落地创新中心

9月4日&#xff0c;2023年中国国际智能产业博览会&#xff08;以下简称“智博会”&#xff09;在重庆拉开帷幕。大会期间&#xff0c;由上海闪马智能科技有限公司&#xff08;以下简称“闪马智能”&#xff09;与重庆首讯科技股份有限公司&#xff08;以下简称“首讯科技”&…...

Android11去掉Settings中的网络和互联网一级菜单

碰到一个不要wifi不要蓝牙的项目&#xff0c;客户要求去掉Settings中的网络和互联网一级菜单&#xff0c;因为硬件都不贴&#xff0c;所以软件对应也要去掉。 我们可以根据packages/apps/Settings/res/xml/top_level_settings.xml的布局文件找到TopLevelNetworkEntryPreferenc…...

基于Python开发的五子棋小游戏(源码+可执行程序exe文件+程序配置说明书+程序使用说明书)

一、项目简介 本项目是一套基于Python开发的五子棋小游戏&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&a…...