【算法题】统计各位数字之和为偶数的整数个数、替换空格、旋转数组的最小数字

一、统计各位数字之和为偶数的整数个数

1.1、题目

给你一个正整数 num ,请你统计并返回 小于或等于 num 且各位数字之和为 偶数 的正整数的数目。

正整数的 各位数字之和 是其所有位上的对应数字相加的结果。

示例 1:

输入:num = 4
输出:2
解释:
只有 2 和 4 满足小于等于 4 且各位数字之和为偶数。

示例 2:

输入:num = 30
输出:14
解释:
只有 14 个整数满足小于等于 30 且各位数字之和为偶数,分别是:
2、4、6、8、11、13、15、17、19、20、22、24、26 和 28 。

来源:力扣(LeetCode)

1.2、理解题目

也就是给一个int型的整数num,统计大于0小于num范围内的满足要求的数字个数,这个数字的要求是其各位数字之和为偶数(比如10的个位数之和为1+0=1,不满足要求;13的个位数之和为1+3=4,满足要求)。

1.3、解题思路(暴力枚举)

首先想到的解题思路:

将num逐步减一(满足小于或等于 num要求),用一个循环对当前值的计算值取模和求余(,每次的取模得到的数(就是各位数)相加,直到求余得到的数为0时退出循环,然后判断取模得到的数(就是各位数)相加是否为偶数。

代码:

class Solution {
public:
    int countEven(int num) {
        int count=0;//统计数量
        while(num>1)// 判断
        {
            int pre=num;// 保存要计算的数值
            int cur=0; // 记录
            while(pre)
            {
                cur+=pre%10;//取模并相加
                pre/=10;//取余
            }
            if(cur%2==0)//偶数
                count++;//统计
            num--;//递减
        }
        return count;
    }
};

测试结果:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.8 MB, 在所有 C++ 提交中击败了40.59%的用户
通过测试用例:71 / 71

时间复杂度: O(n log n) 。

空间复杂度: O(1)。

1.4、解题思路2(数学公式)

首先,位于区间[0, 10)的奇数和偶数的个数都是5个。将 num表示为 10y+x的形式,其中 x大于等于0小于10且y大于等于0 ,那么位于区间 [0,num]的整数可以分为两个部分:区间[10y+0,10y+x]和区间[0,10y+0]

总结就是一个公式:将 num表示为10y+x,y=num/10,x=num%10;然后计算y的各个数之和是否为偶数,如果是偶数则结果为 y5+x/2+1 ,如果是奇数则结果为 y*5+(x+1)/2 。

上述区间中多计入了整数0,因此结果应该是位于上述区间且各位数字之和为偶数的个数减1。

class Solution {
public:
    int countEven(int num) {
        // num=10*y+x
        int y=num/10;
        int x=num%10;
        int res=y*5;
        int ysum=0;
        while(y)
        {
            ysum+=y%10;
            y=y/10;
        }
        if(ysum%2==0)
            res+=x/2+1;
        else
            res+=(x+1)/2;
        return res-1;
    }
};

测试结果:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00%的用户
内存消耗:5.8 MB, 在所有 C++ 提交中击败了30.07%的用户

时间复杂度: O(log n)。

空间复杂度:O(1)。

1.5、小结

暴力枚举各位数之和的核心:

while(...){
    sum+=num%10;
    num/=10;
}
  1. 数学公式: num表示为10y+x , y=num/10,x=num%10 。result=y5+(y的各位数之和是偶数吗)?(x/2+1) : (x(+1)/2) -1 。

二、替换空格

2.1、题目

实现一个函数,把字符串s中的每个空格替换成"%20"。

示例 1:

输入:s = “We are happy.”
输出:“We%20are%20happy.”
来源:力扣(LeetCode)

2.2、解题:遍历原地修改

在 C++ 语言中, string 被设计成「可变」的类型,因此可以在不新建字符串的情况下实现原地修改。

class Solution {
public:
    string replaceSpace(string s) {
        int len=s.length();
        string ret="";
        int i=0;
        while(i<len)
        {
            if(s[i]==' ')
                ret+="%20";
            else
                ret+=s[i];
            i++;
        }
        return ret;
    }
};

时间复杂度 O(N) 。

空间复杂度 O(1) 。

三、旋转数组的最小数字

3.1、题目

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。

注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。

示例 1:

输入:numbers = [3,4,5,1,2]
输出:1

示例 2:

输入:numbers = [2,2,2,0,1]
输出:0

来源:力扣(LeetCode)

3.2、思路

排序数组的查找问题首先考虑使用二分法解决,其可将遍历法的线性级别时间复杂度降低至对数级别。

在这里插入图片描述

二分法算法流程:

  1. 初始化:声明两个指针low、hight分别指向numbers的首元素和尾元素。
  2. 使用一个循环来二分,每次循环都计算中心位置inv=low+(hight-low)/2。
  3. 调整low和hight的位置:如果中心点inv位置的元素小于最高点hight位置的元素,则hight=inv;如果中心点inv位置的元素大于最高点hight位置的元素,则low=inv+1;否则hight=hight+1。
  4. 返回值: 当low==hight时跳出二分循环,并返回 旋转点的值 nums[low] 即可。

3.3、代码实现

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int hight=numbers.size()-1;
        int low=0;
        while(low<hight)
        {
            int inv=low+(hight-low)/2;
            if(numbers[inv]>numbers[hight])
            {
                low=inv+1;
            }
            else if(numbers[inv]<numbers[hight])
            {
                hight=inv;
            }
            else
                hight-=1;
        }
        return numbers[low];

    }
};

时间复杂度 O ( l o g 2 N ) O(log_2 N) O(log2N) : 在特例情况下(例如 [1, 1, 1, 1]),会退化到 O(N)。

空间复杂度 O(1) : 变量使用常数大小的额外空间。

当然,还有暴力枚举法,这种方法不建议。

class Solution {
public:
    int minArray(vector<int>& numbers) {
        int n=numbers.size();
        if(n==0)
            return 0;
            
        int res=numbers[0];
        for(int i=1;i<n;i++)
        {
            if(numbers[i]<numbers[i-1])
                return numbers[i];
        }
        return res;
    }
};

3.4、小结

二分法可以解决排序数组的查找问题,降低时间复杂度。

二分法步骤:

  1. 初始化:定义两个指针low、hight,分别指向开头和末尾。
  2. 循环二分:每次循环都计算中间点(hight-low)/2,根据题意比较中心点和边沿的关系进行low和hight的变化。
  3. 当low==hight时跳出二分循环,并返回low对应的结果即可。

总结

一定要做好总结,特别是当没有解出题来,没有思路的时候,一定要通过结束阶段的总结来反思犯了什么错误。解出来了也一定要总结题目的特点,题目中哪些要素是解出该题的关键。不做总结的话,花掉的时间所得到的收获通常只有 50% 左右。

在题目完成后,要特别注意总结此题最后是归纳到哪种类型中,它在这种类型中的独特之处是什么。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lion Long

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值