演算法競賽——進位換算你會了嗎?

進位轉換

網上查找了很多關於進位轉換的部落格,發現好多不同進位之間的轉換程式碼實現過於複雜、冗餘。而進位換算又是演算法競賽常常考到的基礎知識點,清晰的程式碼實現是十分有必要的!今天我就針對常見的進位換算做一個詳細、清晰的總結,希望對你的學習或者競賽有些許幫助!

一、進位基本介紹

什麼是進位?

就是進位制,是人們規定的一種進位方法。 對於任何一種進位–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進位的整數:按權展開

image

0~15轉成二進位口算技巧:8421碼

轉為4位的二進位表示,4位的二進位數最多表示到15

8:2^3 4:2^2 2:2^1 1:2^0

image

(一)十進位與二進位之間的轉換

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進位:

image

8/16進位轉二進位:
image

1.二進位轉十六進位

題目描述

請將一個不超過100位的二進位數轉換為十六進位數!

輸入

一個不超過100位的二進位整數

輸出

該數對應的十六進位數!

樣例

輸入

11001001111011111000001000010011

輸出

C9EF8213

思路:

image

第一步:判斷字元串的長度是否為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位的二進位數!

image

第一步:將每位的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;
}

進位轉換應用例題:小麗找半個迴文數

image

求這個數在十進位下不是迴文數,但在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位二進位表示,轉換時直接使用,加快效率!

註:如果文章有任何錯誤或不足,請各位大佬盡情指出,評論留言留下您寶貴的建議!如果這篇文章對你有些許幫助,希望可愛親切的您點個贊推薦一手,非常感謝啦