【算法总结-数组相关】 数组中找特定元素相关~
2015-08-11 16:42
489 查看
本次总结在数组中查找特定的元素。
碰到的面试题目主要包括:
题目的意思很明显,1001个数字中只有一个数字出现了两次,其余的都是一次。并且不缺少任何数字。那么找到这个数字,我们可以有很多思路:
思路a:求和,最简单也最容易想出来的方法 1+2+3+4+。。。+1000的和与数组的和求差,差的结果就是这个数字,缺点是,求和的话,结果可能会溢出。不妨看思路2
思路b:异或,位操作总有让你惊喜的地方,原理是:
@ 如果两个相同的数求异或,那么结果为0。
@ 0与一个数异或的结果为这个数
@ a^b ^a = b ;a^b^b = a;
据此,可以对数组的元素与1,2,34,56,7,8...1000依次异或,最后的结果就是出现两次的数字。
相应的代码如下:
[cpp] view
plaincopyprint?
#include <stdio.h>
#include <stdlib.h>
int findTheNum(int *a){
int k = a[0];
for(int i=1;i<=1000;i++){
k ^= (a[i]^i);
}
return k;
}
main(){
int a[1001];
for(int i = 0;i<=999;i++ ){
a[i] = i+1;
}
a[1000] = rand()%1000;
printf("the num : %d %d \n",a[1000],findTheNum(a));
return 0;
}
@onlyonename
假设这两个数为a,b,将数组中所有元素异或结果x=a^b,判断x中位为1的位数(注:因为a!=b,所以x!=0,我们只需知道某一个位为1的位数k,例如0010 1100,我们可取k=2或者3,或者5),然后将x与数组中第k位为1的数进行异或,异或结果就是a,b中一个,然后用x异或,就可以求出另外一个。
据此,不难写出代码:
[cpp] view
plaincopyprint?
#include <stdio.h>
void getNum(int a[],int len,int &one,int &two){
int res = 0;
for(int i = 0;i<len;i++){
res = res^a[i];
}
int temp = res;
int k = 0;
while(!(temp&1)){
temp = temp>>1;
k++;
}
temp = 0;
for(int i = 0;i < len;i++){
if((a[i]>>k)&1){
temp = temp^a[i];
}
}
one = temp ;
two = temp^res;
}
int main(){
int a[] = {2,3,4,4,5,6,6,5};
int one ,two;
getNum(a,8,one,two);
printf("%d --- %d \n",one,two);
return 0;
}
这题可以充分看到位运算的强大。
以该题为例,算法的扫描过程如下图所示:
类似这种找“第一次出现一次的数字(字符)”的题目有一个共同的特点:数字或字符的范围是确定的。因此我们可以考虑计数。具体思路是:首先扫描一次原数组,记录每个数字的出现次数,然后再一次扫描原数组,并查询其出现的次数,一旦遇到出现次数为1的数字,那么就返回这个数字即可。相应的代码如下:
[cpp] view
plaincopyprint?
#include <stdio.h>
#include <string.h>
#define N 1000
int getFirstOne(int *a,int n){
int b[N+1];
memset(b,0,N+1);
for(int i = 0;i < n;i++){
b[a[i]]++;
}
for(int i = 0;i < n;i++){
if(b[a[i]] == 1){
return a[i];
}
}
}
int main(){
int a[] = {1,2,1,2,3,4,5,4,5};
printf("%d \n",getFirstOne(a,9));
}
这个题目有一种算法叫做蓄水池抽样,思路大致是这样的,先选中前m个, 从第m+1个元素到最后一个元素为止, 以m/i (i=m+1, m+2,...,N) 的概率选中第i个元素, 并且随机替换掉一个原先选中的元素, 这样遍历一次得到m个元素, 可以保证完全随机选取。
相应的代码如下:(这里只说明原理,注意rand()函数并不是真正意义上的随机数,具体可见:http://wenku.baidu.com/view/9bee261eb7360b4c2e3f6418.html)
[cpp] view
plaincopyprint?
#include <stdio.h>
#include <stdlib.h>
#define N 10000
#define M 20
int getRandM(int *a,int n,int *result,int m){
int i,j;
for(i = 0;i < m;i++){
result[i] = a[i];
}
for(j = i; j < n; j++){
int randN = rand()%j;
if(randN >= 0 && randN < m){
int randChange = rand()%m;
result[randChange] = a[j];
}
}
}
int main(){
int inStream
;
for( int i = 0;i < N;i++){
inStream[i] = i;
}
int outStream[M];
getRandM(inStream,N,outStream,M);
for(int i = 0;i<M;i++){
printf("%d ",outStream[i]);
}
printf("\n");
}
另:在有序数组中查找符合条件的两个数字的解法见:双指针法的应用: http://blog.csdn.net/ohmygirl/article/details/7850068
在数组中找k个数的和为m的算法。
其他有关数组中选取特定元素的题目待补充。
碰到的面试题目主要包括:
1.假如你有一个用1001个整数组成的数组,这些整数是任意排列的,但是你知道所有的整数都在1到1000之间(包括1000)、此外,除了一个数字出现两次外,其他的数字只出现了一次。假设你对数组做一次处理,用一种算法找出重复的那个数字,要求不使用大量额外的存储空间(即要求辅助空间为O(1))
题目的意思很明显,1001个数字中只有一个数字出现了两次,其余的都是一次。并且不缺少任何数字。那么找到这个数字,我们可以有很多思路:思路a:求和,最简单也最容易想出来的方法 1+2+3+4+。。。+1000的和与数组的和求差,差的结果就是这个数字,缺点是,求和的话,结果可能会溢出。不妨看思路2
思路b:异或,位操作总有让你惊喜的地方,原理是:
@ 如果两个相同的数求异或,那么结果为0。
@ 0与一个数异或的结果为这个数
@ a^b ^a = b ;a^b^b = a;
据此,可以对数组的元素与1,2,34,56,7,8...1000依次异或,最后的结果就是出现两次的数字。
相应的代码如下:
[cpp] view
plaincopyprint?
#include <stdio.h>
#include <stdlib.h>
int findTheNum(int *a){
int k = a[0];
for(int i=1;i<=1000;i++){
k ^= (a[i]^i);
}
return k;
}
main(){
int a[1001];
for(int i = 0;i<=999;i++ ){
a[i] = i+1;
}
a[1000] = rand()%1000;
printf("the num : %d %d \n",a[1000],findTheNum(a));
return 0;
}
2.腾讯的一道面试题:在一堆数字中,有两个数字出现了奇数次,其余的数字出现了偶数次,设计一种算法找出这两个数字。要求时间复杂度为O(n),空间复杂度为O(1)
@onlyonename假设这两个数为a,b,将数组中所有元素异或结果x=a^b,判断x中位为1的位数(注:因为a!=b,所以x!=0,我们只需知道某一个位为1的位数k,例如0010 1100,我们可取k=2或者3,或者5),然后将x与数组中第k位为1的数进行异或,异或结果就是a,b中一个,然后用x异或,就可以求出另外一个。
据此,不难写出代码:
[cpp] view
plaincopyprint?
#include <stdio.h>
void getNum(int a[],int len,int &one,int &two){
int res = 0;
for(int i = 0;i<len;i++){
res = res^a[i];
}
int temp = res;
int k = 0;
while(!(temp&1)){
temp = temp>>1;
k++;
}
temp = 0;
for(int i = 0;i < len;i++){
if((a[i]>>k)&1){
temp = temp^a[i];
}
}
one = temp ;
two = temp^res;
}
int main(){
int a[] = {2,3,4,4,5,6,6,5};
int one ,two;
getNum(a,8,one,two);
printf("%d --- %d \n",one,two);
return 0;
}
这题可以充分看到位运算的强大。
以该题为例,算法的扫描过程如下图所示:
3。数组中有多个数字只出现了一次,且这些数字都在1000之间,用一种算法找出第一次只出现一次的数字,要求线性时间。(类似的有,在一个字符串中找到第一个只出现一次的字符)
类似这种找“第一次出现一次的数字(字符)”的题目有一个共同的特点:数字或字符的范围是确定的。因此我们可以考虑计数。具体思路是:首先扫描一次原数组,记录每个数字的出现次数,然后再一次扫描原数组,并查询其出现的次数,一旦遇到出现次数为1的数字,那么就返回这个数字即可。相应的代码如下:[cpp] view
plaincopyprint?
#include <stdio.h>
#include <string.h>
#define N 1000
int getFirstOne(int *a,int n){
int b[N+1];
memset(b,0,N+1);
for(int i = 0;i < n;i++){
b[a[i]]++;
}
for(int i = 0;i < n;i++){
if(b[a[i]] == 1){
return a[i];
}
}
}
int main(){
int a[] = {1,2,1,2,3,4,5,4,5};
printf("%d \n",getFirstOne(a,9));
}
4.一个输入流,很大,大到没有存储器可以将其存储下来,从这个输入流中随机取出m个元素,要求各个元素被取出的概率相同。输入流只能扫描一次。
这个题目有一种算法叫做蓄水池抽样,思路大致是这样的,先选中前m个, 从第m+1个元素到最后一个元素为止, 以m/i (i=m+1, m+2,...,N) 的概率选中第i个元素, 并且随机替换掉一个原先选中的元素, 这样遍历一次得到m个元素, 可以保证完全随机选取。相应的代码如下:(这里只说明原理,注意rand()函数并不是真正意义上的随机数,具体可见:http://wenku.baidu.com/view/9bee261eb7360b4c2e3f6418.html)
[cpp] view
plaincopyprint?
#include <stdio.h>
#include <stdlib.h>
#define N 10000
#define M 20
int getRandM(int *a,int n,int *result,int m){
int i,j;
for(i = 0;i < m;i++){
result[i] = a[i];
}
for(j = i; j < n; j++){
int randN = rand()%j;
if(randN >= 0 && randN < m){
int randChange = rand()%m;
result[randChange] = a[j];
}
}
}
int main(){
int inStream
;
for( int i = 0;i < N;i++){
inStream[i] = i;
}
int outStream[M];
getRandM(inStream,N,outStream,M);
for(int i = 0;i<M;i++){
printf("%d ",outStream[i]);
}
printf("\n");
}
另:在有序数组中查找符合条件的两个数字的解法见:双指针法的应用: http://blog.csdn.net/ohmygirl/article/details/7850068
在数组中找k个数的和为m的算法。
其他有关数组中选取特定元素的题目待补充。
相关文章推荐
- 【算法总结--数组相关】双指针法的常见应用。
- 对卷积的生动理解
- 注解 @Resource
- Jenkins + GitHub + fir-cli 一行命令从源码到 fir.im
- 跳跳 (BFS) CUS 1259
- 怎么查看你的Win7或者Win8.1的授权方式
- JavaScript小笔记の经典算法等....
- bootstrap插件小记
- js基础
- MyEclipse 2015优化技巧
- LeetCode:Maximus Square(DP)
- mvn命令
- UIWindow
- IIS7 允许下载MDB文件 设置修改办法
- UIApplication
- spark implementation hadoop setup,cleanup
- 代理,通知,KVO的区别
- CC攻击 PV PaaS相关概念
- spring注解( @Autowired、@Qualifier、@Resource、@PostConstruct、@PreDestroy、 @Component、@Scope)-描述的比较清楚
- VMvare虚拟机下,使用ubuntukylin-14.04安装gcc、g++、OpenCV 2.4,编译OpenCV测试程序