引言
前几天同学问了我一道蓝桥杯的试题,我想到了一种解法并成功运行,在做完后想去网上看看有没有更优的解决方法,但发现大家解法都一样,都是先转化成二进制再转化成八进制,我的解法网上其他人都没有提到过(也许不是独特的,但我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;
}
事实上这个解法并没有什么新奇的,肯定也有很多人想出来,因为实际上还是利用了二进制的特性,只不过写起来更简单。
如有错误欢迎指正。