整型数组处理算法(十)给定数组a[n],其中有超过一半的数为一个定值,找出这个数。[2014人人网笔试题]

本文介绍了一种高效查找数组中出现次数超过一半的定值的方法,提供了两种不同的算法实现及其复杂度分析,并通过测试代码验证了算法的有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原链接是:

人人网2013笔试算法题汇总

2.给定有n个数的数组a,其中有超过一半的数为一个定值,在不进行排序,不开设额外数组的情况下,以最高效的算法找出这个数。

int find(int* a, int n);

这道题的算法还可以优化一下,那就是对于这种情况:3,3,3,3,3,3,4,4,4,4。因为个数是10个,当统计的个数大于一半,那就可以中止判断了。实现如下:

int find(int *a, int n)    
{    
    int t = a[0];    
    int count = 0;    
    for (int i=0; i<n; ++i)    
    {    
        if (count == 0)    
        {    
            t = a[i];    
            count = 1;    
            continue;    
        }    
        else    
        {    
            if (a[i] == t)    
            {    
                count++;    
            }    
            else    
            {    
                count--;    
            }   
			
			//增加一个判断
			if (count > n/2)
			{
				return t;
			}
        }    
    }    
    
    return t;    
} 

这样的话,

Time Complexity: O(n/2)

Space Complexity:O(1)


另外一个实现方法:

int find1(int *a, int n) 
{
	int nTotal=0;
	int i;
	int j;
	int nMax=a[0];//最大值
	int nAvg;//平均值
	int nCount;

	for (i=0; i< n; i++)
	{
		nTotal += a[i];

		if (a[i]>nMax)
		{
			nMax = a[i];
		}
	}
	
	nAvg = nTotal/n;

	for (j=nAvg; j<=nMax; j++)
	{
		nCount = 0;
		for (i=0; i< n; i++)
		{
			if (a[i] == j)
			{
				nCount++;
			}

			if (nCount > n/2)
			{
				return j;
			}
		}

	}

	return 0;
}

测试代码:

int main()    
{    
    int n = 11;    
    //int a[10] = {1, 3, 2, 3, 3, 4, 3, 3, 3, 6};    
	//int a[10] = {4, 3, 4, 3, 4, 3, 4, 3,3,3}; 
	int a[11] = {3, 3, 3, 4, 4, 4, 4, 4,3,3,3}; 

	//int a[10] = {3, 10, 10, 10, 10, 10, 10, 3, 3, 3}; 
    
    cout<<find1(a, n)<<endl;   
	
	cout<<find(a, n)<<endl;
    
    system("pause");    
    return 0;    
} 


测试结果就不贴了,有兴趣的朋友试试看。


转载请注明原创链接:http://blog.youkuaiyun.com/wujunokay/article/details/12227851




