对于“大于指定正整数的最小“不重复数”问题”,最初,在 算法:求比指定数大且最小的“不重复数”问题的高效实现 中,我给出了一个递归写法,之后在同一篇博文中给出了一个非递归写法。
后来在 对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 }
来源:https://www.cnblogs.com/pmer/p/3367318.html