公式串问题及约瑟夫环问题

目录

1.公式串问题

 2.约瑟夫环问题


1.公式串问题

首先我们先来看一个原型:

对应letecode链接:

面试题 16.26. 计算器 - 力扣(LeetCode) (leetcode-cn.com)

题目描述:

给定一个包含正整数、加(+)、减(-)、乘(*)、除(/)的算数表达式(括号除外),计算其结果。

表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格  。 整数除法仅保留整数部分。

示例 1:

输入: "3+2*2"
输出: 7
示例 2:

输入: " 3/2 "
输出: 1
示例 3:

输入: " 3+5 / 2 "
输出: 5
说明:

你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。

解题思路:

1.首先定义一个链表

2.如果遇到空格直接continue跳过

3.遇到数字将其存到变量num中

4.遇到操作符首先看链表尾部是不是*或者/并将其弹出,如果是则取链表尾部的数字并将运算结果放入链表尾部,然后再将该运算符放入链表中,否则将其放入栈中

5.将链表中剩余的元素进行计算(注意计算要从左往右算)

对应图解:

公式串问题及约瑟夫环问题

1. 首先遇到3存到num中

2.遇到+号查看链表尾部是否是乘号或者除号,此时栈为空。直接将3和+号放入链表中,并将num置成0

 

公式串问题及约瑟夫环问题

 3.然后再遇到2将其放入num中

4.遇到*检查链表尾部是否为乘号或者除号发现不是直接加入到链表中去并将num置0。

公式串问题及约瑟夫环问题

 5.遇到2保存到num中此时字符串变量结束继续看链表尾部是否为乘号或者除号,发现是的将2取出进行运算结果为4放入链表中。

公式串问题及约瑟夫环问题

 

6.结算从左往右算即可

 对应代码:

class Solution {
public:
    int calculate(string s) {
         list<string>stk;
         int i=0;
        long long  num=0;
         for(int i=0;i<s.size();i++){
             if(s[i]==' '){//空格直接跳过
                 continue;
             }
             else if(isdigit(s[i])){//数字
                 num=num*10+s[i]-'0';
             }
             else{//操作符
                 addNum(stk,num);
                 stk.push_back(string(1,s[i]));
                 num=0;
             }
         }
         addNum(stk,num);
         return getNum(stk);

    }
    void addNum(list<string>&stk,int num){
    if(!stk.empty()){
        int cur=0;
        string top=stk.back();//拿出一个运算符看是不是*或者/如果不是则将其放入栈中
        stk.pop_back();
        if(top=="+"||top=="-"){
            stk.push_back(top);
        }
        else{
           
            cur=stoi(stk.back());
             stk.pop_back();
            num=top=="*"?(cur*num):(cur/num);
        }
    }
    stk.push_back(to_string(num));
}

int getNum(list<string>&stk){
    int res=0;
    string cur;
    int num=0;
    bool add=true;
    while(!stk.empty()){//从左往右算
        cur=stk.front();
        stk.pop_front();
        if(cur=="+"){
            add=true;
        }
        else if(cur=="-"){
            add=false;
        }
        else{
            num=stoi(cur);
           
            res+=add?num:-num;
        }
    }
    return res;
}
};

下面我们来看这一类题: 

对应牛客网链接:

公式字符串求值_牛客题霸_牛客网 (nowcoder.com)

题目描述:

给定一个字符串str,str表示一个公式,公式里可以有整数,加减乘除和左右括号,返回公式的计算结果(注意:题目中所有运算都是整型运算,向下取整,且保证数据合法,不会出现除0等情况)。

输出一行字符串,代表str(1 \leq length_{str} \leq 1000 )(1≤lengthstr​≤1000)(保证str计算的结果不会出现除零,int溢出等情况)。

输出一个整数,代表表达式的计算结果。

输入:

48*((70-65)-43)+8*1
输出:
-1816

输入:

3+1*4

复制输出:

7

本题与上面那道题不一样的地方在于多了括号这让我们计算起来很困难如果我们能采用某种方式巧妙的能够化简成上面那个样子问题就解决了。

思路:

1.定义一个递归函数f返回两个值一个是计算结果一个是计算到了那。

2.遇到(就直接递归跳过从下一个位置开始算

3.遇到)就直接停止

4.其余步骤和上面的思路一样

 图解:

对应公式串:48*((70-65)-43)+8*1

1.首先遇到48将其放入到num中

2.遇到乘号重复上题的步骤

公式串问题及约瑟夫环问题

 

3.遇到左括号递归从下一个位置开始算

公式串问题及约瑟夫环问题

 3.遇到左括号继续递归从下一位置开始算

公式串问题及约瑟夫环问题

 4.此时来到70将其保存到num中

5.遇到-查看链表中的末尾是否为乘号或者除号,发现不是的将70和-放入链表中并将num置0。

公式串问题及约瑟夫环问题

 6.然后遇到65将其放入num中

7.遇到右括号停止重复步骤5的相关操作,计算链表中的结果并将结果和位置返回。

8.上层递归函数中的num接收计算结果,并从下一个位置开始计算。

9.重复上述步骤即可得到结果

对应代码:

#include<iostream>
#include<vector>
#include<list>
#include<string>
using namespace std;

void addNum(list<string>&stk,int num){
    if(!stk.empty()){
        int cur=0;
        string top=stk.back();//拿出一个运算符看是不是*或者/如果不是则将其放入栈中
        stk.pop_back();
        if(top=="+"||top=="-"){//将其放入栈中
            stk.push_back(top);
        }
        else{
           // cout<<stk.top()<<endl;
            cur=stoi(stk.back());
             stk.pop_back();
            num=top=="*"?(cur*num):(cur/num);//计算
        }
    }
    stk.push_back(to_string(num));
}