<think>好的,我现在需要解决用户的问题:如何用C语言编写程序来计算最长平衡子数组的长度。首先,我需要明确什么是“最长平衡子数组”。平衡子数组通常指的是包相同量的0和1的子数组。不过用户没有给出具体定义,所以可能需要进一步确认。根据常见的定义,平衡子数组是0和1量相等的连续子数组。比如数组[0,1,1,0]中,整个数组就是平衡的,因为0和1各有2个。 接下来,我需要回忆相关的算法。最直接的思路是暴力法,遍历所有可能的子数组,统计其中0和1的量,然后记录最长的满足条件的长度。但这种方法的时间复杂度是O(),对于较长的数组来说效率不高。用户可能希望更高效的算法,比如O(n)或O(n)时间复杂度的方法。 想到可以使用前缀和与哈希表的方法。这种方法将0视为-1,然后计算前缀和。当两个前缀和相同时,说明这两个位置之间的子数组的和为0,即0和1的量相等。例如,前缀和数组为s,若s[j] = s[i],那么j到i-1的子数组满足条件。这种方法的时间复杂度是O(n),空间复杂度也是O(n),但需要处理哈希表的实现。 不过,C语言中实现哈希表可能比较麻烦,因为没有内置的据结构。因此可能需要简化,或者寻找替代方法。比如,可以用一个数组来记录每个前缀和第一次出现的位置。因为前缀和的范围可能在-1到n之间(假设数组长度为n),所以需要适当处理的情况。 举个例子,假设输入数组是[0,1,0],对应的处理后的数组为[-1,1,-1]。前缀和依次是0(初始值)、-1, 0, -1。当计算到索引2时,前缀和为-1,而之前在索引0处的前缀和也是-1,所以子数组长度是2-0=2。但正确的平衡子数组应该是从0到1(长度2)或者整个数组(0到2,总共有两个0和一个1,量不等,所以不是)。这说明需要仔细处理转换和前缀和的计算。 另外,需要注意初始条件。哈希表中需要存储前缀和为0时的索引-1,这样当第一次出现前缀和0时,可以正确计算长度。例如,如果前缀和为0出现在索引i,则长度为i - (-1) = i+1。 现在,我需要将这些思路转化为C语言代码。首先,将数组中的0转换为-1,其他元素保持为1。然后计算前缀和,使用一个哈希结构来记录每个前缀和第一次出现的索引。由于C语言没有哈希表,可以用一个数组来模拟,因为前缀和的范围可能从-n到 n,所以需要将负索引转换为正。例如,可以用一个数组hash,长度为2n+1,其中hash[s + n] = index,这样s的范围是-1到n时,可以存储。 接下来,遍历数组,计算当前的前缀和,并检查是否已经存在于哈希表中。如果存在,则计算当前索引与第一次出现位置的差作为长度,并更新最大值。如果不存在,则将该前缀和及其索引存入哈希表中。 具体步骤: 1. 初始化一个数组作为哈希表,初始值为-1,表示未被访问过。 2. 设置初始前缀和为0,索引为-1。 3. 遍历输入数组的每个元素: a. 将当前元素转换为delta(0变为-1,1变为1)。 b. 更新前缀和sum += delta。 c. 如果sum已经存在于哈希表中,则计算当前索引与哈希表中存储的索引的差,更新最大长度。 d. 否则,将sum的当前索引存入哈希表。 4. 最终返回最大长度。 需要注意的是,哈希表的大小。假设数组的最大长度为n,那么前缀和的范围是[-n, n],所以需要2n + 1的空间。例如,当n=1e5时,哈希表可能需要较大的内存,但实际应用中可能需要处理这种情况。 现在,编写代码的大致结构: - 函:输入数组arr,数组长度n。 - 初始化哈希表,大小为2n +1,初始化为-1。 - sum初始化为0,max_len初始化为0。 - 遍历数组处理每个元素: - delta = (arr[i] == 0) ? -1 : 1; - sum += delta; - 如果哈希表中sum对应的位置已经存在,则计算i - hash[sum + n],如果大于max_len则更新。 - 否则,将hash[sum +n]设为i。 这里可能存在一个问题:哈希表中的sum对应的索引是否为第一次出现的?是的,因为一旦记录后,后续再次出现同样的sum时,计算的长度会更长,所以只需要记录第一次出现的位置即可。 比如,数组[0,1,0,0,1],转换后为[-1,1,-1,-1,1]。前缀和依次为0(初始),-1,0,-1,-2,-1。对应的哈希表记录: sum=0 → index=-1; i=0时sum=-1,hash[-1 +n] =0; i=1时sum=0,已经存在,计算1 - (-1)=2 →max_len=2; i=2时sum=-1,此时存在hash[-1 +n]=0,所以长度是2-0=2,不更新max_len; i=3时sum=-2,记录; i=4时sum=-1,此时长度是4-0=4,所以max_len=4; 最终正确的最长子数组是前四个元素[0,1,0,0]?不,因为0和1的是否相等? 转换后的数组是[-1,1,-1,-1,1],前缀和到i=4时为-1。那么从索引0到4,sum的变化是初始0到-1(i=0),到0(i=1),到-1(i=2),到-2(i=3),到-1(i=4)。当i=4时sum=-1,第一次出现的位置是i=0,所以子数组是0到3?或者i=4与i=0之间的长度是4 -0 =4?子数组是原数组的0到3,即四个元素:0,1,0,0。其中0的量是3,1的量是1,显然不平衡。这说明哪里出错了? 哦,这说明我的转换有问题。因为正确的平衡子数组需要0和1的量相等。原来的数组是[0,1,0,0,1],那么每个0转换为-1,1转换为1,总和为-1+1-1-1+1 = -1。所以前缀和的方式可能有问题? 或者可能我的思路有问题。例如,前缀和为0的位置之间的子数组总和为0,即-1和1的量相等。所以,当sum[j] = sum[i],那么j+1到i的子数组总和为0,即0和1的量相等。例如,初始sum=0,当i=1时sum=0,对应的子数组是0到0(索引0到1),即原数组的[0,1],0和1各一个,平衡。此时长度是2。 当i=4时sum=-1,对应的子数组是0到4?或者从索引0到4的sum变化是-1,所以总和为-1,说明0比1多1个。这显然不符合平衡的条件。这说明我的方法可能存在错误。 这说明我的转换或者思路有问题。需要重新审视。 正确的转换应该是:将0视为-1,1视为1,这样总和为0时,0和1的量相等。因此,当两个位置i和j的前缀和相等,那么i+1到j的子数组总和为0,即0和1的量相等。因此,正确的处理应该是,计算前缀和,当sum[j] = sum[i]时,子数组i+1到j的长度为j -i。 所以,在之前的例子中,数组[0,1,0,0,1]转换后的数组为[-1,1,-1,-1,1]。前缀和如下: 初始sum=0; i=0:sum +=-1 → sum=-1; i=1:sum +=1 → sum=0; i=2:sum +=-1 → sum=-1; i=3:sum +=-1 → sum=-2; i=4:sum +=1 → sum=-1. 当i=1时,sum=0,此时与初始的sum=0的位置(-1)之间的距离是1 - (-1)=2,即子数组0到1,正确。 当i=4时sum=-1,第一次出现的位置是i=0,此时子数组长度是4-0=4。对应的原数组是索引0到3(因为子数组是i+1到j?或者这里可能存在误解。假设sum[j] = sum[i],那么子数组是i+1到j。例如,当sum[j] = sum[i],那么子数组i+1到j的和为0。因此,当i=0的sum=-1,j=4的sum=-1,那么子数组是1到4,即原数组的1,0,0,1,其中0的量是2,1的量是2,确实是平衡的。那这个子数组的长度是4,即正确。 所以,这说明之前的思路是正确的。那在之前的例子中,最大的长度是4,对应的子数组是索引1到4,即元素1,0,0,1,0和1各两个,符合条件。 因此,算法的思路是正确的。那在C语言中如何实现呢? 接下来,考虑如何处理哈希表的索引。例如,假设数组最大长度是n,那么前缀和的范围可能从 -n到 +n。因此,哈希表需要能够存储从sum=-n到 sum=+n的每个可能的sum对应的首次出现的索引。因此,可以将sum的值转换为非负,例如,将sum加上n,这样范围变成0到2n。因此,哈希表的大小为2n +1。例如,当n=5,sum的范围是-5到5,加上5变成0到10,需要11个位置。 在C语言中,可以用一个整型数组来作为哈希表,初始化为-1。每个位置存储对应sum首次出现的索引。例如,sum= -1时,哈希表的位置是-1 +n =n-1。如果n是数组的最大可能长度,比如题目中可能的输入是很大的,比如1e5,那么哈希表的大小会是2e5+1,这在内存上是可行的,因为对于1e5来说,哈希表大小是200001,每个int占4字节,大约800KB,这在大多情况下是可以接受的。 现在,编写代码的步骤: 函定义:int findMaxLength(int* nums, int numsSize); 但用户要求的是用C语言实现,所以可能需要完整的代码示例。 例如,输入数组为0,1,0,0,1,输出应为4,对应子数组1到4。 代码的大致步骤: 1. 检查输入数组是否为空或长度为0,返回0。 2. 初始化哈希表:大小为2 * numsSize +1,初始化为-1。 3. 计算max_len=0,sum=0。 4. 哈希表的位置sum + numsSize = 0 + numsSize → 初始化为sum=0对应的索引是-1。 5. 遍历数组中的每个元素: a. 如果当前元素是0,delta=-1;否则delta=1。 b. sum += delta。 c. 计算当前sum对应的哈希表位置:sum + numsSize。 d. 检查哈希表中该位置是否有记录: - 如果有,则计算当前索引i - hash[sum_pos],比较max_len。 - 如果没有,则将hash[sum_pos] =i。 6. 返回max_len。 例如,具体代码: #include <stdio.h> #include <stdlib.h> int findMaxLength(int* nums, int numsSize) { if (numsSize <= 1) return 0; int max_len = 0; int sum = 0; int* hash = (int*)malloc((2 * numsSize + 1) * sizeof(int)); for (int i = 0; i < 2 * numsSize + 1; i++) { hash[i] = -2; // 初始化为-2,表示未被访问过 } hash[numsSize] = -1; // sum=0对应的索引是-1,因为初始sum=0 for (int i = 0; i < numsSize; i++) { sum += (nums[i] == 0) ? -1 : 1; int sum_pos = sum + numsSize; if (hash[sum_pos] != -2) { int current_len = i - hash[sum_pos]; if (current_len > max_len) { max_len = current_len; } } else { hash[sum_pos] = i; } } free(hash); return max_len; } int main() { int arr[] = {0, 1, 0, 0, 1}; int n = sizeof(arr) / sizeof(arr[0]); printf("最长平衡子数组长度: %d\n", findMaxLength(arr, n)); // 应输出4 return 0; } 但是这里可能存在一个问题,哈希表的大小是否正确?比如,当sum的范围是-numsSize到 numsSize,所以sum + numsSize的范围是0到 2*numsSize。因此,哈希表的大小应为2*numsSize +1。例如,当numsSize=5时,sum的范围是-5到5,sum_pos的范围是0到10,共11个位置,即2*5+1=11。所以代码中的哈希表分配正确。 另外,初始化hash数组时,需要将每个位置设为-2,表示未被访问过。而sum=0对应的索引是-1,所以hash[numsSize] = -1。这可能会有问题,因为当sum=0时,sum_pos是0 + numsSize =numsSize的位置。 在循环中,如果sum_pos对应的hash值不是-2,说明之前已经出现过这个sum,此时可以计算长度。否则,将当前i存入hash[sum_pos]。 测试这个代码是否正确。例如,输入数组为[0,1],则sum变化为-1,0。在i=1时sum=0,sum_pos=0 +2=2(假设numsSize=2)。hash[2]初始化为-1(因为sum=0对应的初始索引是-1)。所以current_len=1 - (-1)=2,max_len=2。正确。 另一个测试案例:输入数组为[0,1,0],转换后的sum依次为-1,0,-1。在i=0时sum=-1,sum_pos=-1+3=2。hash[2]初始为-2,所以存入i=0。i=1时sum=0,sum_pos=3,hash[3]初始为-1,current_len=1-(-1)=2,max_len=2。i=2时sum=-1,sum_pos=2,此时hash[2]=0,current_len=2-0=2,max_len还是2。所以最终输出2,正确。 而原来的测试案例,数组是[0,1,0,0,1],numsSize=5,哈希表大小是11。sum的变化: i=0: sum=-1 → sum_pos=5-1=4? 不,sum=-1 +5=4?是的。所以hash[4]设为0。 i=1: sum=0 → sum_pos=5 → hash[5]初始为-1 → current_len=1 - (-1)=2 → max=2. i=2: sum=-1 → sum_pos=4 → hash[4]=0 → current_len=2-0=2 → max仍为2. i=3: sum=-2 → sum_pos=3 → hash[3]=-2 → 存入i=3. i=4: sum=-1 → sum_pos=4 → hash[4]=0 → current_len=4-0=4 → max=4. 最终返回4,正确。 这说明代码是正确的。但需要处理哈希表的初始化和释放,防止内存泄漏。 此外,需要考虑数组长度为0或1的情况,直接返回0,因为不可能有长度≥2的平衡子数组。 可能的优化:哈希表的大小可以设置为2n+1,但当n很大时可能占用较多内存。例如,当n=1e5时,哈希表需要2e5+1个int,这大约是800KB,这在现代计算机中是可行的。 综上所述,这个算法的时间复杂度是O(n),空间复杂度是O(n),适用于较大的输入规模。</think>### 如何用C语言计算最长平衡子数组长度 平衡子数组定义为包相同量0和1的连续子数组。实现方法采用**前缀和+哈希映射**优化算法,时间复杂度$O(n)$,空间复杂度$O(n)$。 #### 算法步骤 1. **变量转换**:将0视为-1,1视为1,平衡子数组总和为0 2. **前缀和映射**:用哈希表记录每个前缀和首次出现的索引 3. **差值计算**:当相同前缀和再次出现时,计算子数组长度 #### 代码实现 ```c #include <stdio.h> #include <stdlib.h> int findMaxLength(int* nums, int numsSize) { if (numsSize <= 1) return 0; int max_len = 0; int sum = 0; int* hash = (int*)malloc((2 * numsSize + 1) * sizeof(int)); // 初始化哈希表为-2(表示未访问) for (int i = 0; i < 2 * numsSize + 1; i++) hash[i] = -2; hash[numsSize] = -1; // 初始前缀和为0的位置是-1 for (int i = 0; i < numsSize; i++) { sum += (nums[i] == 0) ? -1 : 1; int sum_pos = sum + numsSize; if (hash[sum_pos] != -2) { // 当前前缀和已存在 int current_len = i - hash[sum_pos]; if (current_len > max_len) max_len = current_len; } else { // 记录首次出现位置 hash[sum_pos] = i; } } free(hash); return max_len; } int main() { int arr[] = {0, 1, 0, 0, 1}; int n = sizeof(arr) / sizeof(arr[0]); printf("最长平衡子数组长度: %d\n", findMaxLength(arr, n)); // 输出4 return 0; } ``` #### 关键点说明 1. **哈希表偏移**:前缀和范围$[-n, n]$通过`sum + numsSize`映射到非负索引[^1] 2. **初始状态**:哈希表记录`sum=0`对应虚拟索引-1,处理数组起始开始的子数组 3. **内存管理**:动态分配哈希表后需手动释放,避免内存泄漏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值