蓝桥杯试题 【十六进制转八进制】简单易懂的解法,无需转化成二进制 c语言

引言

前几天同学问了我一道蓝桥杯的试题,我想到了一种解法并成功运行,在做完后想去网上看看有没有更优的解决方法,但发现大家解法都一样,都是先转化成二进制再转化成八进制,我的解法网上其他人都没有提到过(也许不是独特的,但我csdn中查了一下好像没看到别人这样解,估计有大佬想过但没有写成博客)。本文将介绍这个解法的思路和实现过程。

问题描述

将一个十六进制数转换为八进制数。

输入格式

  输入的第一行为一个正整数n (1<=n<=10)。

  接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。

传统解法

将十六进制数每一位转换为四位二进制数,然后将得到的二进制数每三位一组,从低位开始分组,不足三位则在高位补0,再将每组三位二进制数转换为对应的八进制数即可。

下面的解法是我在网上找到的转化位二进制的解法

#include<stdio.h>
#include<string.h>
 
void hex2bin(char left[], int count, char ch);
char bin2oct(char left[], int poi);
 
/* 基本思想是将输入的十六进制字符串装入hexs字符数组中,
    从hexs尾部一个一个取字符转换成二进制字符从尾部开始装入left字符数组中,
    将三个十六进制字符(3 * 4 = 12)装入left中后,
    从left尾部以三个二进制数一组(4 * 3 = 12)转换成八进制装入octs[i]字符数组中 
    重新执行上述步骤
*/ 
int main() {
    char hexs[100001], octs[10][130000], left[12];
    int n, i, j, index, hexs_len, count;
    int octs_len[10];
    
    scanf("%d", &n);
    for(i = 0; i < n; i++) {
        
        scanf("%s", hexs);
        hexs_len = strlen(hexs);
        count = 0;
        index = 0;
        
        for(j = hexs_len - 1; j >= 0; j--) {
            hex2bin(left, count, hexs[j]);
            
            count++;
            
            if(count == 3) { // count == 3时,left存满三个十六进制字符 
                for(; count >= 0; count--) {
                    octs[i][index++] = bin2oct(left, count);
                }
                count = 0;
            }
        }
        
        if(count == 1) { // 三个一组从hexs字符数组中取字符不能取完,剩下了一个,即left中还有4个二进制字符 
            octs[i][index++] = bin2oct(left, 3);
            if(left[8] == '1') octs[i][index++] = '1';
        } else if(count == 2) { // 剩下了两个 ,left中还有8个二进制字符 
            octs[i][index++] = bin2oct(left, 3);
            octs[i][index++] = bin2oct(left, 2);
            if(left[4] == '0' && left[5] == '1') octs[i][index++] = '1';
            else if(left[4] == '1' && left[5] == '0') octs[i][index++] = '2';
            else if(left[4] == '1' && left[5] == '1') octs[i][index++] = '3';
        }
        
        if(octs[i][index - 1] == '0') index--; // 如果前导数为零,删除之 
        
        octs_len[i] = index; //将转换成的八进制字符串长度存入octs_len中 
    }
    
    for(i = 0; i < n; i++) {
        for(j = octs_len[i] - 1; j >= 0; j--)
            putchar(octs[i][j]);
        
        putchar('\n');
    }
    
    return 0;
}
 
void hex2bin(char left[], int count, char ch) { // 一个十六进制字符转换成四个二进制字符并存入left中 
    switch(ch) {
        case '0': strncpy(left + 8 - count * 4, "0000", 4); break;
        case '1': strncpy(left + 8 - count * 4, "0001", 4); break;
        case '2': strncpy(left + 8 - count * 4, "0010", 4); break;
        case '3': strncpy(left + 8 - count * 4, "0011", 4); break;
        case '4': strncpy(left + 8 - count * 4, "0100", 4); break;
        case '5': strncpy(left + 8 - count * 4, "0101", 4); break;
        case '6': strncpy(left + 8 - count * 4, "0110", 4); break;
        case '7': strncpy(left + 8 - count * 4, "0111", 4); break;
        case '8': strncpy(left + 8 - count * 4, "1000", 4); break;
        case '9': strncpy(left + 8 - count * 4, "1001", 4); break;
        case 'A': strncpy(left + 8 - count * 4, "1010", 4); break;
        case 'B': strncpy(left + 8 - count * 4, "1011", 4); break;
        case 'C': strncpy(left + 8 - count * 4, "1100", 4); break;
        case 'D': strncpy(left + 8 - count * 4, "1101", 4); break;
        case 'E': strncpy(left + 8 - count * 4, "1110", 4); break;
        case 'F': strncpy(left + 8 - count * 4, "1111", 4); break;
    }
} 
 
