Leetcode小白试炼(20201207 连接连续二进制数字)

一、题目

1. 题目描述

给你一个整数 n ,请你将 1 到 n 的二进制表示连接起来,并返回连接结果对应的 十进制 数字对 109 + 7 取余的结果。
提示:
1 ≤ n ≤ 1 0 5 1 \leq n \leq10^5 1n105

2. 示例

示例1:
输入:n = 1
输出:1
解释:二进制的 “1” 对应着十进制的 1 。
示例2:
输入:n = 3
输出:27
解释:二进制下,1,2 和 3 分别对应 “1” ,“10” 和 “11” 。
将它们依次连接,我们得到 “11011” ,对应着十进制的 27 。

示例3:
输入:n = 12
输出:505379714
解释:连接结果为 “1101110010111011110001001101010111100” 。
对应的十进制数字为 118505380540 。
对 109 + 7 取余后,结果为 505379714 。

3. 题目解析

按照题意,很容易想到先将范围内的十进制数转换为二进制,拼接后,再将这一串二进制转换为十进制。但是要拼接的太长,使用此方法会超时。

二、个人解法

1. 解法分析

  • 使用StringBuffer来拼接存放所有数字的二进制。
  • 使用long类型的result变量来存储十进制结果。
  • 从后往前取二进制字符串中的每一位,转换为十进制,累加至result,只要超过限制值就执行取余操作。
  • 将result转换为int类型,即为所求。

2. 代码

    /**
     * 第一次尝试用Stringbuffer拼接从1到n的二进制,然后逐位求解,超出时间限制
     *
     * @param n
     * @return
     */
    public int concatenatedBinary1(int n) {
        StringBuffer str1 = new StringBuffer();

//        用StringBuffer的append方法将范围内的整数转换的二进制拼接
        for (int i = 1; i <= n; i++) {
//            Integer.toBinaryString方法可以将十进制转换为二进制字符串
            str1.append(Integer.toBinaryString(i));

        }
        double result = 0;
        int j = 0;
//        取二进制的每一位,转换成10进制
        for (int i = str1.length() - 1; i >= 0; i--) {
            double temp = str1.charAt(i) - '0';
            result += temp * Math.pow(2, j);
            j++;
//            超过规定值就取余
            if (result >= Math.pow(10, 9) + 7) {
                result = result % (Math.pow(10, 9) + 7);
            }
        }
        return (int) result;
    }

3. 失败反思

  • 如果n比较大,那么拼接得到的str1会非常长,用int类型的变量i来遍历可能不够用。
  • 遍历拼接,再遍历求值,需要进行两次遍历操作,浪费时间。

4. 改进

(1)改进点

a.从后往前遍历n~1,每遍历到一个数就先转二进制,紧接着直接转10进制进行累加。
b.用long类型的变量j来记录二进制的位数。

(2)代码

 /**
     * 第二次逐个数变二进制再逐位十进制求和,结果也超过时间限制。
     *
     * @param n
     * @return
     */
    public int concatenatedBinary2(int n) {
        long j = 0;
        long result = 0;
        while (n > 0) {
            String str = Integer.toBinaryString(n);
            for (int i = str.length() - 1; i >= 0; i--) {
                long temp = str.charAt(i) - '0';
                result += pow(temp, j);
                j++;
                if (result >= Math.pow(10, 9) + 7) {
                    result = result % ((long) Math.pow(10, 9) + 7);
                }
            }
            n--;
        }
        return (int) result;
    }

5. 再次失败反思

虽然将两次遍历合一,但是仍然需要先转二进制,再逐位转十进制,导致超时。因此思路需要转变。

6. 算法分析

虽然空间复杂度不高,但是占用的内存很大。
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

7. 提交截图

(1)第一次失败截图

在这里插入图片描述

(2)第二次失败截图

在这里插入图片描述

三、官网高星解法

1.位运算法

(1)解法分析

分析示例:
n=1:result=1
n=2:result=101=100+1
n=3:result=10111=10100+11
n=4:result=10111100=10111000+100
可知,n每加1,结果相当于n-1的二进制表示左移k位后再加n,而k就是n的二进制表示所占位数。
在Java中,‘<<’操作符表示左移,详细介绍可参考:
Java基础语法学习(二、注释、标识符和运算符)
也可以直接使用result*math.pow(2,左移位数)来进行左移操作。

(2)代码

public int concatenatedBinary(int n) {
        long result = 0;
        int i = 1;
        while (i <= n) {
            String str = Integer.toBinaryString(i);
            // sum = (sum * (long)Math.pow(2, temp.length()) + i) % mod;
            result = (result << str.length()) + i;
            if (result >= Math.pow(10, 9) + 7) {
                result = result % (long) (Math.pow(10, 9) + 7);
            }
            i++;
        }

        return (int) result;
    }

(3)算法分析

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

(4)提交截图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值