進制轉換
網上查找了很多關於進制轉換的博客,發現好多不同進制之間的轉換代碼實現過於復雜、冗余。而進制換算又是算法競賽常常考到的基礎知識點,清晰的代碼實現是十分有必要的!今天我就針對常見的進制換算做一個詳細、清晰的總結,希望對你的學習或者競賽有些許幫助!
一、進制基本介紹
什么是進制?
就是進位制,是人們規定的一種進位方法。 對於任何一種進制–X進制,就表示某一位置上的數運算時是逢X進一位。
二進制就是逢二進一,八進制是逢八進一,十進制是逢十進一,十六進制是逢十六進一。
- 二進制表示的數中只能由
0~1
的數組成 - 八進制表示的數中只能由
0~7
的數組成 - 十六進制表示的數中只能由
0~9 A~F
的數(字符)組成
n進制如何數數?
十進制:0 1 2 3 4 5 6 7 8 9 10 11........
二進制:0 1 10 11 100 101 110 111 1000 1001 ......
八進制:0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20 21.......
十六進制:0 1 2 3 4 5 6 7 8 9 A B C D E F 10 .... 18 19 1A 1B 1C 1D.....
二、十進制與R進制之間的轉換
十進制轉R進制
十進制轉成R進制的整數:除R取余法,結果倒過來取
注:十進制轉16進制時,如果除出來的余數為
10~15
10 ——> A
11 ——> B
12 ——> C
13 ——> D
14 ——> E
15 ——> F
R進制轉10進制
R進制轉成10進制的整數:按權展開
0~15轉成二進制口算技巧:8421碼
轉為4位的二進制表示,4位的二進制數最多表示到15
8:2^3 4:2^2 2:2^1 1:2^0
(一)十進制與二進制之間的轉換
1.十進制轉二進制
思路:用短除法除2求余,將結果逆序存儲字符串string(你用數組逆序存也可以(棧),string其實說白了也是有數組的功能)
【參考代碼】
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL n, x;
char c;
string s; // 二進制結果
int main()
{
cin >> n;
while(n != 0)
{
x = n % 2;// 獲取結果
c = x + '0';// 將數字轉成字符
s = c + s; // 將結果逆序存入字符串
n /= 2; // 繼續短除
}
// 這里不能用 n == 0判斷特殊情況,因為while介紹 n 最后肯定為0
if(s == "") cout << 0;
else cout << s;
return 0;
}
2.二進制轉十進制
題目描述
請將一個25位以內的2進制正整數轉換為10進制!
輸入
一個25位以內的二進制正整數
輸出
該數對應的十進制
樣例
輸入
111111111111111111111111
輸出
16777215
思路:
從最低位開始(size()-1
),倒過來計算,按權展開。
字符轉成整數:s[i] - '0'
准備變量t
,表示2
的n
次方,t = 1
,每次循環一次t = t * 2
【參考代碼】
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL t = 1, r; // t 表示權重; r 記錄二轉十的結果
string s; // 存放二進制
int main()
{
cin >> s;
for(int i = s.size() - 1; i >= 0; i --)
{
r = r + (s[i] - '0') * t;
t = t * 2;
}
cout << r;
return 0;
}
(二)十進制與十六進制之間的轉換
1. 十進制轉十六進制
題目描述
請從鍵盤讀入一個非負整數n(n是一個不超過18位的正整數),將n轉換為16進制!
注意:16進制即逢16進1,每一位上可以是從小到大為0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F共16個大小不同的數,即逢16進1,其中用A,B,C,D,E,F這六個字母來分別表示10,11,12,13,14,15。
如:60的十六進制為3C。(字母請用大寫)
輸入
一個不超過18位的非負整數n
輸出
該數的十六進制值
樣例
輸入
100000000000
輸出
174876E800
思路:
短除法除16取余,結果逆序存儲。逆序存儲字符串時要注意:
當余數為:
整數0~9
,轉換為字符'0'~'9'
:x + '0'
整數10~15
,轉換為字符'A'~'F'
:x + 'A' - 10
注:
int
最多表達 2^31 - 1,10
位整數;
long long
最多表達 2^63 - 1,19
位整數當數值超過int范圍要開long long
【參考代碼】
解法一:
分別判斷 n%16
的結果在0~9
及10~15
的那個范圍,分別轉換為對應的字符!
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL n, x;
string s; // 存放二進制
char c;
int main()
{
cin >> n;
while(n != 0)
{
x = n % 16;
//將x轉為字符逆序存入字符串s
// 如果余數小於10:'0'~'9'
// 如果余數10~15:'A'~'F'
if(x < 10) c = x + '0';
else c = x + 'A' - 10;
s = c + s; // 逆序存入字符串
n /= 16;
}
if(s == "") cout << 0;
else cout << s;
return 0;
}
解法二:
把0~15的字符結果羅列出來,根據余數直接取到相應的字符:string t = "0123456789ABCDEF";
// 技巧!
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL n, x;
string s; // 存放二進制
string t = "0123456789ABCDEF"; // 技巧!
int main()
{
cin >> n;
while(n != 0)
{
x = n % 16;
//將x轉為字符逆序存入字符串s
s = t[x] + s; // 逆序存入字符串
n /= 16;
}
if(s == "") cout << 0;
else cout << s;
return 0;
}
2. 十六進制轉十進制
題目描述
請將一個不超過10位的十六進制正整數轉換為十進制整數!
輸入
10位以內的十六進制正整數
輸出
該數對應的十進制整數
樣例
輸入復制
2ECF
輸出
11983
思路:逆序計算,按權展開!從s中獲取每一位字符s[i],要轉換為實際的整數:
s[i]:'0'~'9'
——> s[i] - '0'
s[i]:'A~'F'
——> s[i] - 'A' + 10
【參考代碼】
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL n, r, t = 1;
string s; // 存放二進制
int main()
{
cin >> s;
for(int i = s.size() - 1; i >= 0; i --)
{
// 如果s[i]是字符數字
if(s[i] >= '0' && s[i] <= '9') // 用函數判斷也可以 isdigit(s[i]):判斷是不是字符數字
r = r + (s[i] - '0') * t;
else
r = r + (s[i] - 'A' + 10) * t;
t = t * 16;
}
cout << r;
return 0;
}
三、二進制與八/十六進制的轉換
我們知道二進制轉為八/十六進制,可以先將二進制轉為十進制,再用短除法求八/十六進制,但這樣就比較麻煩,添加代碼量!我們可以直接進行轉換,就相當於一個模板!
原理:每位八進制數相當於3位
二進制數(07),每位十六進制數相當於`4位`二進制數(015)。在轉換時,中間的0不能省略,開頭不夠時可以補零。
二進制轉8/16進制:
8/16進制轉二進制:
1.二進制轉十六進制
題目描述
請將一個不超過100位的二進制數轉換為十六進制數!
輸入
一個不超過100位的二進制整數
輸出
該數對應的十六進制數!
樣例
輸入
11001001111011111000001000010011
輸出
C9EF8213
思路:
第一步:判斷字符串的長度是否為4的倍數,如果不是則補0。
s:字符數組
s.size() % 4 == 1,補3個0
s.size() % 4 == 2,補2個0
s.size() % 4 == 3,補1個0
第二步:每四位2進制數轉換成1位的16進制數(借助10進制在轉)輸出。
“1101”轉換為對應的十進制整數 --> 13,注意判斷轉換的結果如果是0~9
,轉換為'0'~'9'
,如果轉換的結果是10~15
,轉換為'A'~'F'
將四位的二進制轉換為1位的16進制:
char num(string s)
{
...
}
【參考代碼】
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
// 將4位的二進制轉成10進制整數 在對應變為16進制符號:0~9 -> '0'~'9'; 10~15 -> 'A'~'F'
char num(string s)
{
LL r = 0, t = 1; // r = 0, t = 1;別忘了
// 從最低位按權展開,轉換為10進制
// 10進制在轉為16進制
for(int i = s.size() - 1; i >= 0; i --)
{
r = r + (s[i] - '0') * t;
t = t * 2;
}
//10進制轉16進制: 0~9 -> '0'~'9'; 10~15 -> 'A'~'F'
char c; // 存放結果
if(r < 10) c = r + '0';
else c = r + 'A' - 10;
return c;
}
int main()
{
string s, t; // 存放二進制
cin >> s;
// 看是否需要補零
if(s.size() % 2 == 1) s = "000" + s;
else if(s.size() % 2 == 2) s = "00" + s;
else if(s.size() % 2 == 3) s = "0" + s;
// 每每截取4位二進制數:substr(i, 4)截取字符串,i += 4
for(int i = 0; i < s.size(); i += 4)
{
t = s.substr(i, 4);
// 將4位二進制轉換為16進制,輸出
cout << num(t);
}
return 0;
}
2.十六進制轉二進制
思路:將每一位的16進制數,轉換位4位的二進制數!
第一步:將每位的16進制轉換為4位的2進制,連接到字符串上!
第二步:清除前導0,也就是要從第一個非0開始輸出。注:當16進制為0000時,轉成二進制后也為0,此時不能把0000全部清除掉,要保留一個0!
【參考代碼】
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
// 技巧:
string t[16] = {"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"};
//求: 將每位16進制轉為對應的4為二進制
//1. 將s[i]轉換為0~15對應的整數,再求對應的4位2進制(技巧:通過數組下標獲取對應二進制)
//2. 最終清除二進制字符串的前導0
int main()
{
string s, r; // s存放16進制; r存放2進制
cin >> s;
for(int i = 0; i < s.size(); i ++)
{
//1. 將s[i]轉換為0~15對應的整數
int x;
if(s[i] >= '0' && s[i] <= '9') x = s[i] - '0'; // 如果是0~9
else x = s[i] - 'A' + 10;
//1. 用字符串拼接16進制所對應表示的2進制
r += t[x];
}
//2. 最后清除前導0
while(r[0] == '0' && r.size() > 1)
{
r.erase(0,1);
}
cout << r;
return 0;
}
C++ STL中的:erase()函數的功能是用來刪除容器中的元素
erase(first,last);------>左閉右開: [first,last)
3.二進制轉八進制
題目描述
請將一個100位以內的二進制整數轉換為8進制整數!
輸入
100位以內的二進制整數
輸出
該數對應的八進制整數
樣例
輸入
111100001111000011110000
輸出
74170360
思路:同上述二進制轉16進制
第一步:判斷字符串的長度是否為3的倍數,如果不是則補0。
s:字符數組
s.size() % 3 == 1,補2個0
s.size() % 4 == 2,補1個0
第二步:每三位2進制數轉換成1位的8進制數(07)輸出。(每3位的二進制數是:08,9的話是1001四位了!),即轉成對應10進制即可
【參考代碼】
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
// 將3位的二進制(一定是0~7) 轉成 10進制整數 對應的8進制必定也是:0 ~ 7
LL num(string s)
{
LL t = 1, r = 0;
// 從最低位按權展開,轉換為10進制
for(int i = s.size() - 1; i >= 0; i --)
{
r = r + (s[i] - '0') * t;
t = t * 2;
}
return r;
}
int main()
{
string s, t; // s存放二進制
cin >> s;
// 看是否需要補0
if(s.size() % 3 == 1) s = "00" + s;
else if(s.size() % 3 == 2) s = "0" + s;
// 截取3位二進制,轉為對應的八進制,輸出
for(int i = 0; i < s.size(); i += 3)
{
t = s.substr(i, 3);
// 將每每3位二進制轉為八進制並輸出
cout << num(t);
}
return 0;
}
4.八進制轉二進制
題目描述
請將一個100位以內的8進制整數轉換為2進制整數!
輸入
100位以內的8進制整數
輸出
該數對應的2進制整數
樣例
輸入
12376532347173217361
輸出
1010011111110101011010011100111001111011010001111011110001
思路:同16進制轉二進制:將每一位的8進制數,轉換為3位的二進制數!
第一步:將每位的8進制(0~7)轉換為3位的2進制,連接到字符串上!
第二步:清除前導0,也就是要從第一個非0開始輸出。注:當8進制為000時,轉成二進制后也為0,此時不能把000全部清除掉,要保留一個0!
【參考代碼】
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
// 存儲0~7 八進制對應的 3位二進制
string t[8] = {"000","001","010","011","100","101","110","111"};
int main()
{
string s, r; // s存放八進制,r存放二進制
cin >> s;
// 8進制 ——> 2 進制:將每一位8進制轉換為3位的2進制(通過數字對應數組直接獲取!)然后拼接
for(int i = 0; i < s.size(); i ++)
{
int x = s[i] - '0'; // 找到每一位8進制所對應的2進制表示
r += t[x]; // 拼接
}
// 清除前導0
while(r[0] == '0' && r.size() > 1)
{
r.erase(0, 1);
}
cout << r;
return 0;
}
進制轉換應用例題:小麗找半個回文數
求這個數在十進制下不是回文數,但在2進制或者16進制下是回文數,則這個數是半個回文數!
思路:
朴素的想法,判斷這個十進制數是否是回文數,如果不是則將其轉為2進制數和16進制數,再判斷是否是回文數!
我們發現一個很相似的操作,那就是這個數在d進制表示下,是否是回文數,我們知道10進制數轉為其它進制使用的是短除法!,我們不必用進制表示的最終結果來判斷是否是回文,我們可以將每一位余數存到一個數組中,判斷它的余數是否為回文即可!——因此,我們可以寫個判斷函數進行判斷統一判斷就可以啦!
【參考代碼】
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
LL n, x;
int a[N]; // 存儲n轉為d進制的每一位對應的余數,用來判斷是否是回文數
// 將 十進制數 n 轉換為 d進制數 並判斷它是不是回文!
bool huiwen(int n, int d)
{
int k = 0;
while(n != 0)
{
x = n % d;
a[k ++] = x; // 存儲余數
n /= d;
}
// 判斷回文:對稱位是否相等,出現不等則不是回文數
for(int i = 0; i < k / 2; i ++)
{
//0 --> k - 1
//1 --> k - 2
//2 --> k - 3... i --> k - i - 1
if(a[i] != a[k - i -1])
{
return false;
break;
}
}
return true;
}
int main()
{
cin >> n;
while(n --)
{
int x;
cin >> x;
if(huiwen(x, 10) == false && (huiwen(x, 2) == true || huiwen(x, 16) == true))
{
cout << x << endl;
}
}
return 0;
}
四、總結
進制轉換可以通過類比10進制計算機制來換算,清楚轉換的原理公式即可。再8/16進制轉2進制時,使用一個字符串數組記錄對應的8/16每一位的3/4位二進制表示,轉換時直接使用,加快效率!
注:如果文章有任何錯誤或不足,請各位大佬盡情指出,評論留言留下您寶貴的建議!如果這篇文章對你有些許幫助,希望可愛親切的您點個贊推薦一手,非常感謝啦