数组中数字出现的次数 -- C语言

需求

 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
    
 示例 1: 
 输入:nums = [4,1,4,6]
 输出:[1,6] 或 [6,1]

 示例 2: 
 输入:nums = [1,2,10,4,1,4,3,3]
 输出:[2,10] 或 [10,2]
  
 限制: 
 2 <= nums <= 10000

思路

从一堆数字里面找出两个出现一次的数,去掉其他出现两次的数。这种问题就用特征值的方式解:

  1. 遍历数组,用异或的方式去掉出现两次的数字,因为两次异或同一个数,等于没有异或
  2. 通过 featureCode & (-featureCode) 的方式,取得featureCode最右面的1,这个必定对应1个出现1次的数字seperator(相同为0,不同为1,这个位上只能有一个数字)
  3. 从数组与 seperator逐个相与,该位上为1的出现两次的都被刷掉了,就剩下了x
  4. 由于 featureCode 里面只有 x 和 y,那么 featureCode ^ x 就等于去掉了x,只剩下了y

代码实现


/*
 * 需求
 
 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
    
 示例 1: 
 输入:nums = [4,1,4,6]
 输出:[1,6] 或 [6,1]

 示例 2: 
 输入:nums = [1,2,10,4,1,4,3,3]
 输出:[2,10] 或 [10,2]
  
 
 限制: 
 2 <= nums <= 10000
     
 gcc singleNumbers-I.c -g -o a.exe -DDEBUG

 */
 
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#ifdef DEBUG
#define LOG(fmt, args...) fprintf(stdout, fmt, ##args)
#else
#define LOG(fmt,...)
#endif

#define TRUE        1
#define FALSE       0

#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) > (b) ? (b) : (a))



/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* singleNumbers(int* nums, int numsSize, int* returnSize){

	if(NULL == nums || 0 == numsSize){
		*returnSize = 0;
		return NULL;
	}

	int * retp = NULL;
	int featureCode = 0, seperator = 0, i = 0, x = 0, y = 0;

	retp = (int *) malloc (2 * sizeof(int));
	if(NULL == retp){
		*returnSize = 0;
		return NULL;		
	}
	memset(retp, 0, 2 * sizeof(int));
	*returnSize = 2;

	/*取得2个数得特征值,出现两次的数字通过^排除掉,所以特征值里面只有2个出现一次的数字*/
	for(i = 0; i < numsSize; i++){
		featureCode = featureCode ^ nums[i];
	}

	/*取得最右侧的1的特征值,只会含有一个数字(不同为1),作为1个的特征值*/
	seperator = featureCode & (-featureCode);

	/*提取第一个数字*/
	for(i = 0; i < numsSize; i++){
		/*出现2次的数字被^去掉了*/
		if(0 != (nums[i] & seperator)){
			x = x ^ nums[i];
		}
	}

	/*通过^,把X从featureCode里面去掉,这剩下y*/
	y = featureCode ^ x;

	retp[0] = x;
	retp[1] = y;
	

	return retp;
	
}


void testsingleNumbers(void){
	
	printf("\n************  testsingleNumbers ************ \n");
	int * retp = NULL;
	int returnSize = 0;

	int nums1[4] = {4,1,4,6};
	retp = singleNumbers(nums1, 4, &returnSize);
	printf("returnSize = %d, retp[0] = %d, retp[1] = %d\n", returnSize, retp[0], retp[1]);
	if(NULL !=  retp){
		free(retp);
		retp = NULL;
	}

	int nums2[8] = {1,2,10,4,1,4,3,3};
	retp = singleNumbers(nums2, 8, &returnSize);
	printf("returnSize = %d, retp[0] = %d, retp[1] = %d\n", returnSize, retp[0], retp[1]);
	if(NULL !=  retp){
		free(retp);
		retp = NULL;
	}
	
	return; 
 
 }


 int main(int argc, char ** argv){
	testsingleNumbers();
 }


编译

gcc singleNumbers-I.c -g -o a.exe -DDEBUG

调试输出

************  testsingleNumbers ************
returnSize = 2, retp[0] = 1, retp[1] = 6
returnSize = 2, retp[0] = 10, retp[1] = 2

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值