进制转换3 学习笔记

一、题目核心信息

1. 题目描述

将 M 进制数 X 转换为 N 进制数输出(2≤M,N≤36);输入字母为大写,输出字母为小写。

2. 输入输出格式

  • 输入:第一行是进制 M、N;第二行是 M 进制数 X(字符串形式,支持数字+大写字母)。
  • 输出:X 的 N 进制表示(字母小写)。

3. 样例

输入:10 2 + 11 → 输出:1011(十进制11转二进制1011)。

二、初始代码思路:十进制中转法

1. 核心逻辑(两步走)

以十进制为中间桥梁,避免直接转换 M→N 的复杂度:
M进制字符串X
转十进制数
转N进制字符串
输出(小写)

2. 初始代码逐段解析

c 复制代码
#include<stdio.h>
#include<string.h>
#include<ctype.h>
int main()
{
    int m,n;
    char x[101];
    scanf("%d %d",&m,&n);
    scanf("%s",x); // 读取M进制字符串(兼容字母)
    
    // 步骤1:M进制 → 十进制(unsigned long long防溢出)
    unsigned long long decimal=0; // 关键:不用int!
    int len=strlen(x);
    for(int i=0;i<len;i++)
    {
        char c=x[i];
        int val=0;
        // 字符转数值:数字(0-9)→0-9,大写字母(A-Z)→10-35
        if(c>='0'&&c<='9') val=c-'0';
        else val=c-'A'+10;
        // 核心公式:逐位累加(位权=进制^位数)
        decimal=decimal*m+val;
    }
    
    // 步骤2:十进制 → N进制(除基取余法,余数逆序)
    char result[100];
    int idx=0;
    // 特殊处理:十进制为0的情况(避免结果为空)
    if(decimal==0) result[idx++]='0';
    else
    {
        while(decimal>0)
        {
            int remain=decimal%n; // 取余数(N进制的一位)
            // 余数转字符:0-9→数字,10-35→小写字母
            if(remain<10) result[idx++]=remain+'0';
            else result[idx++]=remain-10+'a';
            decimal/=n; // 整除N,缩小数值
        }
    }
    
    // 反转结果(取余得到的是逆序,需转正)
    for(int i=0;i<idx/2;i++)
    {
        char temp=result[idx-1-i];
        result[idx-1-i]=result[i];
        result[i]=temp;
    }
    result[idx]='\0'; // 字符串结束符
    printf("%s",result);
	return 0;
}

三、初始代码的核心问题与局限

1. 最大问题:数值溢出(为什么不能用int?)

  • int 通常是4字节,范围仅 -2147483648 ~ 2147483647(约21亿);
  • 若 M 进制数较长(如36进制的8位 ZZZZZZZZ,十进制值≈2.8万亿),int 会溢出,数值"绕回"导致结果错误;
  • 代码中用 unsigned long long(8字节,范围0~1800亿亿)可缓解,但仍无法处理超长字符串(如36进制15位以上)。

2. 其他局限

  • 输入缓冲区固定为101字节,超长输入会溢出;
  • 无输入验证(如 M=16 时输入 'G' 会计算错误);
  • 十进制中转法本质依赖数值类型,无法处理任意长度的大数。

四、核心知识点总结

1. 进制转换核心原理

  • M进制→十进制:按权展开 decimal = decimal*M + 位值(从高位到低位);
  • 十进制→N进制:除基取余法(除以N取余数,逆序排列余数);
  • 字符与数值转换:
    • 大写字母→数值:val = c-'A'+10(A=10,Z=35);
    • 数值→小写字母:c = val-10+'a'(10=a,35=z)。

2. 关键注意点

  • 必须处理"输入为0"的边界情况(否则结果为空);
  • 除基取余得到的余数是逆序,需反转后输出;
  • 输入字母大写、输出小写是题目硬性要求。

五、优化方案:大数直接转换法(解决溢出问题)

1. 核心思路(放弃十进制中转)

直接对 M 进制字符串模拟"手工除法":不断除以 N,每次余数为 N 进制的一位,直到商为0(余数逆序即为结果)。


M进制字符串X
X是否为0?
X除以N,得到商(M进制)+余数(N进制一位)
余数存入结果
反转结果输出

2. 优化后核心函数(大数除法)

c 复制代码
// 大数除法:M进制字符串 ÷ N,基数固定为M,返回余数,商存入quotient
int big_divide(const char *dividend, int N, int base, char *quotient) {
    int remainder = 0;
    char temp_quotient[1024] = {0};
    int q_idx = 0;
    int len = strlen(dividend);
    for (int i = 0; i < len; i++) {
        char c = dividend[i];
        int digit = isdigit(c) ? (c-'0') : (c-'A'+10);
        // 纯int计算(无溢出:remainder≤35 * base≤36 + digit≤35 = 1295 < int范围)
        int current = remainder * base + digit;
        int q_digit = current / N;
        remainder = current % N;
        // 商转字符(M进制,大写)
        temp_quotient[q_idx++] = (q_digit<10) ? (q_digit+'0') : (q_digit-10+'A');
    }
    temp_quotient[q_idx] = '\0';
    // 移除商的前导零
    remove_leading_zeros(temp_quotient, quotient);
    return remainder;
}

六、易错点与避坑指南

  1. 数据类型选择 :十进制中转法优先用 unsigned long long,大数场景必须用字符串模拟;
  2. 前导零处理 :除法得到的商需移除前导零,避免出现 00123 这类无效字符串;
  3. 输入安全性 :用 scanf("%1023s", x) 限制输入长度,避免缓冲区溢出;
  4. 进制基数一致性:大数除法中,被除数始终是 M 进制,因此基数固定为 M(核心易错点)。

七、最终结论

  • 十进制中转法:思路简单,适合小数据场景,机试基础分必拿;
  • 大数直接转换法:无溢出风险,能处理任意长度输入,是机试满分方案;
  • 核心原则:进制转换的本质是"位权运算",无论哪种方法,都要保证字符-数值转换、基数计算的正确性。
相关推荐
Purple Coder2 小时前
人工智能学习路线
学习
小帅学编程2 小时前
Spring(侧重注解开发)
java·学习·spring
爱喝水的鱼丶2 小时前
SAP-ABAP:在SAP世界里与特殊字符“斗智斗勇”:一份来自实战的避坑指南
运维·服务器·数据库·学习·sap·abap·特殊字符
科技林总3 小时前
【系统分析师】认证介绍
学习
日更嵌入式的打工仔3 小时前
Ethercat EOE笔记
网络·笔记·ethercat
不吃橘子的橘猫3 小时前
NVIDIA DLI 《Build a Deep Research Agent》学习笔记
开发语言·数据库·笔记·python·学习·算法·ai
算法与双吉汉堡3 小时前
【短链接项目笔记】6 短链接跳转
java·开发语言·笔记·后端·springboot
刘孬孬沉迷学习3 小时前
层与天线的区别
网络·学习·5g·信息与通信·mimo·预编码·层映射
冬夜戏雪3 小时前
【学习日记】【12.30】【14/60】
服务器·网络·学习