再談大於指定正整數的最小“不重復數”問題


  對於“大於指定正整數的最小“不重復數”問題”,最初,在 算法:求比指定數大且最小的“不重復數”問題的高效實現 中,我給出了一個遞歸寫法,之后在同一篇博文中給出了一個非遞歸寫法。

  后來在 對Alexia(minmin)網友代碼的評論及對“求比指定數大且最小的‘不重復數’問題”代碼的改進 中對我的寫法進行了更詳細的說明,並進行了重要改進。使之適合范圍更大。

  在 評playerc網友的"求比指定數大且最小的“不重復數”問題"  中評論了playerc的寫法。

  playerc的思路沒問題,但代碼復雜且存在錯誤。現在我按照這個思路給出我的寫法。

     詳細的分析求解過程,就不在正文中描述了。代碼中有詳盡的注釋,在前幾篇博文中也進行了說明。這次寫法與前面的主要不同點的是,實現了只一次加0101……。 

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 #define SIZE(x) (sizeof (x) / sizeof (x)[0])
  5 #define BE_DIG(x) ( '0' <= x && x <= '9' ) 
  6 #define MAX 128  
  7 
  8 typedef  struct
  9          { 
 10             unsigned char digs[ MAX + 1 ] ;//從低位到高位存儲各位數字 
 11                                            //並為可能發生的進位預留一個位置  
 12             unsigned char * p_high ;       //記錄最高位數字位置 
 13          }  
 14 Number ; 
 15   
 16 void input( Number * ); 
 17 void reverse( unsigned char * , unsigned char * );
 18 void exchange( unsigned char * , unsigned char * ); 
 19 void squeeze( unsigned char * , unsigned char * * );
 20 void output( const unsigned char * , const unsigned char * ) ;  
 21 void seek( unsigned char * , unsigned char * * );
 22 void add_1( unsigned char * , unsigned char * * );
 23 unsigned char * search ( unsigned char * , unsigned char * );
 24 
 25 int main( void ) 
 26 {
 27    Number num ;
 28    
 29    input( & num );                    //輸入正整數
 30    seek( num.digs , & num.p_high );   //求不重復數
 31    output( num.p_high , num.digs );   //輸出
 32     
 33    return 0; 
 34 } 
 35 
 36 /*
 37   輸入數字到 p_n -> digs數組 ,p_high 記錄最高位位置 
 38 */
 39 void input( Number * p_n )
 40 {
 41    int c ;
 42    size_t max = SIZE(p_n -> digs) - 1u ;        //預留進位位置,最多輸入數字位數 
 43    
 44    p_n -> p_high = p_n -> digs ;
 45    
 46    while ( c = getchar() , BE_DIG( c ) && max -- > 0u ) 
 47       * p_n -> p_high ++ = c - '0' ; 
 48    
 49    if( p_n -> p_high == p_n -> digs )               //沒有輸入數字
 50       exit( 1 );
 51    
 52    -- p_n -> p_high ;
 53    reverse( p_n -> digs ,   p_n -> p_high );       //顛倒次序
 54    squeeze( p_n -> digs , & p_n -> p_high );       //去掉開頭的0 
 55 }
 56 
 57 /*
 58   將從p_b指向的數字到p_e指向的數字逆序排列。123 -> 321 
 59 */
 60 void reverse( unsigned char * p_b , unsigned char * p_e )
 61 { 
 62    while ( p_e > p_b )
 63       exchange( p_b ++ , p_e -- );
 64 } 
 65 
 66 void exchange( unsigned char * p1 , unsigned char * p2 ) 
 67 { 
 68    unsigned char c = * p1 ; 
 69    * p1 = * p2 ; 
 70    * p2 = c ; 
 71 } 
 72 
 73 /*
 74   去掉多余的高位0。00123 -> 123 
 75 */
 76 void squeeze( unsigned char * p_b , unsigned char * * pp_e )
 77 { 
 78    while ( * pp_e > p_b && * * pp_e == 0 )
 79       -- * pp_e ;
 80 } 
 81 
 82 /*
 83    * p_l指向最低位,* pp_h指向最高位
 84    
 85    (A)首先最左位加1           (left) 2 2 1 1 => 3 2 1 1  
 86    由高到低查找重復位         3 2 (1) 1
 87    新的區間為                 (left)(1) 1
 88    轉到(A)                    3 2 (2) 1
 89    (2) 1間沒有重復數字,循環結束
 90    
 91    從 left-1 到 p_l 寫010101……    
 92 */ 
 93 void seek( unsigned char * p_l , unsigned char * * pp_h )
 94 {
 95    unsigned char * break_p = p_l ;
 96    unsigned char * left ;      
 97    
 98    do
 99    {
100       left = break_p ;          
101       add_1( left , pp_h );                         //[left , *pp_h]區間內最左位加1
102    }
103    while ( ( break_p = search ( left , * pp_h ) ) != NULL );//NULL:不能找到重復數字 
104    
105    //從 left-1 到 p_l 寫010101…… 
106    while ( left > p_l )
107    {
108       left[-1] = ! * left ;
109       left -- ; 
110    }
111 }
112 
113 /*
114   加1。處理了可能的進位,以及pp_h所指向的記錄最高位位置指針可能的改變  
115 */ 
116 void add_1( unsigned char * p_l , unsigned char * * pp_h )
117 {
118    ++ * p_l ;                                     //最低位加1
119    while ( p_l < * pp_h )                         //進位處理
120    {
121       * ( p_l + 1 ) += * p_l / 10 ;
122       * p_l ++ %= 10 ; 
123    }
124    if ( * * pp_h > 9 )                            //最高位有進位 
125    {
126       * ( * pp_h + 1 ) = * * pp_h / 10 ;
127       * (* pp_h) ++  %= 10 ;
128    }
129 }
130 
131 /*
132   搜索p_h到p_l所指向數字中第一個重復數字的位置,如無則返回NULL 
133 */ 
134 unsigned char * search ( unsigned char * p_l , unsigned char * p_h )
135 {
136    if ( p_h == p_l )      //一位數情形 
137       return NULL ;
138 
139    while ( p_l + 1 < p_h )                        
140    {
141       if ( * p_h ==  p_h[-1] )
142          return p_h - 1 ;
143       p_h -- ;
144    }
145    
146    if ( * p_l ==  p_l[1] )
147       return p_l ;
148 
149    return NULL ;      
150 }
151 
152 /*
153   輸出p_h指向的數字到p_l指向的數字 
154 */
155 void output( const unsigned char * p_h , const unsigned char * p_l )
156 { 
157    while ( p_h > p_l )
158       printf( "%u" , * p_h -- );
159 
160    printf( "%u" , * p_l );
161    putchar('\n');
162 } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM