CI5.3-寻找特殊的最大、最小数

本文介绍了一种算法,用于找出一个整数n的二进制表示中,比n小的最大整数和比n大的最小整数,同时保证这三个数的二进制表示中1的数量相同。文章详细阐述了实现这一目标的具体步骤,并提供了完整的C++代码。

给定一个整数n,输出小于n的最大整数max和大于n的最小整数min,且n、max、min这三个数的二进制表示中1的个数相等。

思路:

最大整数max:从最低位开始找到第一个为0的位,记为bit0;从bit0位开始继续,找到第一个为1的位,记为bit1。将bit1置为0,然后将[0, bit1)区间的位清零,得到的整数记为temp。此时temp和n中的bit1到最高位的值相同,然后计算temp和n的二进制中1的个数差值count,最后将temp的(bit1, 0]区间的最高count位置为1,得到的结果就是max。

最小正数min:从最低位开始找到第一个为1的位,记为bit1;从bit1为开始继续,找到第一个为0的位,记为bit0,将bit0置为1,然后将[0, bit0)区间的位清零,得到的整数记为temp。此时temp和n中的bit0到最高位的值相同,然后计算temp和n的二进制中1的个数差值count,最后将temp的最低count位置为1,得到的结果就是min。

#include <iostream>
using namespace std;

int CountOne(int num)
{
	int res = 0;
	for (int i = 0; i < 32; ++i)
	{
		if ((num & 1) == 1)
			++res;
		num >>= 1;
	}
	return res;
}

int MinOfLarger(int num)
{
	int bit = 0;
	int temp = num;
	while ((temp & 1) == 0 && bit < 32)
	{
		++bit;
		temp >>= 1;
	}
	while ((temp & 1) == 1 && bit < 32)
	{
		++bit;
		temp >>= 1;
	}
	if (bit == 32)
		return -1;
	temp |= 1;
	temp <<= bit;
	int count = CountOne(num) - CountOne(temp);
	int mask = 1;
	for (int i = 0; i < count; ++i)
	{
		temp |= mask;
		mask <<= 1;
	}
	return temp;
}

int MaxOfSmaller(int num)
{
	int bit = 0;
	int temp = num;
	while ((temp & 1) == 1 && bit < 32)
	{
		++bit;
		temp >>= 1;
	}
	while ((temp & 1) == 0 && bit < 32)
	{
		++bit;
		temp >>= 1;
	}
	if (bit == 32)
		return -1;
	temp &= (~1);
	temp <<= bit;
	int count = CountOne(num) - CountOne(temp);
	for (int i = 1; i <= count; ++i)
	{
		int mask = (1 << (bit - i));
		temp |= mask;
	}
	return temp;
}
int main()
{
	int num;
	while (cin >> num)
	{
		cout << MinOfLarger(num) << endl;
		cout << MaxOfSmaller(num) << endl;
	}
	return 0;
}