int getNum(list<string>&stk){
    int res=0;
    string cur;
    int num=0;
    bool add=true;
    while(!stk.empty()){//从左往右算
        cur=stk.front();
        stk.pop_front();
        if(cur=="+"){
            add=true;
        }
        else if(cur=="-"){
            add=false;
        }
        else{
            num=stoi(cur);
           
            res+=add?num:-num;
        }
    }
    return res;
}
vector<int>value(const string&str,int i){
       list<string>stk;
       int num=0;
       vector<int>bra;
       while(i<str.size()&&str[i]!=')'){
           if(isdigit(str[i])){
               num=num*10+str[i++]-'0';
           }
           else if(str[i]!='('){//遇到的是运算符
               addNum(stk,num);
               
               stk.emplace_back(string(1,str[i++]));
               num=0;
           }
           else{//遇到左括号
               bra=value(str, i+1);
               num=bra[0];
               i=bra[1]+1;
           }
       }
      addNum(stk, num);
     return {getNum(stk),i};
        
}
int main(){
    string s;
    cin>>s;
    cout<<value(s,0)[0];
}

在这里还有一道和这题基本一样的题只不过多了一个空格我们只要跳过即可。

对应letecode连接:

227. 基本计算器 II - 力扣(LeetCode) (leetcode-cn.com) 

题目描述: 

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

示例 1:

输入:s = "3+2*2"
输出:7
示例 2:

输入:s = " 3/2 "
输出:1
示例 3:

输入:s = " 3+5 / 2 "
输出:5
 

提示:

1 <= s.length <= 3 * 105
s 由整数和算符 ('+', '-', '*', '/') 组成,中间由一些空格隔开
s 表示一个 有效表达式
表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
题目数据保证答案是一个 32-bit 整数

 对应代码:

class Solution {
public:
    int calculate(string s) {
        return value(s,0)[0];
    }
    void addNum(list<string>&stk,int num){
    if(!stk.empty()){
        int cur=0;
        string top=stk.back();//拿出一个运算符看是不是*或者/如果不是则将其放入栈中
        stk.pop_back();
        if(top=="+"||top=="-"){//将其放入栈中
            stk.push_back(top);
        }
        else{
           // cout<<stk.top()<<endl;
            cur=stoi(stk.back());
             stk.pop_back();
            num=top=="*"?(cur*num):(cur/num);//计算
        }
    }
    stk.push_back(to_string(num));
}

int getNum(list<string>&stk){
    int res=0;
    string cur;
    int num=0;
    bool add=true;
    while(!stk.empty()){//从左往右算
        cur=stk.front();
        stk.pop_front();
        if(cur=="+"){
            add=true;
        }
        else if(cur=="-"){
            add=false;
        }
        else{
            num=stoi(cur);
           
            res+=add?num:-num;
        }
    }
    return res;
}
vector<int>value(const string&str,int i){
       list<string>stk;
       long long  num=0;
       vector<int>bra;
       while(i<str.size()&&str[i]!=')'){
           if(str[i]==' '){
               i++;
               continue;
           }
           if(isdigit(str[i])){
               num=num*10+str[i++]-'0';
           }
           else if(str[i]!='('){//遇到的是运算符
               addNum(stk,num);
               
               stk.emplace_back(string(1,str[i++]));
               num=0;
           }
           else{//遇到左括号
               bra=value(str, i+1);
               num=bra[0];
               i=bra[1]+1;
           }
       }
      addNum(stk, num);
     return {getNum(stk),i};
        
}
};

 2.约瑟夫环问题

对应牛客网链接:

环形链表的约瑟夫问题_牛客题霸_牛客网 (nowcoder.com)

题目描述:

据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以*选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号。

一行两个整数 n 和 m, n 表示环形链表的长度, m 表示每次报数到 m 就自杀。

输出最后存活下来的人编号(编号从1开始到n)

输入:

5 2

复制输出:

3

解题思路:

公式串问题及约瑟夫环问题

 我们首先来看一下一个函数的图像x%i. 假设i=4.

公式串问题及约瑟夫环问题

这与题目又有什么联系呢?

我们来看一下编号和报数之间的关系假设3个人成一个环

1         1

2          2

3          3

4          1

5          2

6          3

7          1

画出对应的图像

 公式串问题及约瑟夫环问题

 根据数学方法可以推导出 编号=(报数-1)%i+1

 根据题目的意思最后一个活下来的人的遍号一定是1,如果我们能够有一个·函数根据这一轮的编号能够推出上一轮的编号,这样不就解决了吗?

下面我们来看

1 2 3 4 5 6 7  7个人站成一圈现在长度为7,现在每数到3就杀人,变成。

5 6 — 1 2 3 4。

我们画出老编号和新编号的图像,得到其函数表达式:老编号=((新-1)+3)%i+1.这是三节点被杀如果是s节点被杀那么函数表达式为:

老编号=((新-1)+s)%i+1.但是我们发现多一个s,但是每次数到s就杀人不就是上面的那个公式吗?s=(报数-1)%i+1,报数也就是数到几就杀人假设为m。所以老编号=((新-1)+(m-1)%i+1)%i+1=(新+(m-1)%i)%i+1=(新+m-1)%i.

这个化简过程是假设m-1=k*i+res;将其带入到这两个公式中发现他们是等效的

对应代码:

#include<iostream>
using namespace std;
int main(){
    int n,m;
    cin>>n>>m;
    int ans=1;
    for(int i=2;i<=n;i++){
        ans=(ans+m-1)%i+1;//由新编号求老编号
    }
    
    cout<<ans;
}
上一篇:牛客数组和字符串2.5


下一篇:jQuery通知查件noty