5位数的数字黑洞是多少_打基础之LeetCode算法题第66篇:用数学方法解旋转数字问题...

一直很纠结算法的文章应该怎么写。最后觉得还是从最简单的level开始写吧,一开始就弄些重量级的,什么人工智能,机器学习的算法,还要有大量的数学以及优化的知识,小白们估计会很郁闷,当然我也不一定能做出来对吧。

我计划每题给出两种语言的解决方案,一种静态语言,一种动态语言。

我选择C语言,Python和Java作为实现语言,由于篇幅有限,其他语言的实现有兴趣的朋友请自己尝试。

LeetCode 788. 旋转数字(Rotated Digits)

问题描述:

我们称一个数 X 为好数, 如果它的每位数字逐个地被旋转 180 度后,我们仍可以得到一个有效的,且和 X 不同的数。要求每位数字都要被旋转。

如果一个数的每位数字被旋转以后仍然还是一个数字, 则这个数是有效的。0, 1, 和 8 被旋转后仍然是它们自己;2 和 5 可以互相旋转成对方;6 和 9 同理,除了这些以外其他的数字旋转以后都不再是有效的数字。

现在我们有一个正整数 N, 计算从 1 到 N 中有多少个数 X 是好数?

注:

· N 的取值范围是 [1, 10000]。

示例:

af5e6ee8ea15e53152ee7339bb3b3974.png

C语言实现:

这个问题可能有人没看懂。这其实说的就是火柴棒数字问题。在火柴棒数字中,2和5是互为镜像的,6和9是互为镜像的,0,1,8 旋转180度还是自身,3,4,7旋转后不是数字。

3638b3a38a7fdce9505cf5c9f9e076e8.png

要解决这个问题,我们可以用暴力方法,遍历0~N的所有整数,检查它们数字中是否包含3,4,7,如果包含就忽略,然后再检查数字中是否保护2,5,6,9中某些数字,如果有就计数1.

这种方法,算法复杂度是O(N).

但是我觉得有更好的办法。

不知道大家有没有注意到这是一个排列组合问题。

87afc307164a14fa2354abc8a9618831.png

“好数”要满足条件:要有一个数是2,5,6,9,其他的数可以是0,1,8,2,5,6,9。

我们会发现对最大的Y位数,我们可以有公式:7*Count(Y-1)+4*3^(Y-1)。例如对于最大一位数9,有4个好数;对于两位数99,十位可以取0,1,2,5,6,8,9共6种,这时候个位可以取,2,5,6,9,另外还要加上一种情况:当十位取2,5,6,9的时候,后面个位可以取0,1,8,合在一起99的总的好数数量Count就等于40种。

我们还可以进一步扩展,对于任意一个数字后面跟多个9的情况。例如499。百位可以取0,1,2 三种数字,所有有3*Count(2) = 120 ,还要加上百位取[2,5,6,9]的情况,这里我们发现只能取到2,所以是一种,就有1*3*3 = 9,合计是129种好数。

我们再扩展,如上图,对于2948这个数,我们想计算前1999个数中的好数,计算玩以后千位数2就变成固定的了,然后计算899个数中的好数,这个时候百位的9也就被固定了。为什么是899?其实加上前面计算1999的步骤,实际上我们计算的就是2899个数中好数的笼统计算(为什么是笼统?因为我们还有一种情况没有考虑,后面接着说。)如此这样,直到计算完39之后(统计的是2939)我们发现遇到了[3,4,7]中的4,结束,因为有4的数都不是好数,所以没必要继续下去。这时候第一步结束了,下面开始第二步。

如前面所述,我们第一步只是考虑到一种情况,即,当前数在[0,1,2,5,6,8,9]中取一个数,后面的位数组成的数好数的情况。第二步我们要考虑的是,如果前面数有[2,5,6,9]的时候,注意,我们第一步的时候已经将它们固定了,也就是说,如果前面数是1和7的时候,那么只判断1和7,因为它们不是我们要的数,所以这一步跳过,如果是2,6或者2,1这样的的,那么需要这一步。

方法和第一步类似,只是这里计算的是各位取[0,1,8]后的组合数。

代码如下:

1ba185022a1c1e5e1623ae6bb4f45fcc.png
19f1bf1c336d305bb39c52dc6cd7ee44.png

首先定义了3个数组,用于快速查找定位。其中x表示的是取[0,1,8]的可能性。比如对于7,他可以取0,1两种。y是取[2,5,6,9]的可能性。z是为了定位数字是哪一种。

函数_func(),是用来计算f999这样的数出现的好数数量,其中f表示最大位的数,i表示有几位数,例如要计算499,那么_func(4, 3)。

代码14~20行,是将数N按位存储到数组中,方便后面操作。

代码23~27行,这个循环就是处理第一步的任务。

代码29~40行,处理第二部的任务。这里众多的判断其实多是考虑N中有0的情况。0出现在N的末尾和中间,处理起来是不同的。

可以看出,这个方案虽然复杂一些,但是效率很高,它之于N的位数有关。而N的位数是有限的,所以算法的时间复杂度是O(1)。

9e946badfe43f0437bebe572d87cbe3e.png

python语言的实现:

python语言的依然可以用上面的原理来实现,但是代码可能会比较长。我认为如果考虑用python来解决的问题,算法的效率往往不是最重要的。简洁,易实现,容易维护可能更加重要,所以我这里用的是传统的方法。

代码如下:

1afb00a9336009ca717e64a49d9ad5eb.png
e91287ef40e0d9fe50a1dcd0b256a59b.png

Java语言的实现:

Java的实现和C语言的实现相同。

代码如下:

596e85c68ccb914005bc3b14c642e3f1.png
37d37d080c86d8793535bc81d0ba00b3.png
408954553d977c12ede3366a1f0fb36d.png

更新的有些晚,因为我想给大家一个更好的解决方案,而这个实现描述起来还有些复杂,没办法表达能力有限,如果有不懂得地方,尽管问吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值