火龙信奥
  • 分享
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

整数拆位

作者: 作者的头像   huolong , 时间:2026-08-24 15:39:07 , 所有人可见, 阅读  58

C++ 整数拆位(提取个十百千位)完全技术讲义

在编程入门与算法题(如数位和、回文数、水仙花数、大数处理)中,拆位(Digit Extraction) 是最基础但极其关键的核心技能。


目录

  1. 拆位的核心数学原理
  2. 固定位数拆解(三位/四位数为例)
  3. 通用任意长度拆解(循环提取)
  4. 负数与 0 的边界处理(C++11 行为)
  5. 字符/字符串法拆位(处理超大整数)
  6. 四大经典实战应用
  7. 速查公式总结

一、拆位的核心数学原理

拆位的本质是利用十进制的 整数除法(/) 和 取模运算(%):

  • x % 10(取个位):获取当前数字的最末尾一位(个位)。
  • x / 10(降位/剔除末尾):在整数除法中会舍弃小数部分,相当于将整个数向右移一位(删掉当前的个位数)。
以提取 1234 的每一位为例:
1234 % 10  =  4    --->  拿到【个位 4】
1234 / 10  =  123  --->  降位,去掉 4,剩下 123

 123 % 10  =  3    --->  拿到【十位 3】
 123 / 10  =  12   --->  降位,去掉 3,剩下 12

  12 % 10  =  2    --->  拿到【百位 2】
  12 / 10  =  1    --->  降位,去掉 2,剩下 1

   1 % 10  =  1    --->  拿到【千位 1】
   1 / 10  =  0    --->  降位,结果为 0,提取结束!

二、固定位数拆解

若已知数字的位数区间(例如明确是三位数或四位数),可以直接通过数学公式单行提取:

1. 三位数提取示例(以 $n = 358$ 为例)

#include <iostream>

int main() {
    int n = 358;

    int ge  = n % 10;          // 358 % 10 = 8
    int shi = (n / 10) % 10;   // 358 / 10 = 35, 35 % 10 = 5
    int bai = n / 100;         // 358 / 100 = 3 (因为是最高位,无需再 % 10)

    std::cout << "百位: " << bai << "\n"
              << "十位: " << shi << "\n"
              << "个位: " << ge  << std::endl;
    return 0;
}

2. 四位数提取公式

int ge    = n % 10;
int shi   = (n / 10) % 10;
int bai   = (n / 100) % 10;
int qian  = n / 1000;

通用第 $k$ 位公式(设个位为第 0 位,十位为第 1 位……): $$\text{Digit}_k = \left( \frac{n}{10^k} \right) \pmod{10}$$


三、通用任意长度拆解

当数字长度未知(如输入的 int 或 long long 不定长),通常配合循环操作。

1. 逆序提取(从个位到最高位)

这是最自然的拆位方式,最右边的位最先被提取出来:

#include <iostream>
#include <vector>

int main() {
    int num = 987654;
    int temp = num;

    std::cout << "逆序拆位: ";
    while (temp > 0) {
        int digit = temp % 10;   // 1. 提取末尾位
        std::cout << digit << " ";
        temp /= 10;              // 2. 剥离末尾位
    }
    // 输出: 4 5 6 7 8 9
    std::cout << std::endl;

    return 0;
}

2. 正序提取(从最高位到个位)

方法 A:利用 std::vector 逆转或头插

#include <iostream>
#include <vector>
#include <algorithm>

std::vector<int> getDigits(int n) {
    std::vector<int> digits;
    if (n == 0) return {0};

    while (n > 0) {
        digits.push_back(n % 10);
        n /= 10;
    }
    // 当前 digits 是 [个, 十, 百...],翻转后即为 [最高位 ... 个位]
    std::reverse(digits.begin(), digits.end());
    return digits;
}

int main() {
    auto digits = getDigits(12345);
    for (int d : digits) {
        std::cout << d << " "; // 输出: 1 2 3 4 5
    }
    std::cout << std::endl;
    return 0;
}

方法 B:纯数学正序提取(通过计算最高权值)

#include <iostream>

void printDigitsForward(int n) {
    if (n == 0) {
        std::cout << 0 << std::endl;
        return;
    }
    // 1. 寻找最高位对应的 10 的幂(例如 1234 -> 1000)
    int power = 1;
    while (n / power >= 10) {
        power *= 10;
    }

    // 2. 从高向低逐位剥离
    while (power > 0) {
        int digit = n / power;     // 取得最高位
        std::cout << digit << " ";
        n %= power;                // 删去最高位
        power /= 10;               // 权值降低一位
    }
    std::cout << std::endl;
}

int main() {
    printDigitsForward(12345); // 输出: 1 2 3 4 5
    return 0;
}

四、负数与 0 的边界处理

在 C++11 标准中,对于负数整除与求模: * 截断规则:向零取整(Truncate toward zero)。 * 负数取模结果可能为负数:例如 -123 % 10 在 C++ 中为 -3,-123 / 10 为 -12。

1. 0 的处理(推荐 do-while)

如果使用普通的 while (n > 0),当输入 0 时循环体会一次都不执行。使用 do-while 结构可以完美解决:

#include <iostream>
#include <cmath>

