回文数的一点点讨论【转】

时间:2021-10-23 05:12:57

问题:

    将所有回文数从小到大排列,求第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;
    }
}