《剑指offer》:[40]数组中只出现一次的数字

题目:一个整型数组里除了两个数字外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度为O(N),空间复杂度为O(1)。

例如输入数组{2,4,3,6,3,2,5,5},因为只有4和6在这个数组里出现了一次,所以最后输出的是4,6。

分析:因为题目要求的时间复杂度和空间复杂度分别为:O(N)和O(1)。所以这个题目不能借助辅助空间,那么也就是要在一次遍历后就能找出只出现一次的数据还是有一定的难度的。我在第一次看这个题目的时候也没想到什么好的办法。最后看了书上的提示才想到这个解决方案。

首先我们考虑只有一个数字出现的情况,这时候我们该怎么做呢?也就是其他的数字都是成对的出现,而唯独只有一个数字是单独出现。注意这句话非常重要。成对出现的数字有什么特点吗?这里有点难想到,就是这个切入点很难想到,那就是异或运算。相同的数字经过异或运算后,就变为了0。然后如果一个数字和0异或运算就是它本身。说到这儿,应该就明白了吧!我们将数组里的每个数字进行异或运算,如果只有一个数字最后出现过一次,那么经过异或完以后,最后剩下的那个不为0的数字就是我们要找的那个数字。因为成对出现的都消失了,剩下的就是孤苦无依的那个单的,呵呵!

看完了只有一个数字出现一次的情况,我们再来看看有两个数字出现的情况。想想成对的经过异或运算都化为了0.那么剩下了两个数据怎么样了呢?一定是不为0,那么我们可以寻找这两个数字的不同,哪里不同?异或运算不为0就是不同啊,所以我们将数字同过异或完最后结果的倒数第一位不是0的二进制位来将数组分为两组,这样的话就把我们需要找的两个数字分到了两个数组里,并且在这个两个子数组里,除了一个数字出现一次外,其他的都是成对的出现,这下好了,问题转化为我们讨论的第一种情况,那么困难也就迎刃而解了!

具体实现代码:

#include <iostream>
using namespace std;
int arr[8]={2,4,3,6,3,2,5,5};
//判断二进制位是否是1;
bool IsBit1(int num,unsigned int indexBit)
{
	num=num>>indexBit;
	return num&1;
}
//找到数组进行异或操作后第一个是1的二进制位。
unsigned int FindFirstBitIs1(int num)
{
	int indexBit=0;
	while((num & 1)==0 && (indexBit<8*sizeof(int)))
	{
		num=num>>1;
		++indexBit;
	}
	return indexBit;
}
//找到出现一次的的数子
void FindNumAppearOnce(int *arr,int length,int *num1,int *num2)
{
	if(arr==NULL || length<2)
		return ;
	int resultExclusiveOR=0;
	for(int i=0;i<length;++i)
		resultExclusiveOR^=arr[i];
	unsigned int indexof1=FindFirstBitIs1(resultExclusiveOR);
	*num1=*num2=0;
	for(int j=0;j<length;++j)
	{
		if(IsBit1(arr[j],indexof1))
			*num1^=arr[j];
		else
			*num2^=arr[j];
	}
}
int main()
{
	int num1,num2;
	FindNumAppearOnce(arr,8,&num1,&num2);
	cout<<"只出现一次的两个数为:"<<num1<<" "<<num2<<endl;
	system("pause");
	return 0;
}

运行结果:

时间: 2024-10-24 11:20:40

《剑指offer》:[40]数组中只出现一次的数字的相关文章

[剑指Offer]40.数组中只出现一次的数字

题目 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 思路 我们直到异或的性质: 任何一个数字异或他自己都等于0. 所以说我们如果从头到尾依次异或每一个数字,那么最终的结果刚好只出现一次的数字,因为成对出现的两次的数字全部在异或中抵消了. 这道题中有两个数字只出现一次.这样的话我们得到的结果就是这两个数字的异或结果.因此我们想办法把原数组分成两个子数组,使得每个子数组包含一个只出现一次的数字.这样我们就可以对这两个子数组分别异或,就能得到两个只出现一

