问题:给定任意一个正整数,求比这个数大且最小的“不重复数”,“不重复数”的含义是相邻两位不相同,例如1101是重复数,而1201是不重复数 ——引自 百度2014校招笔试题目题解
http://www.cnblogs.com/pmer/p/3351466.html#!comments
问题的提法:
为代码简便,将问题等价地改为,求大于等于指定正整数的不重复数。由find()函数实现。
调用:
find( i + 1u )
原型:
unsigned find( unsigned );
算法:
以19922884u为例。
首先确定高位是否是重复数。即依次判断
1u
19u
199u
1992u
19922u
199228u
1992288u
是否是重复数。
如高位不是重复数,则当前数不变,并判断当前数是否是重复数。
例如对19u,由于1u不是重复数(一位正整数不是重复数,是显而易见的事。(if ( n < 10u ) return n;),所以判断19u是否是重复数(通过简单地判断19u的个位和十位是否相同。n % 10u == n /10u %10u)。
当当前数为199u时,高位(19u)不是重复数,当前数本身(119u)是重复数。
此时,将当前数加1,问题变为求大于等于200u的不重复数。
由于200u的高位不是重复数,而200u本身是重复数,所以经过了
n:2
n:20
之后,问题变成了求大于等于201u的不重复数。
201u不是重复数,所以回到求大于等于1992u的不重复数时,由于对于1992u来说,由于高位是重复数(返回值大于1992u/10u。 if ( n/10u <(t = find( n/10u)) )n = t * 10;),所以问题变成了求2010u的不重复数(n = t * 10;)。
2010u是不重复数,求大于等于19922u的不重复数变成了求大于等于20100u的不重复数。
但由于20100u的高位不是重复数,20100u本身是重复数(个位和十位相同),所以问题又变成了求大于等于20101u的不重复数的问题( if ( n % 10u == n /10u %10u ) return find( n + 1u );)。
重复以上过程,可得结果为20101010。
代码:
1 #include <stdio.h> 2 3 unsigned find( unsigned ); 4 5 int main( void ) 6 { 7 unsigned i ; 8 9 //测试 10 for ( i = 19922884u ; i < 19922884u + 1u ; i++ ) 11 { 12 printf ( "%u %u\n" , i , find( i + 1u ) ); 13 } 14 15 return 0; 16 } 17 18 unsigned find( unsigned n ) 19 { 20 unsigned t; 21 22 printf( "n:%u\n" , n ) ; //演示一下调用路径 23 24 if ( n < 10u ) 25 return n; 26 27 if ( n / 10u < ( t = find( n / 10u ) ) ) 28 n = t * 10u ; 29 30 if ( n % 10u == n /10u % 10u ) 31 return find( n + 1u ); 32 33 return n ; 34 }
提前回复:
飞鸟_Asuka 网友大概又会问:“有没有不用递归的算法呢?”我提前回复,有。不过目前写得还很难看,实在拿不出手。等我改好了再拿出来献丑。
非递归写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
#include <stdio.h>
typedef
struct
{
unsigned
char
t[ 20 ] ;
//这个空间应该够了
int
top ;
//记录第一位的下标
}
Map ;
unsigned find( unsigned );
void
parse( Map * , unsigned ) ;
int
search (
const
Map * );
void
add_1( Map * ,
const
int
);
void
clear( Map * ,
const
int
,
const
int
);
unsigned combi(
const
Map * );
int
main(
void
)
{
unsigned iu ;
//测试
for
( iu = 19922884u ; iu < 19922884u + 1u ; iu++ )
{
printf
(
"%u %u\n"
, iu , find( iu + 1u ) );
}
return
0;
}
unsigned combi(
const
Map * p_m )
{
unsigned nu = 0u ;
int
i;
for
( i = p_m->top ; i >= 0 ; i -- )
{
nu *= 10u ;
nu += p_m->t[i] ;
}
return
nu;
}
void
clear( Map * p_m ,
const
int
from ,
const
int
to )
{
int
i ;
for
( i = from - 1 ; i > to - 1; i -- )
p_m->t[i] = 0u ;
}
void
add_1( Map * p_m ,
const
int
from )
{
int
i ;
p_m->t[from] ++;
//最低位加1
for
( i = from ; i < p_m->top ; i ++ )
//进位处理
{
p_m->t[i + 1] += p_m->t[i] / 10u ;
p_m->t[i] %= 10u ;
}
if
( p_m->t[p_m->top] > 9u )
//最高位有进位
{
p_m->t[p_m->top + 1] = p_m->t[p_m->top] / 10u ;
p_m->t[p_m->top ++ ] %= 10u ;
}
}
int
search (
const
Map * p_m )
{
int
i ;
for
( i = p_m->top ; i > 0 ; i-- )
{
if
( p_m->t[i] == p_m->t[i-1] )
break
;
}
return
i - 1 ;
}
void
parse( Map * p_m , unsigned n )
{
p_m->top = -1 ;
while
( n > 0u )
{
p_m->t[ ++ p_m->top ] = n % 10u ;
n /= 10u ;
}
}
unsigned find( unsigned n )
{
Map map ;
int
end = 0 , b_point ;
parse( &map , n ) ;
//将n分解为数字
while
( ( b_point = search ( &map ) ) > -1 )
//为-1时说明不是重复数
{
add_1( &map , b_point );
//重复数部分加1
clear( &map , b_point , end );
//后面改为0
end = b_point ;
//确定下次循环的处理范围
}
return
combi( &map );
}
|
算法描述:
以19922884为例,
数据结构:用一数组及所使用到的最大下标表示。
map:
4 8 8 2 2 9 9 1
7
find():
从19922884的高位开始查找不重复数,记录位置
b_point = search ( &map )
b_point :5
b_point以后部分加1:add_1( &map , b_point );
map:
4 8 8 2 2 0 0 2
7
从 end到b_point-1之间的元素清零:clear( &map , b_point , end );
map:
0 0 0 0 0 0 0 2
7
记录b_point作为下次循环的end。
end:5
第二次循环,
b_point = search ( &map )
b_point :5
b_point以后部分加1:add_1( &map , b_point );
map:
0 0 0 0 0 1 0 2
7
从 end到b_point-1之间的元素清零:由于此时end为5,所以没有任何元素清零
记录b_point作为下次循环的end。
end:5
第三次循环,
b_point = search ( &map )
b_point :3
b_point以后部分加1:add_1( &map , b_point );
map:
0 0 0 1 0 1 0 2
7
从 end到b_point-1之间的元素清零:由于此时end为5,b_point 为3,所以没有任何元素清零
记录b_point作为下次循环的end。
end:3
第四次循环,
b_point = search ( &map )
b_point :1
b_point以后部分加1:add_1( &map , b_point );
map:
0 1 0 1 0 1 0 2
7
从 end到b_point-1之间的元素清零:由于此时end为3,b_point 为1,所以没有任何元素清零
记录b_point作为下次循环的end。
end:1
第五次循环,
b_point = search ( &map )
b_point :-1
循环结束。
重新合成整数:return combi( &map );
问题得解。
总结
非递归写法很难写,就这个问题而言,效率方面也不比递归方法好。