按输入序列长度为M,按其数据二进制中 ‘1’ 的个数多少,返回前N多的元素

本文介绍了一种基于二进制位‘1’计数的排序算法,该算法可以找出输入序列中二进制表示包含最多‘1’的前N个元素,并在数量相同时选择数值较大的元素。通过两种不同的计数方法实现,提供了完整的Python代码示例。
# usr/bin/env python
# _*_ coding:utf-8 _*_
'''
按输入序列长度为M,按其数据二进制中 ‘1’ 的个数多少,返回前N多的元素
( 若元素含有的 ‘1’ 个数相同则返回数值较大的那个 )

如:
5
2
5 3 7 1 2
返回:
7 5
'''

def getResult(M, N, array):

	new_array = []
	bin_array = [bin(i) for i in array]         # 二进制转换

	# # 获取数组中对应位置二进制中‘1’的个数     方法一
	# for i in range(M):
	# 	new_array.append(bin_array[i].count('1'))

	# 获取数组中对应位置二进制中‘1’的个数       方法二
	for i in range(M):
		temp = array[i]
		num = 0
		while temp:
			temp &= (temp-1)
			num += 1
		new_array.append(num)

	# 获取二进制中‘1’个数前N多的元素
	Res = []
	for j in range(N):
		# 获取当前最多
		res =  array[new_array.index(max(new_array))]
		# 删除最多
		new_array.remove(max(new_array))
		# 删除最多
		array.remove(res)
		# 保存结果
		Res.append(res)

	return Res

if __name__ == '__main__':
	M = input()
	N = input()
	array = [int(i) for i in raw_input().split()]

	print getResult(M, N, array)