【剑指offer】数组中只出现一次的数字(1)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27649027 题目描述: 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 输入: 每个测试案例包括两行: 第一行包含一个整数n,表示数组大小.2<=n <= 10^6. 第二行包含n个整数,表示数组元素,元素均为int. 输出: 对应每个测试案例,输出数组中只出现一次的两个数.输出的数字从小到大的顺序. 样例输入: 8 2 4 3 6 3

【剑指offer】数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. /*考虑过程: 首先我们考虑这个问题的一个简单版本:一个数组里除了一个数字之外,其他的数字都出现了两次.请写程序找出这个只出现一次的数字. 这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:任何一个数字异或它自己都等于0 .也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中

【剑指offer】数组中仅仅出现一次的数字(1)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27649027 题目描写叙述: 一个整型数组里除了两个数字之外,其它的数字都出现了两次.请敲代码找出这两个仅仅出现一次的数字. 输入: 每一个測试案例包括两行: 第一行包括一个整数n,表示数组大小.2<=n <= 10^6. 第二行包括n个整数,表示数组元素,元素均为int. 输出: 相应每一个測试案例.输出数组中仅仅出现一次的两个数.输出的数字从小到大的顺序. 例子输入: 8 2 4

【剑指offer】数组中只出现一次的数

题目描述 一个整型数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字. 分析: 经典的异或技巧题 两个相同的数字异或的结果为0,一个数和0异或的结果是其本身,假设现在那两个不同的数字是A和B,那么将整个数组的元素依次异或得到的结果ans就是A和B的异或结果 ans的二进制中第k位的1代表A的第k位和B的第k位不同,我们现在按照第k位是否相同将原数组分成两个子数组,那么必定A和B分别分散在两个子数组中,然后将子数组依次异或,得到的结果就是A和B的值! 至于k的取值则

剑指offer (36) 数组中的逆序对

题目:在数组中的两个数字如果前面一个数字大于后面一个数字,则这两个数字组成一个逆序对 题解分析: 首先应该想到很简单的一种解法,顺序遍历数组,对每个数,逐个比较该数字和其以后的数字,T(n) = O(n^2) (1)总体的意思就是将数组分成两段,首先求段内的逆序对数量,比如下面两段代码就是求左右两端数组段内的逆序对数量 count += Merge(data, temp, first, mid);//找左半段的逆序对数目 count += Merge(data, temp, mid + 1, e

【剑指offer】数组中的逆序对

# @left part: [start, mid] # @right part: (mid, end] def merge(data, start, mid, end): if mid < start or end < mid: return 0 reverse = 0 ''' @ for start, it play as the start index of left part, and mid @ play as the end index of left part; @ mid +

剑指OFFER之数组中出现次数超过一半的数字(九度OJ1370)

题目描述: 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 输入: 每个测试案例包括2行: 第一行输入一个整数n(1<=n<=100000),表示数组中元素的个数. 第二行输入n个整数,表示数组中的每个元素,这n个整数的范围是[1,1000000000]. 输出: 对应每个测试案例,输出出现的次数超过数组长度的一半的数,如果没有输出-1. 样例输入:

剑指offer (29) 数组中出现次数超过一半或1/3或1/N的数字

题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字 方法一:如果把这个数字排序,那么排序之后位于数组中间的数字一定就是出现次数超过数组长度一半的数字 这个数字就是统计学中的中位数,即长度为n的数组中第n/2大的数字 在数组中得到任意第k大数字,这一问题有O(n)解,注:这里第kth个元素,kth从1开始计数,并且重复元素不去重 (1) 直接sort排序,然后定位到索引为kth-1的元素 int FindKth1(std::vector<int>& num, int kt

【Java】 剑指offer(39) 数组中出现次数超过一半的数字

本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, 4, 2}.由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. 思路 思路一:数字次数超过一半,则说明:排序之后数组中间的数字一定就是所求的数字. 利用partition()函数获得某一随机数字,其余数字按大小排在该数字的左右.若该