《剑指offer》面试题12 打印1到最大的n位数 Java版
书中方法:这道题的一个陷阱在于不能用int或者long去存储你要打印的数,然后用打印函数打印,因为这个数可能会很大。如果加1后超出了最大的n位数,就不打印了。用最高位是否进位判断是否结束,打印的时候注意不要打印出前面可能出现的0.
public void print(int n){
if(n<=0){
return;
}
//必须要用字符数组防止大数
char[] c = new char[n];
for(int i=0; i<n; i++){
c[i] = '0';
}
while(!increment(c)){
digitsPrint(c);
}
}
private boolean increment(char[] c){
boolean overflow = false;
//因为是加1,当作从小数进位了1.
int carry = 1;
//从个位开始计算
for(int i=c.length-1; i>=0; i--){
int sum = c[i] - '0' + carry;
//如果进位了
if(sum == 10){
if(i == 0){
overflow = true;
break;
}
carry = 1;
c[i] = '0';
}else{//如果没进位
c[i] = (char)('0' + sum);
break;
}
}
return overflow;
}
private void digitsPrint(char[] c){
int index = 0;
while(c[index] == '0'){
index++;
}
for(int i=index; i<c.length; i++){
System.out.print(c[i]);
}
System.out.println();
}
我的方法:看到“所有”和“打印”这样的关键字,很容易想到用回溯的方法去做,因为实质是求出所有组合,上面的方法也是为了能遍历到所有组合。和求字符串的全排列以及组合不同(题目28),这里字符可以重复使用(又联想到了打印n位以内数字不重复整数...这里不展开,后面再说)。回想一下我们打印字符串的全排列时,因为每个字符只能使用一次,所以我们得想办法保证每个字符只被选取一次(利用额外的数组或者交换)。现在我们只需要简单地在每个位子上选取可能出现的值,然后递归下去就行了。外层是一个控制长度的循环,内层为每一位选取数字,每一位上都有‘0’-‘9’字符可以选择(第一位除外)。
public void print2(int n){
if(n <= 0)return;
List<String> result = new ArrayList<String>();
String line = "";
//用n控制位数
for(int i=1; i<=n; i++){
find(result, line, 0, i);
}
for(String s : result){
System.out.println(s);
}
}
private void find(List<String> result, String line, int level, int border){
//每一位添加完毕后保存
if(level >= border){
result.add(new String(line));
return;
}
//用户保护原始的line,即保护现场(可百度这个关键词)
String temp = new String(line);
for(int i=0; i<=9; i++){
//第一位不能为0
if(level == 0 && i == 0){
continue;
}else{
//当前位添加
line += i;
//继续添加下一位
find(result, line, level+1, border);
//让line恢复,进入下次for循环
line = temp;
}
}
}