剑指offer——数学

这篇博客介绍了多种算法题目,包括剪绳子、表示数值的字符串、计算1在数字序列中出现的次数、查找数字序列中某一位的数字以及求丑数的问题。通过动态规划和数学解法,展示了如何高效地解决这些编程挑战,涉及字符串处理、数学计算和递推关系等核心概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(一)剑指 Offer 14- I. 剪绳子

在这里插入图片描述
基本思路:
1.如果我们不考虑数学问题,我们可以采用动态规划的方法来解决这个问题。有点类似于青蛙跳台阶的题。
2.但是这道题还有数学解法:
(1)最优: 3 。把绳子尽可能切为多个长度为 3 的片段,留下的最后一段绳子的长度可能为 0,1,2三种情况。
(2)次优: 2 。若最后一段绳子长度为 22 ;则保留,不再拆为 1+1。
(3)最差: 1 。若最后一段绳子长度为 1 ;则应把一份 3 + 1替换为 2 + 2
推导见官方解答

动态规划代码:

class Solution {
    //每一段都最大
    
    void helpFun (int sum ,int[] save){
        int maxNum = 1;
        for(int i = 1;i<sum; i++){
            int m = i*save[sum-i];
            maxNum = Math.max(m,Math.max(maxNum,i*(sum-i)));
        }
        save[sum] = maxNum;
    }
    public int cuttingRope(int n) {
        int[] save = new int[n+1];
        // save[1] = 1;
        save[2] = 1;
        for(int i = 3;i <= n;i++){
            helpFun(i,save);
            // System.out.println(i+"  "+save[i]);
        }
        return save[n];
    }
}

数学解法如下:

class Solution {
    public int cuttingRope(int n) {
        if(n <= 3) return n - 1;
        int a = n / 3, b = n % 3;
        if(b == 0) return (int)Math.pow(3, a);
        if(b == 1) return (int)Math.pow(3, a - 1) * 4;
        return (int)Math.pow(3, a) * 2;
    }
}

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

在这里插入图片描述
基本思路:
这道题中涉及到状态转化,首先我们需要考虑,究竟有哪几种状态
(1)+/-
(2).
(3)E/e
(4)数字
首先,我们需要根据e来将数字分为指数和底数,因为指数和底数的规则是不同的;
接下来,对于底数,首先我们判断首位是否为±符号,如果是,则不会出现其他加减号;接下来,判断是否有小数点,小数点可以有0或1个。其他的都是数字。
对于指数,我们判断是否首位是否为-符号,如果是,则不会出现其他加减号;接下来,判断是否有小数点,小数点可以有0或1个。其他的都是数字。

class Solution {
    private int index = 0;//全局索引
    private boolean scanUnsignedInteger(String str) {
        //是否包含无符号数
        int before = index;
        while(str.charAt(index) >= '0' && str.charAt(index) <= '9') 
            index++;
        return index > before;
    }
    private boolean scanInteger(String str) {
        //是否包含有符号数
        if(str.charAt(index) == '+' || str.charAt(index) == '-') 
               index++;
        return scanUnsignedInteger(str);
    }
    public boolean isNumber(String s) {
        //空字符串
        if(s == null || s.length() == 0)
            return false;
        //添加结束标志
        s = s + '|';
        //跳过首部的空格
        while(s.charAt(index) == ' ')
            index++;
        boolean numeric = scanInteger(s); //是否包含整数部分
        if(s.charAt(index) == '.') {  
            index++;
            //有小数点,处理小数部分
            //小数点两边只要有一边有数字就可以,所以用||,
            //注意scanUnsignedInteger要在前面,否则不会进
            numeric = scanUnsignedInteger(s) || numeric;
        }
        if((s.charAt(index) == 'E' || s.charAt(index) == 'e')) { 
            index++;
            //指数部分
            //e或E的两边都要有数字,所以用&&
            numeric = numeric && scanInteger(s);
        }
        //跳过尾部空格
        while(s.charAt(index) == ' ')
            index++;
        return numeric && s.charAt(index) == '|' ;
    }
}

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

在这里插入图片描述
基本思路:
我们可以分别计算1出现在个位、十位、百位…上次数的和。
官方解答

