剑指offer--数组中出现一次的数字

题目描述

一个整型数组里除了两个数字之外,其他的数字都出现了偶数次。请写程序找出这两个只出现一次的数字

  之前遇到过类似的题,不过当时的题目是整型数组中除了一个数字外,其他都出现了偶数次,找出那个只出现一次的,那个就很简单了,这时候就能想到我们的异或运算符,异或是一个位运算符,他会对比两个数字的二进制数,相同位置符号相同为0不同为1。所以如果我们一个数字中除了某一个数字外,其他的数字都出现了两次,相同的数字异或的时候他们的每一位都是相同的所以结果为0,两个两个抵消掉,整个数组不停的去异或得到的就是我们想要的那个出现一次的数字

public static int find1From2(int[] a){

int len = a.length, res = 0;

for (int i = 0; i < len; i++){

res = res ^ a[i];

}

return res;

}

但是这次不同,这次我们是有两个数字是出现一次的,所以如果你再去异或的话得到的结果就是两个数字异或的结果。这里先不说如何简单的去求解,先说一下暴力求解,这里我用了两个for循环来一个数字一个数字的对比。

class Solution {

public:

void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {

int i = 0;

int j = 0;

int flag = 0;

int flag1 = 0;

int length = data.size();

for (i = 0; i < length; i++)

{

for (j = 0; j < length; j++)

{

flag = 0;

if (j == i)

{

continue;

}

if (data[i] == data[j])

{

flag = 1;

break;

}

}

if (flag == 0)

{

if (flag1 == 0)

{

*num1 = data[i];

flag1 = 1;

}

else

{

*num2 = data[i];

}

}

}

return ;



}

};

这里我定义了两个标志符号,一个是来标记这个数字在数组里边有没有相同的数字,另一个来标记得到的这个出现一次的数字是给了num1还是给num2,这个程序应该不难理解。

下边我们说一下通过异或来解决的方法:

  我们上边也说了如果是一个数组里边除了一个数字其他都出现了偶数个那我们解决起来是很简单的,但是我们现在有两个单独的数字,这里我们就像能不能有一种方法去把我们的这整个数组给分开,把这两个数字放到不同的数组里边,并且除去他们两个之外相同的数字也要在同一个数组里边,这不就把我们现在这个问题转换成了刚刚我们所说的我们能够实现的问题。

 但是这里要怎么分才是难点,不能说像我刚刚一样O(n2)去对比这样都能求出来了就没必要分开了。

class Solution {

public:

void FindNumsAppearOnce(vector<int> data, int* num1, int *num2) {

int len = data.size();

int ret = 0;

int count = 0;

int i = 0;

for (i = 0; i < len; i++)

{

ret = ret^data[i];

}

for (count = 0; count < 32; count++)

{

if ((ret & 1))

{

break;

}

else

ret = ret >> 1;



}

*num1 = 0;

*num2 = 0;

for (i = 0; i < len; i++)

{



if (((data[i]>>count)&1))

{

*num1 ^= data[i];

}

else

{

*num2 ^= data[i];

}

}



}

};

修改了很久的代码,总是有各种小错误。最后也不知道咋莫名其妙的通过的测试。这里我们简单说一下,我们先让数组中所有数据进行异或,这样得到的结果就是我们那两个数字的异或,既然是异或那就是如果异或得到的数字中某一位是1那就说明这两个数字对应的位置的数是不同的,既然是不同的那我们就能把他俩区分开,至于哪个是1哪个是0根本不重要我重要的是把他们两个区分开。
 

for (count = 0; count < 32; count++)

{

if ((ret & 1))

{

break;

}

else

ret = ret >> 1;



}

这里我们就是在找这两个数字里边哪一位不同,随便找一位就可以啊,所以我们开始让他右移然后和1进行与运算如果运算结果是1那就说明当前这个位置是1,,找到后退出循环,也就是说这两个不同的数字的第count位是不同的。

for (i = 0; i < len; i++)

{



if (((data[i]>>count)&1))

{

*num1 ^= data[i];

}

else

{

*num2 ^= data[i];

}

}

然后把数组中第count位是1的在一组和num1异或,count是0的在另一组和num2异或,就可以了,哪有人有疑问,其他的数据是怎么分开的,那我还是这个标准,你相同的数组在count位肯定都是0或者都是1,自然肯定就在一组了,至于那边数据多那边数据少不影响啊,肯能num2这里只有一个那个单独的数据,但是不影响结果。

 

今天给大家分享一个简单的选择题

如有定义:char str[20];,能将从键盘输入的字符串“How are you”保存到 str 数组的语句是(   )

A.scanf("%s", str);

B.str[] = getch();

C.gets(str);

D.str = gets();

正确答案是C。这里要注意的是scanf这个函数,这个函数确实可以实现A的用法,但是他并不能正确存储我们的字符串是因为,scanf是不能保存空格这个符号的,当时gets可以,并且还有tab符,回车。但是这两个函数也有一个共同点,就是当接受完字符串后会自动在字符串后边加入’\0’

这里我特意进行了一次测试,以前还真没发现scanf的这个特性。

gets从标准输入设备读字符串函数,其可以无限读取,不会判断上限,以回车结束读取,所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。对于返回值读入成功,返回与参数buffer相同的指针;读入过程中遇到EOF(End-of-File)或发生错误,返回NULL指针。

scanf("%s",s);读入时,遇到空白字符,包括空格,制表符,换行符时均会停止输入

并且两个还有一个区别就是终止后,对终止字符处理不同。
比如输入为"test\nabcd"。
执行gets后,\n不会留在缓冲区中,即这时调用getchar得到的字符是'a'。
执行scanf后,\n会留在缓冲区,这时调用getchar得到的字符是'\n'。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值