<think>我们想要找出数组中的小数min1和次小数min2。注意,次小数min2应该是大于等于min1且小于其他所有数的数(即第二小的数)。 我们可以通过一次遍历数组来实现,同时记录小值和次小值。 思路: 1. 初始化min1和min2,可以将数组的前两个元素中较小的赋给min1,较大的赋给min2(但要注意数组可能只有一个元素,不过题目没有说明,我们假设至少有2个元素)。 2. 遍历数组(从第三个元素开始),对于每个元素: a. 如果当前元素比min1还小,那么更新min2为原来的min1,然后更新min1为当前元素。 b. 否则,如果当前元素比min2小(但大于等于min1),那么更新min2为当前元素。 c. 还有一种情况:当前元素等于min1,那么它可能比min2小(因为min2可能大于min1),所以不需要更新min2?但是注意,如果min1和min2相等,那么当前元素等于min1时,我们不需要更新min2,因为min2还是等于min1。但是我们的条件中,如果当前元素比min2小且大于min1,才会更新min2。所以,当当前元素等于min1时,我们不需要更新min2,因为min2应该大于等于min1,而当前元素并没有比min2小(除非min2等于min1,那么我们需要更新吗?) 实际上,我们允许min1和min2相等,因为数组可能有重复的小值。 但是,我们需要注意初始化的正确性。我们可以这样: min1 = (前两个数中较小者) min2 = (前两个数中较大者) 然后从第三个元素开始遍历。 另一种做法是初始化min1和min2为较大的数,然后遍历整个数组。但题目要求找出两个小值,所以数组元素至少为2个。 我们假设数组至少有2个元素。 具体步骤: 1. 比较前两个元素,设置min1和min2。 2. 从第三个元素开始遍历(下标2开始): if (a[i] < min1) { min2 = min1; // 原来的小值变成次小值 min1 = a[i]; // 更新小值 } else if (a[i] < min2) { // 这里a[i]不小于min1,但是小于min2,那么更新min2 min2 = a[i]; } // 注意:还有一种情况,当a[i]等于min1时,我们不需要更新min2?因为min2应该大于等于min1,如果min2等于min1,那么说明我们已经有两个小值了,那么当前这个等于min1的数不会改变次小值(因为次小值还是min1,即有两个min1,那么min2也是min1)。但是,如果min2大于min1,那么当前这个等于min1的数不会比min2小,所以不会更新min2。所以上面的条件可以覆盖。 但是,有一个问题:如果数组一开始min1和min2相等,然后后面遇到一个比min1大的数,那么min2不会更新,直到遇到一个介于min1和min2之间的数?实际上,我们初始化的时候,如果前两个数相等,那么min1和min2都等于这个数。然后,当我们遇到一个比min1大的数,我们不会更新min2,直到遇到一个比min2小的数(但必须大于min1)才会更新。但是,如果后面一直没有比min2小的数(除了比min1小的数),那么min2还是等于min1。这样是正确的,因为次小值就是min1(重复的小值)。 所以,这个逻辑可以处理重复值。 但是,还有一个问题:在遍历过程中,如果一开始min2设置得过大,比如数组前两个数分别是3,5,然后后面遇到4,那么4应该更新min2(因为4小于5)。上面的逻辑中,4>min1(3)且4<min2(5),所以会更新min2为4。 因此,代码实现如下: 注意:题目没有给出数组,我们假设数组名为a,长度为n(n>=2)。 下面我们写一个完整的程序示例,但题目要求是补充代码,所以我们需要根据题目要求来。 题目要求:补充Ccon0210.C,找出数组中小数min1和次小数min2。 假设程序框架如下: #include <stdio.h> int main() { float a[] = {5.3, 2.1, 7.4, 1.5, 3.3, 1.5, 4.2}; // 示例数组 float min1, min2; int i, n = sizeof(a)/sizeof(a[0]); // 计算数组长度 // 初始化min1和min2 if (a[0] <= a[1]) { min1 = a[0]; min2 = a[1]; } else { min1 = a[1]; min2 = a[0]; } // 从第三个元素开始遍历 for (i=2; i<n; i++) { if (a[i] < min1) { min2 = min1; // 原来的小值变成次小值 min1 = a[i]; // 更新小值 } else if (a[i] < min2) { min2 = a[i]; } } // 输出结果 printf("min1=%.2f, min2=%.2f\n", min1, min2); return 0; } 但是,上面的程序有一个问题:如果数组长度小于2怎么办?题目没有说,但我们假设至少有2个元素。 另外,上面的程序在遍历过程中,如果遇到一个比min1大但比min2小的数,就会更新min2。但是,如果数组中有重复的小值,比如数组{1,1,2,3},那么: 初始:min1=1, min2=1 然后遍历第三个元素2:2>min1,且2>min2(min2=1)? 2>1,所以不会更新min2。 第四个元素3同理。 所以min1=1, min2=1。这是正确的,因为小的两个数都是1。 但是,如果数组是{1,2,1,3},那么: 初始:min1=1, min2=2 第三个元素1:1<min1? 1等于min1,所以不满足第一个条件,然后判断1<min2(2)吗?满足,所以min2被更新为1。这样,min1=1, min2=1。正确。 所以,这个逻辑可以处理重复值。 但是,还有一种情况:如果数组一开始是{3,2},那么初始化min1=2, min2=3。然后从第三个元素开始(没有第三个元素),所以结果正确。 因此,我们可以这样实现。 但是,我们也可以将min1和min2初始化为一个很大的数,然后从第一个元素开始遍历。不过题目没有要求,我们按照上述方法。 现在,根据题目要求,我们补充代码。题目没有给出完整的程序,我们假设程序框架已经给出,我们只需要补充min1和min2的查找部分。 但是,题目中给出的程序名是Ccon0210.C,我们假设它已经包含必要的头文件和数组定义。 下面,我们按照题目要求补充代码,假设数组已经定义,长度为n(n>=2),我们需要找出min1和min2。 注意:题目没有给出数组长度,但我们可以通过计算得到,或者题目已经给出长度。这里我们假设数组名为a,长度已知为n。 由于题目没有给出具体代码框架,我们按照通常情况写。 但是,如果题目要求只能遍历一次数组,且不能使用排序,那么我们的方法就是一次遍历。 因此,我们补充的代码为: 初始化min1和min2(使用前两个元素): if (a[0] < a[1]) { min1 = a[0]; min2 = a[1]; } else { min1 = a[1]; min2 = a[0]; } for (int i=2; i<n; i++) { if (a[i] < min1) { min2 = min1; min1 = a[i]; } else if (a[i] < min2) { min2 = a[i]; } } 但是,如果数组长度可能为1?题目没有说,我们假设至少2个元素。 如果数组长度可能为1,那么我们需要判断,但题目没有要求,我们就按至少2个。 后,输出min1和min2。 因此,完整的程序示例(Ccon0210.C)如下:</think>以下是正确实现找出数组中小数`min1`和次小数`min2`的C语言代码,采用单次遍历高效算法: ```c #include <stdio.h> int main() { float a[] = {5.3, 2.1, 7.4, 1.5, 3.3, 1.5, 4.2}; // 示例数组 int n = sizeof(a) / sizeof(a[0]); // 数组长度 float min1, min2; // 小值和次小值 // 初始化小值和次小值 if (a[0] < a[1]) { min1 = a[0]; min2 = a[1]; } else { min1 = a[1]; min2 = a[0]; } // 遍历数组找出小值和次小值 for (int i = 2; i < n; i++) { if (a[i] < min1) { min2 = min1; // 原小值降为次小值 min1 = a[i]; // 更新小值 } else if (a[i] < min2 && a[i] != min1) { min2 = a[i]; // 更新次小值 } } printf("小值 min1 = %.2f\n", min1); printf("次小值 min2 = %.2f\n", min2); return 0; } ``` ### 算法说明: 1. **初始化处理**: ```c if (a[0] < a[1]) { min1 = a[0]; min2 = a[1]; } else { min1 = a[1]; min2 = a[0]; } ``` 将数组前两个元素中较小者设为`min1`,较大者设为`min2`[^1] 2. **单次遍历核心逻辑**: ```c for (int i = 2; i < n; i++) { if (a[i] < min1) { min2 = min1; // 原min1降级为min2 min1 = a[i]; // 更新小值 } else if (a[i] < min2 && a[i] != min1) { min2 = a[i]; // 更新次小值(排除重复小值) } } ``` - 当遇到比`min1`小的值时,原`min1`降级为`min2` - 当遇到介于`min1`和`min2`之间的值时,更新`min2` - 条件`a[i] != min1`确保不重复记录相同的小值[^2] 3. **时间复杂度**: - 仅需单次遍历数组,时间复杂度为$O(n)$ - 空间复杂度$O(1)$,仅使用固定数量变量 ### 示例输出: ``` 小值 min1 = 1.50 次小值 min2 = 2.10 ``` ### 关键点说明: 1. **重复值处理**:`a[i] != min1`条件确保当数组包含多个相同小值时(如示例中的1.5),次小值不会被错误覆盖 2. **边界安全**:算法正确处理数组长度≥2的情况,对于空数组或单元素数组可添加额外检查 3. **效率优化**:相比先排序再取值的方案,此算法避免$O(n\log n)$排序开销
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值