class Solution {
    public int countDigitOne(int n) {
        //1在个位+1在十位+1在百位,,,,以此类推
        int total = 0;
        int weight = 0;//当前位的值
        int round = 0;//由高位决定
        int base = 1;//对应的位数,1,10,100等
        int dig = 0;//保存低位数字
        while(n/base > 0){//说明此位上有数值
            weight = (n/base)%10;
            if(weight == 0){
                //如果这一位为0 
                round = n/(base*10);
                total += base*round; 
            }else if (weight == 1){
                dig = n%base;
                if(n/base == 1){
                    total += dig+1;
                }else{
                    round = n/(base*10)+1;
                    total += base*(round-1) + dig + 1;
                }
                
            }else{
                round = n/(base*10)+1;
                total += round * base;
            }
            if(base == 1000000000){//10^9次方是int中最大的10倍数,10次方就超出范围了
                break;
            }
            base *= 10;

        }
        return total;
    }
}

(三)剑指Offer44. 数字序列中某一位的数字

在这里插入图片描述
基本思路:
1.首先我们应当判断这个数字对应的是几位数的范围。规律:
(1)一位数从0到9共十个数,占10位
(2)两位数从10到99共100-10个数,占100x2
(3)三位数从100到999共1000-100-10个数,占1000x3位
以此类推
在这里插入图片描述
2.所求数位 在从数字 start开始的第 [(n - 1) / digit] 个 数字 中( startstart 为第 0 个数字)。

num = start + (n - 1) // digit

class Solution {
    public int findNthDigit(int n) {
        int digit = 1;//几位数
        long start = 1;//10的几次方
        long count = 9;//这一类位数对应的数字的数量
        while (n > count) { 
            n -= count;
            digit += 1;
            start *= 10;
            count = digit * start * 9;
        }
        long num = start + (n - 1) / digit; // 2.
        return Long.toString(num).charAt((n - 1) % digit) - '0'; // 3.

        
    }
}

(四)剑指 Offer 49. 丑数

在这里插入图片描述
基本思路:
每一个丑数必然是由之前的某个丑数与2,3或5的乘积得到的,这样下一个丑数就用之前的丑数分别乘以2,3,5,找出这三这种最小的并且大于当前最大丑数的值,即为下一个要求的丑数。

class Solution {
    public int nthUglyNumber(int n) {
        int p2=0,p3=0,p5=0;
        int[] dp=new int[n];
        dp[0]=1;
        for(int i=1;i<n;i++){
            dp[i]=Math.min(dp[p2]*2,Math.min(dp[p3]*3,dp[p5]*5));//下一个可能的丑数,在下面的数选择一个最小的
            if(dp[i]==dp[p2]*2) p2++;//指向下一个丑数大的数
            if(dp[i]==dp[p3]*3) p3++;//不确定是这个数乘以3大还是p2乘以2大,所以还要进行一次比较
            if(dp[i]==dp[p5]*5) p5++; 
        }
        return dp[n-1];

    }
}

(五)剑指 Offer 67. 把字符串转换成整数

在这里插入图片描述
在这里插入图片描述
基本思路:
1.首先去除掉前面的空格
2.如果有-号,则保存为符号位
3.判断各位是否为数字,是数字就添加上,不是数字就终止
4.注意的是,这里需要进行是否溢出的判断
在这里插入图片描述

class Solution {
    public int strToInt(String str) {
        //检查是否超过范围:正数如果第二个数比第一个数还小,负数如果第二个数比第一个数还大
        int res = 0, bndry = Integer.MAX_VALUE / 10;
        int i = 0, sign = 1, length = str.length();
        if(length == 0) return 0;
        while(str.charAt(i) == ' ')
            if(++i == length) return 0;
        if(str.charAt(i) == '-') sign = -1;
        if(str.charAt(i) == '-' || str.charAt(i) == '+') i++;
        for(int j = i; j < length; j++) {
            if(str.charAt(j) < '0' || str.charAt(j) > '9') break;
            if(res > bndry || res == bndry && str.charAt(j) > '7')
                return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            res = res * 10 + (str.charAt(j) - '0');
        }
        return sign * res;

    }
}

(六)剑指 Offer 42. 连续子数组的最大和

在这里插入图片描述
基本思路:
基于贪心算法,当叠加的和小于0时,就从下一个数重新开始,同时更新最大和的值(最大值可能为其中某个值)。故左往右计算,如果和是大于自己的,就取和,否则就取自己.在这个过程中记录最大值

class Solution {
    public int maxSubArray(int[] nums) {
        //
        int tmp = nums[0];
        int res = tmp;
        for(int i = 1; i < nums.length;i++){
            tmp = Math.max(nums[i],tmp+nums[i]);
            res = Math.max(tmp,res);
        }
        return res;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值