【日耕一题】6. 数字反转(NOIP2011 普及组 / 洛谷 P1307)

你好,我是林森lsjs

我的Github 地址: sqyCoder (Qiyang) · GitHub

以博文记录成长,用心打磨代码与思维

欢迎来到日耕一题

今天这道是 NOIP 普及组的经典入门题,也是洛谷 P1307。

看着就是 "把数字倒过来" 这么简单,但新手一写就容易踩坑:负号丢了、前导零消不掉、输入 0 直接输出空值。

这道题有两种主流写法:字符串反转法和纯数学法。

我们重点讲更练基本功的数学解法

一、题目完整解读

原题

NOIP 2011 普及组 数字反转

给定一个整数 N,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零。

输入格式

一个整数 N。

输出格式

一个整数,表示反转后的新数。

输入输出样例

样例 1:

输入:123

输出:321

样例 2:

输入:-380

输出:-83

样例 3:

输入:0

输出:0

拆解所有要求

  1. 数位反转:将整数的每一位数字倒序排列
  2. 符号保留:负数反转后负号仍在最前面,不能丢
  3. 无前导零:原数末尾的零,反转后不能出现在最高位(原数为 0 除外)
  4. 零特殊兼容:输入 0 时输出 0,不能输出空值或报错

Java 中负数直接取模,结果符号和被除数一致,会得到负的数位,直接累加就会出错

末尾多个零的情况(如 1000),反转后要全部消掉,只留 1

数据范围在 int 取值内,无需考虑溢出问题

二、核心知识点

1. 数位拆解:取模 + 除法的经典用法

这是所有数位操作题的核心基本功:

取最后一位:n % 10,可以得到整数最末尾的数字

去掉最后一位:n / 10,整数除法会自动舍弃小数位,相当于砍掉最后一位

循环执行这两步,直到 n 变成 0,就能把所有数位从后往前依次取出来。

2. 符号隔离:规避负数取模坑

如果直接对负数取模,比如 -380 % 10,在 Java 中结果是 -0(实际为 0)

-38 % 10 结果是 -8。如果直接把负数位拼进结果,符号就全乱了。

最稳妥的写法是:先单独记录符号,把原数转成正数再处理,最后再把符号乘回去

逻辑清晰,完全避开负数取模的坑。

3. 前导零的自动消除原理

很多新手会特意写判断去前导零,其实完全不用。我们用 res = res * 10 + 当前数位 的方式拼接结果,初始 res 为 0。遇到原数末尾的零时,res 始终是 0,相当于自动跳过了这些前置的零,等遇到第一个非零数字后才开始真正累加,天然实现去前导零。

三、分步代码编写 + 逐行讲解

1. 第一步:处理符号与初始化

java 复制代码
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int sign = 1;   // 符号标记,1为正,-1为负
int res = 0;    // 反转结果

// 如果是负数,记录符号,把n转成正数处理
if (n < 0) {
    sign = -1;
    n = -n;
}

先把符号和数值拆开,后面只处理纯数字,逻辑干净很多。

2. 第二步:逐位反转核心循环

java 复制代码
while (n > 0) {
    int digit = n % 10;  // 取出当前n的最后一位
    res = res * 10 + digit; // 把该位拼到结果的末尾
    n = n / 10;          // 砍掉n的最后一位
}

**为什么能自动去前导零?**比如 n=380:

  • 第一次循环:取出 0,res = 0*10 + 0 = 0
  • 第二次循环:取出 8,res = 0*10 + 8 = 8
  • 第三次循环:取出 3,res = 8*10 + 3 = 83

开头的零全程没有存在感,自动就被消掉了,完全不用额外写 if 判断。

同时,输入 0 的时候,循环条件n > 0不满足,res 保持 0,天然兼容零的情况,不用单独处理。

3. 第三步:还原符号并输出

java 复制代码
res *= sign;  // 把符号乘回去
System.out.println(res);
sc.close();

正数乘 1 不变,负数乘 - 1 还原负号,一步到位。

四、完整可运行代码

java 复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int sign = 1;
        int res = 0;

        // 处理负号,转正数计算
        if (n < 0) {
            sign = -1;
            n = -n;
        }

        // 逐位反转
        while (n > 0) {
            int digit = n % 10;
            res = res * 10 + digit;
            n = n / 10;
        }

        // 还原符号并输出
        System.out.println(res * sign);
        sc.close();
    }
}

五、执行流程全复盘(重点)

盘就完了,我们拿最容易出错的样例 2 输入-380,一步步跑一遍程序,看清楚每个变量的变化。

程序启动读取 n = -380,sign 初始为 1,res 初始为 0

第一步:符号处理判断 n < 0 → 成立sign = -1,n = -(-380) = 380

第二步:逐位反转循环第一次循环(n=380):

  • digit = 380 % 10 = 0
  • res = 0 * 10 + 0 = 0
  • n = 380 / 10 = 38

第二次循环(n=38):

  • digit = 38 % 10 = 8
  • res = 0 * 10 + 8 = 8
  • n = 38 / 10 = 3

第三次循环(n=3):

  • digit = 3 % 10 = 3
  • res = 8 * 10 + 3 = 83
  • n = 3 / 10 = 0

循环结束(n=0,不满足 n>0)

第三步:输出结果 res * sign = 83 * (-1) = -83输出 -83,和样例完全一致。

再验证样例 3 输入 0:n=0,不进入循环,res=0,sign=1,输出 0,完全正确。

六、拓展:字符串反转写法

如果追求快速实现,也可以用字符串反转的思路,但算法训练更推荐数学解法:

复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        StringBuilder sb = new StringBuilder(s);
        sb.reverse(); // 直接反转字符串
        
        // 处理负号:如果原数是负数,反转后负号在末尾,移到开头
        if (s.charAt(0) == '-') {
            sb.deleteCharAt(sb.length() - 1); // 删掉末尾的负号
            sb.insert(0, '-'); // 插到最前面
        }
        
        // 转成int自动去前导零,输出
        System.out.println(Integer.parseInt(sb.toString()));
        sc.close();
    }
}

这种写法依赖 Java 内置方法,适合快速做题,但对数位思维的锻炼不如数学法,面试手写代码优先写数学解法。

七、总结~

核心:用取模取最后一位 + 除法去最后一位的循环完成数位反转,符号单独处理,前导零由累加逻辑自动消除,零值天然兼容。

避坑提醒:

  1. 不要直接对负数取模,先转正数再处理,避免符号错乱
  2. 不用特意写判断去前导零,res 从 0 开始累加会自动跳过
  3. 输入 0 的情况不用单独写分支,循环不执行天然输出 0

一句话:吃透取模 + 除法的数位操作,这一类题全是送分题。

++就到这里,诸位共勉!++