本文记录作者刷题过程中与数学情景相关的题目。

一、实现函数

50. Pow(x, n)(中等函数)

剑指 Offer 16. 数值的整数次方(中等函数)

class Solution {
public:
    // x的n次幂等于n个x相乘,请注意,n可能为负数
    // 直接使用while循环,n非常大时会超时,所以需要用快速幂的位运算
    // 同时注意n可能是一个极大的负数,转换为long
    double myPow(double x, int n) {
        if(x==0.0f) return 0.0f;// 0的任何幂都设为0
        if(x==1.0f) return 1.0f;// 1的任何幂都设为1
        double res=1.0f;
        // 特殊情况:n为负数
        long b = n;//排除n为极大的负数
        if(b<0){
            x=1/x;
            b=-b;
        }
        // 快速幂算法的位运算版本
        while(b){
            if(b&1==1) res=res*x;//如果b是奇数,则res直接*x
            x=x*x;//x自乘
            b>>=1;//b右移1位,相当于/2
        }
        return res;
    }
};

剑指 Offer 67. 把字符串转换成整数(中等难度)

8. 字符串转换整数 (atoi)(中等难度)

class Solution {
public:
    /*本题是要将字符串转为有符号的32位整数。
    根据示例,字符串可能存在前端空格、正负号、后缀字母、首字符非数字、数字越界共计5种情况,
    这也是本题的难点*/
    bool isDigit(char c){
        return c-'0'>=0&&c-'0'<=9;
    }
    int strToInt(string str) {
        int n=str.length();
        if(n==0) return 0;//非有效转换输出0
        int start=0;
        int negative=false;
        // 去掉前端空格
        for(char c:str){
            if(c==' ') start++;
            else break;
        }
        if(start==n)return 0;//去掉前导空格后到了末尾
        // // 判断非空首字符
        if (str[start] == '-') {
            negative = true;
            start++;
        }
        else if (str[start] == '+') 
        {
            negative = false;
            start++;
        }
        else if (!isDigit(str[start])){
            return 0;//非数字
        } 
        // 判断
        int ans = 0;
        while (start < n && isDigit(str[start])) {
            int digit = str[start] - '0';
            if (ans > (INT_MAX - digit) / 10) {
                // 本来应该是 ans * 10 + digit > Integer.MAX_VALUE
                // 但是 *10 和 + digit 都有可能越界,所有都移动到右边去就可以了。
                return negative? INT_MIN : INT_MAX;
            }
            ans = ans * 10 + digit;
            start++;
        }
        return negative? -ans : ans;
    }
};

剑指 Offer 20. 表示数值的字符串


二、N位数

剑指 Offer 17. 打印从1到最大的n位数(简单难度)

class Solution {
public:
    /*本题的难点在于n的增加,会让结果数组指数级增加:
    n=1,只有9个数;n=2,就有99个数;n=3,就有999个数
    所以n=n时,推理有n个9位数的数
    这道题返回一个int数组,测试数据也没有溢出
    如果让返回一个int数,可能存在溢出的情况,则难度会提升。
    */
    vector<int> printNumbers(int n) {
        // 计算需要多少个数
        int sum = 1;
        for (int i = 0; i < n; i++) sum *= 10;
        // 向结果数组中添加数字
        vector<int> res(sum - 1);
        for(int i = 0; i < sum - 1; i++) res[i] = i + 1;
        return res;
    }
};

400. 第 N 位数字

class Solution {
public:
    /*题意:将整数序列看做一个大整数,返回其第n位的数字*/
    int findNthDigit(int n) {
        if(n<=9) return n;
        //先确定第n位数字到了几位数,每种数字分别占用字符:9*1,90*2,900*3 等等
        long num=9;
        int d=1;//第n位所在的数字的位数
        while(n>num*d){
            n-=num*d;
            d++;
            num*=10;
        }
        //即为d位数,注意n此时一定大于0,因为若n归0,则在上一while循环不会减
        int m=(n-1)/d+1;
        int p=n-(m-1)*d;//所求即为d位数的第m个数字的第p位
        int k=(int)(num/9+m-1);//所求数字所在的那个数
        return (k/(int)pow(10,d-p))%10;
    }
};

剑指 Offer 43. 1~n 整数中 1 出现的次数

233. 数字 1 的个数

class Solution {
public:
    int countDigitOne(int n) {
        return dfs(n);
    }
    //下面我们都用 1234 和 2345 来举例
    long dfs(int n){
        // 上一级递归 n = 20、10之类的整十整百之类的情况;以及n=0的情况
        if(n== 0) return 0;
        // n < 10 即为个位,这样子只有一个1
        if(n < 10) return 1;
        string s = to_string(n);
        //长度:按例子来说是4位
        int length = s.length();
        //这个base是解题速度100%的关键,本例中的是999中1的个数:300
        // 99的话就是20 ; 9的话就是1 ;9999就是4000 这里大家应该发现规律了吧。
        long base = (length-1)*(int)pow(10,length-2);

        //high就是最高位的数字
        int high = s[0] - '0';
        //cur就是当前所数量级,即1000
        int cur = (int)pow(10,length -1);
        if(high == 1){
            // 最高位为1,1+n-cur就是1000~1234中由千位数提供的1的个数
            // 剩下的f函数就是求1000~1234中由234产生的1的个数
            return base + (1+n-cur)+dfs(n-high*cur); 
        }else{
            // 这个自己思考
            return base*high+cur+dfs(n-high*cur);
        }
    }
};

一、扑克牌相关

剑指 Offer 61. 扑克牌中的顺子(简单难度)

class Solution {
public:
    /*解析:本题要求判断一个5个元素的数组是否是顺子
    稍微思考下,明白首先要从小到大排序,然后判断是否有除0以外的重复数,有则肯定不是顺子
    难点在于0可以看做然后数来填补顺子,如何判断几张0填补顺子是算法的难点?
    解决方案:找到minVal(非0)和maxVal,通过maxVal-minVal的差来判断需要几张0填补
    */
    bool isStraight(vector<int>& nums) {
        int zeroCnt=0;//0的数组
        int n=nums.size();//此题固定为5
        sort(nums.begin(),nums.end());//从小到大排序
        // 遍历数组,记录0的个数并判断是否存在重复数
        for(int i=0;i<n;i++){
            if(nums[i]==0)zeroCnt++;
            else if(i+1<n&&nums[i]==nums[i+1]) return false;
        }
        // maxVal-minVal<5时必定可以
        return nums[n-1]-nums[zeroCnt]<5;
    }
};