TA的每日心情 | 开心 2021-3-12 23:18 |
---|
签到天数: 2 天 [LV.1]初来乍到
|
问题: 将所有回文数从小到大排列,求第N个回文数。 一个正数如果顺着和反过来都是一样的(如13431,反过来也是13431),就称为回文数。约束: 回文数不能以0开头。 最小回文数是1。 思路: 许多朋友(包括我自己)一开始就思考使用循环:从1开始,判断该数是否是回文数,然后用一 个计数器记下回文数,一直到计数器得到N,返回第N个回文数。比较常用的是以下这种方法来判断是 否回文数:
static
boolean
isPN(
int
num) {
int
o
=
num;
int
tmp
=
0
;
//
使用循环把数字顺序反转
while
(num
!=
0
) {
tmp
*=
10
;
tmp
+=
num
%
10
;
num
/=
10
;
}
//
如果原始数与反转后的数相等则返回true
if
(tmp
==
o)
return
true
;
return
false
;
}
这种思路的确可得到正确结果,但随着用来测试的N的增大,效率的问题就浮现了。因为是一重 循环,效率是O(n)。所以当N非常大时,所花的时间还是十分大的。 另一种思路: 回文数的个数其实是有规律的。如: 1位回文数: 9个 2位回文数: 9个 3位回文数: 90个 4位回文数: 90个 5位回文数: 900个 6位回文数: 900个 … 我们看到9、90、900,是不是很有规律,那是什么原因?很简单,我们把回文数拆开两半 [123321]来看。两半的变化一样的,那我们只算其中一半就行了。首位不能是0,所以左半最小为 100,最大为999,共有999-100+1=900个,如此类推。 所以我们可以基于以下原则: 1、 回文数的数位每增长2,回文数的个数为原来的10倍。如从个位回文数到百位回文数,个数 从9个变为90个。 2、 个位回文数的个数是9,1、2、3、…、9。 总之理解原理后一切好办,步骤就偷懒不写了,看代码吧! 核心代码:
static
long
find(
int
index) {
int
count
=
0
;
int
number
=
9
;
//
记录数位上的回文数,如个位回文数为9
int
w
=
0
;
//
记录数位
long
half;
//
保存回文数的左半边的结果
long
h
=
1
;
//
回文数的左半边的起始基数
long
res;
//
结果
while
(
true
) {
if
(w
>
0
&&
w
%
2
==
0
) {
//
每进两个数位,回文数乘以10
number
*=
10
;
}
w
++
;
//
数位加一
if
(count
+
number
>
index)
//
回文数大于查找的回数,跳出
break
;
count
+=
number;
//
回文数加上当前数位上的回文数
}
index
-=
count;
//
在当前数位上的位置。如w=5,index=50,则万位上的第50个回文数是我们所求
for
(
int
i
=
0
; i
<
(w
-
1
)
/
2
; i
++
) {
//
求回文数的左半边的基数,如回文数在万位上,则为100
h
*=
10
;
}
half
=
h
+
index;
//
回文数的左半边,如100 + 50 = 150
res
=
half;
if
(w
%
2
!=
0
)
//
如果为奇数,则中间那个数不必算入右半边了!
half
/=
10
;
while
(half
!=
0
) {
//
拼接回文数
res
=
res
*
10
+
half
%
10
;
half
/=
10
;
}
return
res;
}
全部代码:
- public class PalindromicNumber {
- public static void main(String[] args) {
- int input = Integer.parseInt(args[0]);
- long res = find(input);
- System.out.println("res:" + res);
-
- }
-
- static long find(int index) {
- int count = 0;
- int number = 9; //记录数位上的回文数,如个位回文数为9
- int w = 0; //记录数位
-
- long half; //保存回文数的左半边的结果
- long h = 1; //回文数的左半边的起始基数
- long res; //结果
-
- while(true) {
- if(w > 0 && w%2 == 0) { //每进两个数位,回文数乘以10
- number *= 10;
- }
- w++; //数位加一
- if(count + number > index) //回文数大于查找的回数,跳出
- break;
- count += number; //回文数加上当前数位上的回文数
- }
-
- index -= count; //在当前数位上的位置。如w=5,index=50,则万位上的第50个回文数是我们所求
- for(int i = 0; i < (w-1) / 2; i++) { //求回文数的左半边的基数,如回文数在万位上,则为100
- h *= 10;
- }
-
- half = h + index; //回文数的左半边,如100 + 50 = 150
- res = half;
-
- if(w%2 != 0) //如果为奇数,则中间那个数不必算入右半边了!
- half /=10;
-
- while(half != 0) { //拼接回文数
- res = res *10 + half % 10;
- half /= 10;
- }
-
- return res;
- }
- }
复制代码
源码下载:http://www.java3z.com/cwbwebhome/dir1/dir5/PalindromicNumber.zi |
|