<think>我们有一个任务:用C语言实现将输入的由0和1二进制数转换为八进制数输出。 思路: 1. 二进制转换为八进制通常采用“三位一组”的方法。因为3位二进制数恰好可以表示0~7(即八进制的一位)。 2. 由于输入的是二进制字符串,我们需要从右向左每三位分一组(不足三位的左边补0),然后将每组的三位二进制数转换为一个八进制数字。 3. 注意:输入可能是一个很长的二进制字符串,所以我们要考虑如何处理字符串。 步骤: a. 读取一个二进制字符串(由0和1)。 b. 计算字符串长度,并确定需要补0的个数(使得总长度是3的倍数)。 c. 从右向左(即从低位到高位)每三位一组进行转换。 d. 将每组的三位二进制数转换为一个八进制数字(0-7)。 e. 将转换后的八进制数字按顺序存储(注意顺序,因为是从低位开始转换,所以存储的是低位八进制位,输出时需要反转)。 f. 输出八进制数(从高位到低位)。 注意:可能会输入整数部分,不包括小数部分(根据问题描述,输入是由0和1二进制数,没有提到小数点,所以先不考虑小数部分)。 示例: 二进制数:11011101 分组(从右往左):011(补0后)、101110(注意:实际上我们分组后,最左边一组可能是1位或2位,需要补0至3位) 转换: 011 -> 3 101 -> 5 110 -> 6 所以八进制数为:365 但是注意,我们分组是从右向左,所以转换后的序列是3(第一组)、5(第二组)、6(第三组),但是输出应该是从左到右(即先转换的最左边一组,实际上是原二进制的高位),所以输出应该是653?不对,应该注意分组后的顺序。 实际上,我们分组后,最左边的一组是二进制的高位,转换后也是八进制的高位。但是我们在转换时,是从右往左分组的,所以: 原始二进制11011101 长度为8,我们补0:补一个0在左边变011011101?不对,实际上我们通常从右向左每三位分,所以: 从右边开始:101(最后三位),然后011面三位,但是这里只有两位,所以需要在面补0变011),再面还剩11,补0变011?不对,实际上原字符串为:11011101长度为8,我们可以补0至9位(3的倍数)?或者从右边开始分组: 第1组:101(最后三位) -> 5 第2组:110(接下来的三位) -> 6 第3组:011(最面的两位,面补0变011) -> 3 所以八进制数应该是365?但是顺序上,我们是从高位到低位输出,所以先输出3,再输出6,再输出5,即365。 但是,在转换过程中,我们分组是从右向左,然后转换后的数字也是从右向左(即先转换的是低位,后转换的是高位)。所以我们需要将转换后的八进制数字反转,或者从后往存储。 另一种做法:先补齐长度(在字符串面补0,使总长度为3的倍数),然后从左到右每三位一组,这样转换后的顺序就是八进制数的顺序。但是注意,分组是从左到右,但是每三位二进制数的权重还是从左到右递减(即第一组是最高位)。 实际上,我们通常采用从右向左分组,然后转换结果也是从低位到高位,最后需要反转。 具体步骤: 1. 输入二进制字符串,假设为 char bin[100]; 2. 计算长度 len = strlen(bin); 3. 计算需要补0的个数:numZeros = (3 - len % 3) % 3; // 这样如果len%3为0,则补0个,否则补3-len%3个 4. 在字符串面补numZeros个'0'(注意:需要重新分配空间或者移动字符串,或者我们可以从后向处理,不需要实际补0,而是用逻辑处理) 5. 为了简便,我们可以先计算分组数:group = (len + numZeros) / 3; 或者另一种思路:不需要实际补0,而是在处理时判断分组时是否够三位。 这里我们选择实际补0(在二进制字符串面补0),这样便于后续分组处理。 6. 补0操作:我们可以创建一个新的字符串,长度为len+numZeros+1(包括结束符),然后面numZeros个位置设为'0',后面复制原字符串。 7. 然后从新字符串的起始位置开始,每三个字符取一组,将这三个字符(二进制数字符)转换为一个整数(0-7),然后输出这个整数(作为八进制的一位)。 注意:分组是从左到右,每三位一组,这样第一组就是最高位,所以输出的顺序就是八进制数的顺序。 例如:二进制字符串原为"11011101",长度为8,需要补1个0,则新字符串为"011011101"。 然后分组: 第一组:"011" -> 0*4+1*2+1*1 = 3 第二组:"011" -> 0*4+1*2+1*1? 不对,这里应该是三组:011, 011, 101? 不对,应该是三个字符一组:三个"011"、中间三个"011"、后面三个"101"? 不对,新字符串长度是9,分组为:011, 011, 101? 但是原字符串是011011101,实际上应该是011(第0-2位)、011(第3-5位)、101(第6-8位)?不对,应该是: index0: '0', index1: '1', index2: '1' -> 第一组 index3: '0', index4: '1', index5: '1' -> 第二组 index6: '1', index7: '0', index8: '1' -> 第三组 显然不对,因为原字符串是011011101,我们期望的分组是:0(补的)和原字符串的两位(11)组第一组?不对,我们补0是补在左边(即高位),所以分组应该是: 第一组:三位(下标0-2):011 -> 3 第二组:中间三位(下标3-5):011 -> 3 第三组:后三位(下标6-8):101 -> 5 因此输出为335?但实际应该是365(因为原二进制11011101应该是:1 1011 101,分组补0后变011(3) 011(3) 101(5)?不对,我们重新计算原二进制数的值: 二进制110111011*2^7 + 1*2^6 + 0*2^5 + 1*2^4 + 1*2^3 + 1*2^2 + 0*2^1 + 1*2^0 = 128+64+0+16+8+4+0+1=221 八进制365:3*8^2 + 6*8 + 5 = 3*64+48+5=192+48+5=245 -> 不对,说明分组有误。 正确的分组应该是: 从右向左(低位到高位): 最低三位:101 -> 5 次低三位:111 -> 7 最高两位:11 -> 需要补0变011 -> 3 所以八进制应该是:375?再计算:3*64+7*8+5=192+56+5=253,还是不等于221。 重新计算二进制11011101128+64+0+16+8+4+0+1 = 128+64=192, 192+16=208, 208+8=216, 216+4=220, 220+1=221. 221的八进制:221除以8,商27余5;27除以8,商3余3;3除以8,商0余3。所以八进制为335(从高位到低位:3 3 5)? 221 ÷ 8: 8×27=216,余5(个位) 27 ÷8: 3×8=24,余3(十位) 3 ÷8: 0余3(百位) 所以八进制为335。 那么为什么分组会出错? 正确的分组(从右向左分组): 原二进制11011101,从右向左: 第一组(最低三位):101 -> 5 第二组:111 -> 7 第三组:11 -> 面补0变011 -> 3 -> 八进制数:335(从高位3到低位5) 所以分组是对的,但是我们在补0后,新字符串是0011011101吗?不对,因为我们只补一个0,并且是在整个字符串的左边补0,那么新字符串应该是:0 11011101 -> 011011101(9位),然后从左向右每三位一组: 011 -> 3 011 -> 3 101 -> 5 所以分组后是335,这样就和八进制数一致了。 结论:我们补0后,从左向右每三位一组,转换得到的八进制数就是正确的。 步骤总结: 1. 输入二进制字符串(不带符号,只包含0和1)。 2. 计算长度len,然后计算需要补0的个数:numZeros = (3 - len % 3) % 3; 3. 在字符串面(左边)补numZeros个'0',形新的二进制字符串。 4. 然后遍历新字符串,每三个字符一组,将这三个字符转换一个整数(八进制位)。 转换方法:对于字符串片段s[i]s[i+1]s[i+2](三个字符),计算:(s[i]-'0')*4 + (s[i+1]-'0')*2 + (s[i+2]-'0')*1 5. 将转换后的整数依次输出(这些整数连起来就是八进制数)。 注意:如果转换后的八进制数的第一位是0,那么我们应该省略这个0吗?通常,八进制数导0可以省略(除了0本身),但这里我们按实际转换输出,如果补0后第一组就是0,那么应该输出0吗? 例如:二进制数"1",补0后变"001",转换后为1(八进制),所以输出1。而二进制数"0",补0后变"000",转换后为0,应该输出0。但是如果我们有个分组,第一个分组转换后是0,那么输出时应该去掉导0吗? 但是注意,我们补0后,第一个分组可能为0,但实际上原二进制数高位可能有0,我们不应该省略(因为输入可能是010这样的,但通常输入不会有余的导0,但为了通用性,我们按照分组输出,包括导0)。 但是,如果我们输入的是空字符串,或者全0,那么转换后可能有个0,我们只输出一个0?或者按照分组输出,例如输入"000",补0后还是"000",分组后输出一个0?但是我们分组有三个0,转换后得到一个0,输出0。如果输入"000000",分组后两组,转换后得到00,输出00?这样就是八进制00,等于八进制0。但我们希望输出0(不带导0)。 所以,为了避免导0,我们可以从第一个非0的八进制位开始输出。但是注意,有可能整个数就是0,那么我们需要输出0。 因此,可以在转换过程中,用一个标志位(firstNonZero)来标记是否遇到了第一个非0的八进制位。在第一个非0出现之,如果转换的八进制位是0,则不输出(除非整个数都是0,则输出0)。 另一种方法:将转换后的八进制数字存储在一个数组中,然后去掉导0(第一个非0之的所有0),但如果全部是0,则保留最后一个0。 具体步骤: 我们可以先转换所有的分组,然后从第一个分组开始遍历,直到遇到非0分组,然后从那里开始输出。如果一直没有遇到非0,则输出0。 但是,我们也可以边转换边输出,用一个标志位来标记是否已经输出了第一个非0数字。如果没有输出过非0数字,那么遇到0不输出(但要注意后面可能有非0,所以不能直接跳过所有0,直到遇到非0才输出,并且一旦输出了非0,后面的0都要输出)。 然而,有一个特殊情况:如果整个数就是0,那么我们会跳过所有的0,最后什么都没输出。所以我们需要在最后判断,如果从来没有输出过任何数字,则输出0。 实现: 设置一个标志位:isFirst = 1; // 表示是否还没有输出第一个非0的八进制位 然后遍历每个分组: 转换分组得到八进制数字digit(0-7) 如果isFirst为1: 如果digit不为0,则输出digit,并将isFirst设为0; 否则(digit为0),则不输出(除非这个分组是最后一个分组,否则继续等待非0?但是注意,如果后面还有分组,可能整个数就是0开头的,比如0010,那么第一个分组0,第二个分组1,那么第一个0不能输出,第二个分组1输出,所以这里我们只跳过导0)。 如果isFirst为0,则输出digit(包括0也要输出)。 但是,如果所有分组都是0,那么isFirst始终为1,我们需要在遍历结束后判断:如果isFirst仍然为1,说明整个数都是0,那么我们输出一个0。 示例: 输入:000000 -> 分两组:000和000,转换后digit1=0,digit2=0。 处理第一个分组digit1=0:isFirst=1,跳过不输出。 处理第二个分组digit2=0:isFirst=1,跳过不输出。 循环结束,isFirst=1,则输出0。 输入:001010 -> 分组:0011)和010(2) 第一组:001 -> digit=1 -> 非0,输出1,isFirst=0。 第二组:010 -> digit=2 -> 输出2。结果为12。 输入:000010 -> 分组:000(0)和010(2) 第一组:0 -> 跳过(isFirst=1) 第二组:2 -> 输出2(isFirst设为0)。所以结果为2。 但是注意,输入000010(二进制)等于十进制的2,八进制也是2,所以正确。 但是,如果输入是000(只有一组): 分组:000 -> digit=0 因为isFirst=1,所以跳过不输出。 然后循环结束,isFirst=1,输出0。 所以合理。 另外,如果输入为空字符串?我们可以在程序开始判断,如果输入为空,则输出0?或者按照上述流程,len=0,补0个数=(3-0%3)%3=0,然后分组数为0,那么循环一次都不会执行,最后isFirst=1,输出0。这样也是合理的。 代码步骤: 1. 读入二进制字符串(注意可能包含空格?题目说输入由0和1,所以我们可以用scanf或gets,但要注意安全,这里用fgets或者scanf限定长度) 2. 去掉可能的换行符(如果使用fgets) 3. 计算长度,补0,创建新的字符串 4. 遍历新字符串,每三个字符一组,转换为整数 5. 输出处理(去掉导0) 但是注意:输入可能很大,我们假设最大100个字符。 代码示例: 注意:我们也可以不实际补0,而是通过计算分组时判断位置,但是为了清晰,我们实际补0。 下面写代码: </think>以下是用C语言实现将输入二进制数(由0和1)转换为八进制数输出的代码,并附有详细说明: ```c #include <stdio.h> #include <string.h> int main() { char binary[100]; // 存储二进制输入 printf("请输入二进制数:"); scanf("%s", binary); int len = strlen(binary); int numZeros = (3 - len % 3) % 3; // 计算需要补0的个数 char padded[300] = {0}; // 在左侧补0使长度为3的倍数 for (int i = 0; i < numZeros; i++) { padded[i] = '0'; } strcat(padded, binary); // 拼接原二进制字符串 int totalLen = len + numZeros; int firstNonZero = 0; // 标记是否遇到首个非0八进制位 printf("八进制数:"); // 每3位一组转换为八进制 for (int i = 0; i < totalLen; i += 3) { int digit = (padded[i] - '0') * 4 + (padded[i+1] - '0') * 2 + (padded[i+2] - '0'); // 跳过导0(除非是全0) if (digit != 0) firstNonZero = 1; if (firstNonZero || i + 3 >= totalLen) { printf("%d", digit); } } printf("\n"); return 0; } ``` ### 代码说明: 1. **输入处理** 读取二进制字符串(如"11011101"),最大支持100位。 2. **长度对齐** - 计算需补0个数:`numZeros = (3 - len % 3) % 3` - 例如:`len=5` → 需补1个0(`(3-5%3)%3 = (3-2)%3=1`)[^1] - 在左侧补0使总长度为3的倍数(如"101"补为"00101") 3. **三位一组转换** - 每组二进制按权展开:$d = b_0×4 + b_1×2 + b_2×1$ - 例如:`"101" → 1×4 + 0×2 + 1×1 = 5`(八进制)[^1] - 转换公式:`digit = (b0-'0')*4 + (b1-'0')*2 + (b2-'0')` 4. **导0处理** - 跳过开头的0(如`00101`输出`5`而非`005`)[^1] - 特殊处理全0情况(如`000`输出`0`) ### 示例运行: ```shell 请输入二进制数:11011101 八进制数:335 # 验证:11011101₂ = 335₈ (221₁₀) ``` ```shell 请输入二进制数:10101000 八进制数:250 # 验证:10101000₂ = 250₈ (168₁₀) ``` ### 关键点: - **补0规则**:始终在**数值左侧**补0(高位补0),确保分组正确[^1] - **分组顺序**:从左向右处理分组(高位组先转换),符合八进制读数顺序 - **效率**:时间复杂度 $O(n)$,适用于长二进制数 > 此实现利用了二进制与八进制间$2^3=8$的映射关系,通过三位分组直接转换,避免了中间十进制转换[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值