leetcode刷题--栈与递归

news2024/11/24 13:33:17

文章目录

      • 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();
                   }

                   else
                       last.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);
                else
                    stack.pop_back();
            }
            else if(item==']'){
                if(stack.empty()||stack.back()!='[')
                    stack.emplace_back(item);
                else
                    stack.pop_back();

            }
            else if(item=='}'){
                if(stack.empty()||stack.back()!='{')
                    stack.emplace_back(item);
                else
                    stack.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个方法的具体效果如下:

```csharp
     NestedInteger 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)](

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/989448.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker 部署vue

1&#xff1a; 首先部署nginx docker run --name nginx -d -p 88:80 nginx 2&#xff1a;访问 http://xxxxxxx:88/ 3: 进入nginx docker exec -it nginx /bin/sh 4: 回到vs&#xff0c;编译项目 npm run build 得到dist文件夹 5&#xff1a;创建docker 6&#xff1a; 将…

量化交易最新12篇顶会论文及开源代码汇总(WWW23、AAAI23、KDD23)

对比普通的交易方式&#xff0c;量化交易具有高效性、稳定性以及强风险控制能力&#xff0c;因此&#xff0c;量化交易在金融领域中已经得到了广泛的应用&#xff0c;目前已有许多大型投资机构和对冲基金将量化交易列为其投资组合的重要部分。 本次分享的是我从各大顶会中精选…

826. 安排工作以达到最大收益;2257. 统计网格图中没有被保卫的格子数;816. 模糊坐标

826. 安排工作以达到最大收益 核心思想&#xff1a;排序维护最大利润。首先我们需要对工人按照能力排序&#xff0c;前面工人满足的最大利润后面的工人肯定是满足的&#xff0c;所以我们只需要用一个tmp来维护小于等于当前工人的最大利润&#xff0c;然后如何得到tmp&#xff…

java.lang.Exception: No runnable methods

无可执行test方法异常&#xff0c;报错为&#xff1a; 1.查看是否添加了Test注解在执行的方法上 2.查看测试类的注解 3.查看test类的导包&#xff0c;一定要是junit的。

【SpringMVC】RESTful风格CRUD实现

目录 一、REST简介 1.1 什么是REST? 1.2 REST风格的优点 1.3 请求方式 二、构建项目 ⭐思路分析 2.1 环境准备 2.1.1 导入相关pom依赖 2.1.2 jdbc.properties&#xff1a;配置文件 2.1.3 配置代码生成器 generatorConfig.xml 2.1.4 spring与mybatis整合的配置文件 s…

开机性能-如何抓取开机systrace

一、理论 1.背景 抓取开机 trace 需要使用 userdebug 版本&#xff0c;而我们测试开机性能问题时都要求使用 user 版本&#xff0c;否则会有性能损耗问题。因此想要在抓取开机性能trace 时&#xff0c;需要在 user 版本上打开 atrace 功能之后才能抓取 trace&#xff0c;默认 …

什么是CE认证?CE认证如何办理?亚马逊欧洲站产品适用范围

什么是CE标志&#xff1f; CE标志是欧盟强制认证标志&#xff0c;代表着产品符合欧洲指令的基本要求。这意味着&#xff0c;只有经过认证并贴上CE标志&#xff0c;你的产品才能合法地进入欧洲市场销售。那么&#xff0c;如何让你的产品贴上CE标志呢&#xff1f;接着往下看。 如…

【刷题篇】贪心算法

文章目录 分割平衡字符串买卖股票的最佳时机Ⅱ跳跃游戏钱币找零 分割平衡字符串 class Solution { public:int balancedStringSplit(string s) {int lens.size();int cnt0;int balance0;for(int i0;i<len;i){if(s[i]R){balance--;}else{balance;}if(balance0){cnt;}}return …

作为程序员,如何快速上手项目

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 本节从“道、术、器”三个方面为大家分享程序员如何快速上手项目。 1 道 道&#xff0c;就是道理、规律&#xff0c;万事万物都有道&#xff0c;软件开发也有道。 软件开发…

strerror函数

目录 strerror 函数介绍&#xff1a; 举例&#xff1a; 使用案例&#xff1a; 优化&#xff1a; perror&#xff1a; strerror 函数介绍&#xff1a; 函数声明&#xff1a; char * strerror ( int errnum );头 文 件&#xff1a;#include <string.h>返 回 值&a…

基于 SpringBoot 的智慧养老平台,附源码、教程

文章目录 1.研究背景2. 技术栈3.系统分析4系统设计4.1 软件功能模块设计4.2数据库设计与实现 5系统详细设计5.1系统功能模块5.2后台登录功能5.2.1管理员功能 源码下载地址 1.研究背景 困扰管理层的许多问题当中,智慧养老平台一定是养老平台不敢忽视的一块。但是管理好智慧养老…

一.使用qt creator 设计显示GUI

一.安装qt creator 二.创建项目 文件-》新建项目 三.使用设计 可以直接使用鼠标拖拽 四.转换为py文件 # from123.py 为导出 py文件的文件名 form.ui 为 qt creator创造的 ui 文件 pyuic5 -o x:\xxx\from123.py form.ui五.显示GUI from PyQt5.QtWidgets import * fr…

【C进阶】指针(二)

六、函数指针数组 数组是一个存放相同类型数据的存储空间&#xff0c;我们已经学习了指针数组 eg&#xff1a; int *arr[10] //整形指针数组-数组-存放的是整形指针 char *arr[5] //字符指针数组-数组-存放的是字符指针 那么把函数的地址存到一个数组中&#xff0…

2023国赛A题保姆级思路代码:定日镜场的优化设计

A题是一套传统的机理分析加规划求解题&#xff0c;首先我们要根据每个月21号的特定时间点建立一个太阳角度框架&#xff0c;根据题目所给出的公式计算效率&#xff0c;还有输出的热功率&#xff0c;然后根据月份求解各种效率&#xff0c;再把年份进行汇总&#xff0c;二三题都是…

阿里国际、亚马逊等跨境电商平台做测评怎么避免踩坑?

由于跨境电商的卖家越来越多&#xff0c;测评逐渐成为众多卖家的关注焦点&#xff0c;在各个跨境电商平台上&#xff0c;随着店铺数量的增加&#xff0c;同一商品之间的竞争也日益激烈&#xff0c;不同于国内的电商平台&#xff0c;跨境电商平台很少提供与卖家客服进行沟通的机…

论文简读 LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS

论文地址&#xff1a;https://arxiv.org/pdf/2106.09685.pdf 项目地址&#xff1a;https://github.com/microsoft/LoRA 全文翻译地址&#xff1a;https://zhuanlan.zhihu.com/p/611557340 本来想自行翻译的&#xff0c;但最近没有空 1、关键凝练 1.1 LORA是什么&#xff1f; …

序列号读取

1.序列号读取 1.1 应用示例目的与思路 获取光盘的外接圆&#xff1b;然后进行极坐标变换&#xff0c;获取字符所在的区域&#xff1b;最后进行字符分割、识别及其在原图上显示。 1.2 应用示例相关算子介绍 (1) mean_image(Image : ImageMean : MaskWidth, MaskHeight : ) …

SpringMVC的整合完成CRUD(增删改查)

SpringMVC是一种基于Java的Web框架&#xff0c;它是Spring框架的一部分。SpringMVC通过使用MVC&#xff08;Model-View-Controller&#xff09;设计模式来组织和管理Web应用程序的开发。 在SpringMVC中&#xff0c;Model代表数据模型&#xff0c;View代表用户界面&#xff0c;C…

2023年证券投资咨询行业研究报告

第一章 概况 1.1 定义 证券投资咨询业务是指取得监管部门颁发的相关资格的机构及其咨询人员为证券投资者或客户提供证券投资的相关信息、分析、预测或建议&#xff0c;并直接或间接收取服务费用的活动。 根据服务对象的不同&#xff0c;证券投资咨询业务可以分为&#xff1a…

2023年股票大宗减持研究报告

第一章 股票大宗减持概述 1.1 概念 大宗减持是指通过大宗交易的方式进行减持&#xff0c;即达到规定的最低限额的证券单笔买卖申报&#xff0c;买卖双方经过协议达成一致并经交易所确定成交的证券交易&#xff0c;其中A股单笔交易数量在30万股&#xff08;含&#xff09;以上…