您的位置:首页 > 其它

leetcode 1 Two Sum

2015-08-08 10:48 351 查看
leetcode 是一个以经典面试题为题库的OJ,上面的第一题便是这道Two Sum, 题目很简单。

Two Sum

Total Accepted: 119107 Total Submissions: 673743

Given an array of integers, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

You may assume that each input would have exactly one solution.

Input: numbers={2, 7, 11, 15}, target=9

Output: index1=1, index2=2

由题目可知,我们需要写一个twoSum函数来完成这个功能,题目给出了这个函数的函数头,那我们就来实现这个函数。

int* twoSum(int* nums, int numsSize, int target)
{
}


我采用的是C代码来实现。

short hashTable[200000] = {0};


这道题是用hash表来做无疑是比较好的选择,因为这样的话时间复杂度是O(n),而如果直接使用bruteforce来搜索的话时间复杂度是O(n2)。如果给的数据量很大的话,需要很长时间才能得到结果。由于才接触leetcode,所以不清楚这上面是否有超时一说,但在一般的acm oj上都是有明确的时间限制的,所以直接bruteforce很容易超时。我这里使用了一个最简单的hash形式,最多覆盖20万个数,而题目的范围是整数范围,所以如果测试数据很大,例如100000000,那么这个hash表就肯定不适用了,然而根据测试表明这个hash表是能够通过测试的,所以leetcode oj上的测试数据值不会太大。之所以用short是因为考虑到输入的数据中可能有重复的情况,所以hashTable不仅要记录某个值是否出现同时还要记录其出现的次数。

int *indices = (int*)malloc(sizeof(int) * 2);

int leastElement = 0;

for (int i = 0; i < numsSize; i++)
{
if(nums[i] < leastElement)
leastElement = nums[i];
}


第一句是为返回的数组分配空间,这里需要注意的是必须使用malloc来动态分配空间,而不能直接定义一个数组然后返回。这两种方式的区别在于malloc分配的空间在堆上,而直接定义的数组是存储在栈上。当当前函数返回时,该函数中申明的局部变量都将被释放,所以数组空间被释放了。当我们再在该函数外部通过指针访问该数组空间时,得到的是错误的值。而堆空间则是由程序员来控制的,所以如果没有用free来释放空间那么数据一直存在。

后面的leastElement是用来存储所有输入数据中的最小值,因为我想要通过数据的值来作为hashTable中的索引号,例如:如果我们每读到一个5,那么我就把hashTable[5]的值加1。但这样就会出现一个问题,如果我们读到的是-5呢,hashTable[-5]会访问出错,所以我就考虑通过一个偏移量offset来解决这个问题。例如所有数据中最小的是-10,那么偏移量就是10,我把所有数据的值都加上10来作为索引值,那么最小的数据对应的索引号也就是0,那么所有数据对应的索引号也都是非负数,所以就可以避免这一问题,例如:-5对应hashTable[5], 5对应hashTable[15]。

int offset = 0 - leastElement;

for (int i = 0; i < numsSize; i++)
{
hashTable[nums[i] + offset]++;
}


这几句实现的就是求出偏移量,然后遍历所有数据,对于每一个数据,将它对应的hashTable中的值加1,所以当遍历完成后,hashTable中的每一项的值就是它所对应的数在整个数据中出现的次数。

for(int i = 0; i < numsSize; i++)
{
int temp = target - nums[i];

if(temp + offset < 0)
continue;


现在开始遍历数据找可以凑起来是target的数,temp表示要凑成target所需要的数。然后我们判断temp+offset的值,如果小于0,说明需要的值太小了,我们的所有数据中都没有,因为我们的最小值加offset等于0。

if(hashTable[temp + offset] )
{
if(temp == nums[i] && hashTable[temp + offset] == 1)
continue;


否则,我们来查看temp对应的hashTable的值如果大于0说明我们需要的数存在。但这有种特殊情况,例如输入数据是1,3,4,而target是6,当我们检测3时,temp等于3,由于我们数据中有3,所以hashTable[temp+offset]=1,但实际上我们只有一个3,并不能凑成6,所以为了排除这种情况,我们就增加了第二个if判断。

indices[0] = i + 1;

for(int j = i + 1; j < numsSize; j++)
{
if(nums[j] == temp)
{
indices[1] = j + 1;
break;
}
}
break;
}
}

return indices;


如果不是上面的那种情况,那么就是说我们需要的数的确存在,那么我们就需要把这个数在整个数据中对应的序号找出来。我们先把第一个数的序号村到indice[0]中,第二个数一定在第一个数的后面,因为如果在前面,遍历过程从前往后,当搜索前面那个数时,就会把后面这个数找出来,从而函数返回,根本就不可能继续搜索后面的数。所以第二个数我们从i+1开始找,另外注意当找到序号
b03e
后存入indice时都要加1,因为我们数据存储的索引是从0开始的算的,而题目要求从1开始算。

后面是程序的完整代码:

#include <iostream>
#include "stdlib.h"
using namespace std;

int* twoSum(int* nums, int numsSize, int target)
{
short hashTable[200000] = {0};

int *indices = (int*)malloc(sizeof(int) * 2); int leastElement = 0; for (int i = 0; i < numsSize; i++) { if(nums[i] < leastElement) leastElement = nums[i]; }

int offset = 0 - leastElement; for (int i = 0; i < numsSize; i++) { hashTable[nums[i] + offset]++; }

for(int i = 0; i < numsSize; i++) { int temp = target - nums[i]; if(temp + offset < 0) continue;

if(hashTable[temp + offset] ) { if(temp == nums[i] && hashTable[temp + offset] == 1) continue;

indices[0] = i + 1;

for(int j = i + 1; j < numsSize; j++)
{
if(nums[j] == temp)
{
indices[1] = j + 1;
break;
}
}
break;
}
}

return indices;
}
int main()
{
int numsSize;
cout<<"Input the size of input:"<<endl;
cin>>numsSize;

int *nums = (int*)malloc(sizeof(int) * numsSize);
int i = 0;
do
{
cin>>nums[i++];
}while(i!=numsSize);

int target;
cin>>target;

int *p = twoSum(nums,numsSize,target);
cout<<*p<<" "<<p[1]<<endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode