题目:
我们把只包含4或7的数称为幸运数字.
例如:4,7,44,47,74,77等等是幸运数字,而41,42不是幸运数字.
求从小到大的顺序第M个幸运数字.而M的大小是,0 <
M<
10^18次幂
思路:
由于M数字可以非常大,基本上断绝了使用遍历的方式来求结果的方式。
我们可以首先看一下数字的规律,[4,7,44,47,74,77,444,447,474,477,744,747,774,777]
,简单分析后,可以将其画成二叉树的形式:
0
4 7
44 47 74 77
444 447 474 477 744 747 774 777
第一行是为了方便计算自动补充的,当求第M个幸运数字时,可以看做求此二叉树的第Q个值,Q=M+1。
此外,可以看出二叉树不同的层次,数字位数是不同的,从第二层开始,每层的数字位数递增1,那么我们可以得知,当Q处于第N层时,那么该层的数字位数是N-1。
那么如何计算Q处于第几层呢?根据二叉树的性质得知,深度为k的完全二叉树,至少有 2 k 2^{\begin{aligned}k\end{aligned}} 2k个节点,至多有 2 k + 1 − 1 2^{\begin{aligned}k+1\end{aligned}}-1 2k+1−1个节点,其中根节点的深度是0。
2 k < Q < 2 k + 1 − 1 l o g 2 Q < k < l o g 2 ( Q + 1 ) − 1 2^{\begin{aligned}k\end{aligned}} < Q < 2^{\begin{aligned}k+1\end{aligned}}-1 \\ log_2Q < k <log_2(Q+1)-1 2k<Q<2k+1−1log2Q<k<log2(Q+1)−1
假设我们要求第8个节点(M=7),也就是第四层的第一个节点。
将其带入公式中: l o g 2 8 = 3 log_28 = 3 log28=3,可以得到第8个节点在深度为3的二叉树中,也就是在二叉树的第四层中。同样我们也可以得知第四层中的数字都是三位的。
到了此时,要求的Q已经在一个数组中了:[444,447,474,477,774,747,774,777]
,那么如何进一步确定Q在第几个位置呢?
因为我们已经知道Q处于第四层了,而深度为2的节点总数又是
2
2
+
1
−
1
2^{\begin{aligned}2+1\end{aligned}}-1
22+1−1,那么用
Q
−
(
2
2
+
1
−
1
)
−
1
Q - (2^{\begin{aligned}2+1\end{aligned}}-1) - 1
Q−(22+1−1)−1就可以得到Q在第四层数组中的索引了,而Q = M+1,则可以知道M在第四层的索引为
M
−
(
2
2
+
1
−
1
)
M - (2^{\begin{aligned}2+1\end{aligned}}-1)
M−(22+1−1)。
到了此时,M所处的层数的索引也知道了,所处层数的数字位数也知道了,那么该如何求出在索引处的数字呢?
我们可以将4看做0,将7看做1,就会发现该层中[000,001,010,011,100,101,110,111]
,而将他们分别转换成十进制就恰好是索引的数字,那么,我们就可以将索引转换为3位的二进制数,碰到一个碰到一个0就追加一个4,碰到一个1就追加一个7
public static void main(String[] args) {
System.out.println( printLuckNumber(7));
}
private static String printLuckNumber(int m) {
// 求出m所在的深度
int k = (int) Math.ceil(Math.log((double) (m + 1)) / Math.log((double) 2));
// 求出m-1深度所有节点数
int preCount = (int) (Math.pow(2, k - 1 + 1 ) - 1);
// 计算索引
int index = (m - preCount);
// 追加0/1
StringBuilder sb = new StringBuilder();
for (int i = 0; i < k; i++) {
sb.append((index & 0x1) == 1 ? '7' : '4');
index >>>= 1;
}
return sb.reverse().toString();
}
结果
444