《算法零基础100讲》(第19讲) 进制转换(一) - 入门[C语言题解]

一.概念定义

1.1何为进制

  在我们的日常生活中,使用的数字计算进制都是逢十进一,当一个数字满十的时候,就往更高位进一,而这种计算方式就是所谓的十进制。
  当然我们还有很多其他的进制方式,目前常用的有二进制,十进制和十六进制。

二. 进制转化

对于进制的转化,我们用二进制与十进制之间的转化为例:
11101转化为十进制的计算过程为:

1 * 2 ^ 4 + 1 * 2 ^ 3 + 1 * 2 ^ 2 + 0 * 2 * 1 + 1 * 2 ^ 0

得:

= 16 + 8 + 4 + 0 + 1
得:
= 29
我们设a为二进制的数,bi为二进制an的第i为的数,则公式为:
an = bn * 2 ^ (n - 1) + b(n - 1) * 2 ^ (n - 2) +.......+ b₀ * 2 ^ 0

  二进制转十进制是每一位上的数乘对应的2次幂,并相加得到,则反过来,十进制转化为二进制,应该用过模计算得到二进制的每一位上的数。
如图举例:
在这里插入图片描述
通过分析,我们可以得知十进制转化为二进制,我们可以通过不断地取模和辗转相除的方式进行转化。
代码如下:

#include <stdio.h>

void Scaletrain(int * nums, int x, int* returnSize){
    
    
	int cnt = 0;
	while (x){
    
    
		nums[cnt++] = x % 2;
		x /= 2;
	}
	*returnSize = cnt;
}

int main(){
    
    
	int arr[100];
	int x = 25;
	int Size;
	Scaletrain(&arr, x, &Size);
	for (int i = Size - 1; i >= 0; i--){
    
    
		printf("%d", arr[i]);
	}
	putchar('\n');
	return 0;
}

通过十进制转化为二进制我们可以扩展到其他的高进制转化为低进制,不断地对低进制n进行取模和辗转相除分解得到。

三. 课后习题

3.1 二进制中1的个数

剑指 Offer 15. 二进制中1的个数
在这里插入图片描述
在这里插入图片描述
分析:
  将数字n进行拆分,并用一个变量进行统计1的个数,如果拆分的到的数为1,则++。

int hammingWeight(uint32_t n) {
    
    
    int ret = 0;
    while(n){
    
    
        int k = n % 2;
        n /= 2;
        if(k == 1){
    
    
            ret++;
        }
    }
    return ret;
}

在这里插入图片描述

3.2 各位相加

258. 各位相加
在这里插入图片描述
  这道题我们先讲解基本的循环解法,通过对十进行辗转相除和模运算,对其进行分解,并将分解的数进行相加,直到最后和小于两位数为止
代码如下:

int addDigits(int num){
    
    
    int ret = num;
    //当ret < 10时,得到结果
    while(ret > 9){
    
    
        int sum = 0;
        //更新num
        num = ret;
        while(num){
    
    
        	//拆分计算
            sum += num % 10;
            num /= 10;
        }
        ret = sum;
    }
    return ret;
}

在这里插入图片描述
方法二:
  我们可以根据一个公式:
在这里插入图片描述
但光知道这个公式还不够,我们还需回忆起这两个定理:

  1. 能够被9整除的整数,各位上的数字加起来也必然能被9整除。
  2. 不能被9整除的整数,各位上的数字加起来,结果对9取模,和初始数对9取摸,是一样的。

再考虑到一些特殊情况后,得到最后公式:(n - 1) % 9 + 1;
代码如下:

int addDigits(int num){
    
    
    return (num - 1) % 9 + 1;
}

在这里插入图片描述

3.3 二进制链表转整数

1290. 二进制链表转整数
在这里插入图片描述
在这里插入图片描述
根据二进制的转化公式,我们发现,需要知道在数字所在的位数,而链表中的数据时从高位开始的,如果要知道每个数字的位数,我们首先应该计算出整个数据的位数,但实际上我们一定要知道高位的位数吗?其实是不必要的。

  1. 我们每读取一个结点,可以认为读到的节点为当前的最高位。
  2. 当我们读取下一个节点时,可以先将前面的储存的节点乘2,再相加。
    在这里插入图片描述
    最后我们发现,其结果和公式完全一样。
    代码如下:
int getDecimalValue(struct ListNode* head){
    
    
    int ret = 0;
    while(head){
    
    
        ret = ret * 2 + head -> val;
        head = head -> next;
    }
    return ret;
}

在这里插入图片描述

3.4 K 进制表示下的各位数字总和

1837. K 进制表示下的各位数字总和
在这里插入图片描述
1 <= n <= 100
2 <= k <= 10
这道题很简单,具体就不怎么分析了,多少进制就模多少,除多少嘛。
直接上代码:

int sumBase(int n, int k){
    
    
    int ret = 0;
    while(n){
    
    
        ret += n % k;
        n /= k;  
    }
    return ret;
}

在这里插入图片描述

3.5 统计最大组的数目

1399. 统计最大组的数目
在这里插入图片描述
在这里插入图片描述
分析:
  这道题是让我们将数字都拆分位个位,然后并把拆分后的数加起来,把计算出的数的结果相同的分为一组,找出成员最多的组有多少个。
思路:

  1. 我们可以利用哈希表的方法,通过数组下标来对应拆分后的值,假设拆分后的值位i,则hash[i]++,数组中存储的就是拆分计算后的的值i的个数。
  2. 然后我们找出数量最多的那一组,并计算这样的组数有多少。

代码如下:

int countLargestGroup(int n){
    
    
    int arr[100] = {
    
     0 };
    for(int i = 1; i <= n; i++){
    
    
        int m = 0, k = i;
        while(k){
    
    
            m += k % 10;
            k /= 10;
        }
        arr[m]++;
    }
    int max = 0;
    int num = 0;
    for(int i = 1; i < 100; i++){
    
    
        if(arr[i] > max){
    
    
        	//说明找到了更大的相同组数
        	//对max更新
            max = arr[i];
            //num从新计数
            num = 1;
        }
        else if(arr[i] == max){
    
    
            num++;
        }
    }
    return num;
}

在这里插入图片描述

Guess you like

Origin blog.csdn.net/qq_53060585/article/details/121203419