【C语言.oj刷题】找出整形数组中出现一次的元素##{思路+C源码}

目录

概览 

题目1

 题目2

解题

题目2

思路一:

局限性: 

思路二:

题目1

思路一:

 局限性:

 思路二:

扩展


概览 

题目1


 一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

例如:

有数组的元素是:1,2,3,4,5,1,2,3,4,6

只有5和6只出现1次,要找出5和6.


 题目2


 一个数组中只有一个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

例如:

有数组的元素是:1,2,3,4,5,1,2,3,4,

只有5出现1次,要找出5


 在解决题目1之前,我们先解决题目2作为预热,更有利于解决题目1和后续的拓展。

解题

题目2

思路一:

        循环遍历,拿数组中的某一个元素遍历数组,直到找到与它相同的一个元素为止;

        这个元素不能是他自己。

        输出没有配对的元素。

实现源码:

​

int main()
{
	int arr[] = {1,2,3,4,5,1,2,3,4};
	int sz = sizeof(arr)/sizeof(arr[0]);
	for(int i = 0;i < sz;i++)//遍历数组,用arr[i]去找arr[j],每一个arr[i] 都要遍历一边数组
	{
		int f = 0;
		for(int j = 0;j < sz;j++)
		{
			if((i != j) && arr[i] == arr[j])//找到的arr[i]不能是arr[i]本身,所以限制i != j
			{
				f = 1;
				break;
			}
		}
		if(f == 0)
		{
			printf("%d ",arr[i]);
		}
	}
	return 0;
}

​
局限性: 

        思路一方法易想简单,但是代码量较大;

        经计算,遍历算法时间复杂度是O(N^2)

思路二:

 参考异或运算的特性:

相同为0,不同为1,即
        1 ^ 1 = 0
        0 ^ 0 = 0
        1 ^ 0 = 1

我们可以推知异或的一个重要特性->自反性: A ^ B ^ B = A 

由此,我们可以创建初始化为0的整型变量,记为(int tem = 0),让tem 异或数组中的每一个元素,得到的结果就是只有一个的元素ret;

实现源码:


int main()
{
	int arr[] = {1,2,3,4,5,1,2,3,4};
	int sz = sizeof(arr)/sizeof(arr[0]);
	int ret = 0;
	for(int i = 0;i < sz;i++)
	{
		ret ^= arr[i];
	}
	printf("%d",ret);
	return 0;
}

思路二代码量有了很大的降低,经计算,时间复杂度为O(N),有了很大的提效。

题目1

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

我们发现:题目一的要求更多了,有两个只出现一次的数字,怎样解决呢?

思路一:

        循环遍历,拿数组中的某一个元素遍历数组,直到找到与它相同的一个元素为止;

        这个元素不能是他自己;

        输出没有配对的元素。

实现源码:


int main()
{
	int arr[] = {1,2,3,4,5,1,2,3,4,6};
	int sz = sizeof(arr)/sizeof(arr[0]);
	for(int i = 0;i < sz;i++)
	{
		int f = 0;
		for(int j = 0;j < sz;j++)
		{
			if((i != j) && arr[i] == arr[j])
			{
				f = 1;
				break;
			}
		}
		if(f == 0)
		{
			printf("%d ",arr[i]);
		}
	}
	return 0;
}

是不是和题目二的源码一样?为什么会这样?由于题目二并不会将所有的元素都异或在一个变量上,而是一个一个的去比对遍历,符合要求的单独输出,所以元素便于分离。

换个角度->如果我们还用异或的方法去把所有元素结合,得到的结果就是两个单独出现的元素的异或值了,难以分离。

 局限性:

对于遍历方法,有自己的优势,但是经过计算,遍历算法的时间复杂度是O(N^2),有待提速。

 思路二:

异或法不好分离,我们可以将两个单独出现一次的数字分在两个不同的数组中,再分别异或操作,这样不就好了?

(1) 先通过一次异或操作,重复元素会被抵消,最终结果相当于两个单次出现的元素的异或值;
2) 由异或规则可知,,若两个元素的异或值的某二进制位为1,则表示两个元素在该二进制位上的值不同(一个为0,另一个为1),找到其中一个满足条件(为1)的二进制位。

(3) 根据(2)找到的二进制位,可以将原数组分成两个部分,两个单独出现的元素分别在两个部分,而相同的重复元素也会被分到同一个部分(因为其相应的二进制位的值是相同的);

(4) 对于两个部分再次进行异或操作,即相当于 排除偶次重复 问题,最终可以得到两个单独出现的元素。

补充:

        (一般情况下,两个非零元素的异或值 != 0,那么这个异或值二进制位一定有1)

          我们要找的二进制位可以是最右侧的二进制位,我们可以通过  ret &((~ret) + 1)的方式实现

          假设N取  001010110000

运算 二进制结果
N 001010110000
~N 110101001111
(~N)+1 110101010000
N&((~N)+1) 000000010000

实现源码:

​

#include
int search(int arr[],int sz)//此函数用于求得数组中的所有元素的异或
{
	int result = 0;
	for(int i = 0;i < sz;i++)
	{
		result ^= arr[i];
	}
	return result;//其实就是5^6^0=5^6
}
int main1()
{
	int arr[] = {1,2,3,4,5,1,2,3,4,6};
	int sz = sizeof(arr)/sizeof(arr[0]);
	int ret = search(arr,sz);
	
	int bit_ret = ret &((~ret) + 1);//操作:取得二进制最右侧的1
	
	int arr1[50] = {0};//存储
    int arr2[50] = {0};
	int c1 = 0;//计数
	int c2 = 0;
	for(int i = 0;i < sz;i++)
	{
		
		if((arr[i] & bit_ret) != 0)
		{
			arr1[c1] = arr[i];//不同
			c1++;
		}
		else
		{
			arr2[c2] = arr[i];//相同
			c2++;
		}
	}
	printf("%d\n",search(arr1,c1));
	printf("%d\n",search(arr2,c2));
	return 0;
}

​

 时间复杂的仍然为O(N),效率仍然较高。


扩展

        用遍历算法可以处理任意个单个元素的情况,但是需要牺牲一些时间。

        异或算法是一种高效的算法,但是需要多次分组,用到递归。


完~


未经作者同意禁止转载

你可能感兴趣的:(决胜oj,c语言,算法,开发语言)