剑指-4和7幸运数字 (幸运数字 4和7)

题目:
我们把只包含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+11个节点,其中根节点的深度是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+11log2Q<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+11,那么用
Q − ( 2 2 + 1 − 1 ) − 1 Q - (2^{\begin{aligned}2+1\end{aligned}}-1) - 1 Q(22+11)1就可以得到Q在第四层数组中的索引了,而Q = M+1,则可以知道M在第四层的索引为 M − ( 2 2 + 1 − 1 ) M - (2^{\begin{aligned}2+1\end{aligned}}-1) M(22+11)

到了此时,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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值