char bin2oct(char left[], int poi) { // 三个二进制字符转成一个八进制字符
    if(left[poi*3+0]=='0' && left[poi*3+1]=='0' && left[poi*3+2]=='0') return '0';
    else if(left[poi*3+0]=='0' && left[poi*3+1]=='0' && left[poi*3+2]=='1') return '1';
    else if(left[poi*3+0]=='0' && left[poi*3+1]=='1' && left[poi*3+2]=='0') return '2';
    else if(left[poi*3+0]=='0' && left[poi*3+1]=='1' && left[poi*3+2]=='1') return '3';
    else if(left[poi*3+0]=='1' && left[poi*3+1]=='0' && left[poi*3+2]=='0') return '4';
    else if(left[poi*3+0]=='1' && left[poi*3+1]=='0' && left[poi*3+2]=='1') return '5';
    else if(left[poi*3+0]=='1' && left[poi*3+1]=='1' && left[poi*3+2]=='0') return '6';
    else if(left[poi*3+0]=='1' && left[poi*3+1]=='1' && left[poi*3+2]=='1') return '7';
}

新解法的思路

将每三位十六进制数字转化位十进制放到一个int变量中,再将这个变量当成八进制打印,最后拼接起来

例如:十六进制ABCDEF,将ABC 转化成十进制放到变量a中(不需要知道这个数是多少),将DEF放到变量b中,最后将a当成四位数的八进制数字打印,结果为5274,b为6757,而你会发现,ABCDEF打转化成八进制的结果刚好就是52746757即两个数拼接起来。

注意:当十六进制数的个数不是3的倍数时,要处理好边界问题。如:ABCD,要将A当成一个int,将BCD当成另一个。

新解法的实现

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>

int main()
{
    int n;
    scanf("%d", &n);
    int** pp = (int**)malloc(sizeof(int*) * n);
    int* count = (int*)malloc(sizeof(int) * n);
    char str[100001] = "0";
    int i, j, k;
    for (i = 0; i < n; i++)
    {
        pp[i] = (int*)malloc(sizeof(int) * 50000);
    }
    //读取十六进制数字,每三个当成一个int型整数知道遇到换行
    for (i = 0; i < n; i++)
    {
        scanf("%s", str);
        char* p = str;
        int len = strlen(str);
        //处理第一个特殊的情况
        if (len % 3 == 1)
        {
            sscanf(p, "%1x", &pp[i][0]);
            p += 1;
            len -= 1;
        }
        else if (len % 3 == 2)
        {
            sscanf(p, "%2x", &pp[i][0]);
            len -= 2;
            p += 2;
        }
        else
        {
            sscanf(p, "%3x", &pp[i][0]);
            p += 3;
            len -= 3;
        }
        //处理剩下的数字
        for (j = 0,k=1; j < len; j++)
        {
            if (j % 3 == 2)
            {
                sscanf(p, "%3x", &pp[i][k]);
                p += 3;
                k++;
            }
        }
        count[i] = k;
    }
    //将每个int数据当成4位数的八进制数打印
    for (i = 0; i < n; i++)
    {
        //第一个特殊情况
        printf("%o", pp[i][0]);
        for (j = 1; j < count[i]; j++)
            printf("%04o", pp[i][j]);
        printf("\n");
    }
    //释放内存
    for (i = 0; i < n; i++)
    {
        free(pp[i]);
    }
    free(pp);
    free(count);
    return 0;
}

事实上这个解法并没有什么新奇的,肯定也有很多人想出来,因为实际上还是利用了二进制的特性,只不过写起来更简单。

如有错误欢迎指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值