文章目录
一、题目
1. 题目描述
给你一个整数 n ,请你将 1 到 n 的二进制表示连接起来,并返回连接结果对应的 十进制 数字对 109 + 7 取余的结果。
提示:
1
≤
n
≤
1
0
5
1 \leq n \leq10^5
1≤n≤105
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)