void solveAnyInteger(int n) {
    // 转换为正数处理(注意:INT_MIN 需考虑溢出,通常使用 long long 或单独特判)
    long long val = std::abs(static_cast<long long>(n));

    do {
        int digit = val % 10;
        std::cout << digit << " ";
        val /= 10;
    } while (val > 0);

    std::cout << std::endl;
}

int main() {
    solveAnyInteger(0);    // 正常输出: 0
    solveAnyInteger(-520); // 正常输出: 0 2 5
    return 0;
}

五、字符/字符串法拆位

当输入的数字非常大(超过 long long 的范围,例如 100 位的大整数),无法使用数值整除,此时应以字符串形式读取并拆位。

原理:字符 ASCII 码相减

字符 '0' 到 '9' 的 ASCII 码是连续的,只要用当前字符减去 '0',即可直接转换为对应的数字: $$\text{数值} = \text{字符} - \text{'0'}$$

#include <iostream>
#include <string>

int main() {
    // 输入一个超出 64 位整数范围的超长数字
    std::string bigNum = "987654321987654321987654321";

    std::cout << "正序遍历大整数的每一位: ";
    for (char ch : bigNum) {
        int digit = ch - '0'; // 关键转换: '9' - '0' = 9
        std::cout << digit << " ";
    }
    std::cout << std::endl;

    return 0;
}

六、四大经典实战应用

1. 计算数位和(Sum of Digits)

输入一个正整数,求其各位数字之和。

#include <iostream>

int getDigitSum(int n) {
    int sum = 0;
    while (n > 0) {
        sum += n % 10;
        n /= 10;
    }
    return sum;
}

int main() {
    std::cout << "12345 的数位和为: " << getDigitSum(12345) << std::endl; // 输出: 15
    return 0;
}

2. 整数反转(Number Reversal)

输入 1234,输出 4321(LeetCode 经典题目原型)。

#include <iostream>

int reverseNumber(int n) {
    int rev = 0;
    while (n > 0) {
        // 核心:上一轮的结果乘 10 + 当前个位数
        rev = rev * 10 + (n % 10);
        n /= 10;
    }
    return rev;
}

int main() {
    std::cout << "12345 反转后为: " << reverseNumber(12345) << std::endl; // 输出: 54321
    return 0;
}

3. 水仙花数(Narcissistic Number)

一个三位数,其各位数字立方和等于该数本身(如 $153 = 1^3 + 5^3 + 3^3$)。

#include <iostream>

int main() {
    std::cout << "所有的三位水仙花数: ";
    for (int i = 100; i <= 999; ++i) {
        int a = i / 100;        // 百位
        int b = (i / 10) % 10;  // 十位
        int c = i % 10;         // 个位

        if (a * a * a + b * b * b + c * c * c == i) {
            std::cout << i << " "; // 输出: 153 370 371 407
        }
    }
    std::cout << std::endl;
    return 0;
}

4. 回文数判定(Palindrome Number)

判断一个数正着读和反着读是否相同(如 12321)。

#include <iostream>

bool isPalindrome(int x) {
    if (x < 0) return false;          // 负数不是回文数
    if (x % 10 == 0 && x != 0) return false; // 末尾为0但本身不为0的一定不是

    int reversedHalf = 0;
    // 只反转后半部分,避免整个反转导致溢出
    while (x > reversedHalf) {
        reversedHalf = reversedHalf * 10 + x % 10;
        x /= 10;
    }

    // 偶数长度: x == reversedHalf (例如 1221 -> x=12, rev=12)
    // 奇数长度: x == reversedHalf / 10 (例如 12321 -> x=12, rev=123)
    return (x == reversedHalf) || (x == reversedHalf / 10);
}

int main() {
    std::cout << std::boolalpha;
    std::cout << "12321 是回文数吗: " << isPalindrome(12321) << std::endl; // true
    std::cout << "12345 是回文数吗: " << isPalindrome(12345) << std::endl; // false
    return 0;
}

七、速查公式总结

操作 公式 / 代码实现 说明
取个位 n % 10 获取最右侧的一位数
去个位 n / 10 整体向右平移一位
取十位 (n / 10) % 10 先去个位,再取末尾
取百位 (n / 100) % 10 先去个、十位,再取末尾
取第 $k$ 位 (n / pow(10, k)) % 10 $k$ 从 0 开始(0 代表个位)
字符转数字 ch - '0' 针对单字符 '0'~'9'
反转累加构造 res = res * 10 + digit 逆序重构整数的标准逻辑

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

关于火龙

  • 关于我们
  • 学员获奖
  • 预约试听
  • ACM课程
  • CSP课程
  • 学习指南

帮助中心

  • 用户协议
  • 打字练习
  • 在线画图
  • DevC++下载
  • CSP报名
  • GESP官网

推荐课程

  • C++零基础入门(可试看)
  • C++进阶提升
  • GESP考级辅导
  • GESP打卡
  • CSP-J/S打卡

公众号

火龙信奥公众号二维码

地址:义乌市北门街188号新天地商厦二楼2F 邮箱:wdlok305@126.com

© 2017-2026 义乌市睿码科技有限公司版权所有 浙ICP备2021013995号

火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码