1. 两数之和
思路如下
假入给定数组里边最大数是19,而且都是正数,我们可以再写一个数组,弄一个如下的对应关系。不妨假设上边数组叫a数组,下边数组叫b数组。b存储数据的位置是a的值对应的位置,b的值是a的位置。例如,a[1]=7,所以,在b【7】中存1.
经过上方的对应关系,得出以下结果
如果我给b数组都在没有数值的地方都赋值为-1,
那么在b数组中,就可以很方便找到,a中的某个数m是否存在,如果存在,这个数在b数组相应位置的值,就是m的位置。例如,a中的11存在不,我只需要在b中看b【11】是否等于-1,如果等于-1,那就不存在,不等于就存在,而且11的位置在a中是2,即
a【2】==11。如果我要看,a中12存在不,就在b中看b【12】是不是等于-1,我们一看,b[12]=-1,所以a中不存在这个数。
接下来我们回到这个题上,对于这组测试用例,target=9。
我们就可以遍历a数组,从头到尾找那个可以配对的数,看对于,a【i】来说,我需要知道9-a[i]在a中存不存在。那么我们只需要看b【9-a[i]】是不是等于-1,如果不等于,那么这两个数的位置我们就找到了,一个是i,另一个是b【9-a[i]】。
在这个测试用例中,当我们遍历到a[0]时,我们需要知道9-a[0]=9-2=7,也就是7这个值是否存在,我们看b数组,发现b【7】不是-1,所以我们就找到了这两个值,一个是0,一个是b[7]的值,1。
接下来我么分析一下时间。加入a数组有1000个数字,即numsSize=1000,按照你的写法
1 for(i=0; i<numsSize;i++){ 2 for(j=i+1; j<numsSize; i++){ 3 ..... 4 } 5 }
这大概算了1000000次。
如果按照我这种写法,将a数组中的值对应到b中,运算1000次,遍历a数组,在b中找等不等-1,又运算1000次。总共是2000次。速度是你的500倍。这样写的好处,就是当我知道a[i]了,想要找到他配对的数,就不用在从头到尾找一遍,看看能不能找到那个配对了。你从头到尾找,还不一定能找到,白白浪费了时间。加了b数组之后,一下子就知道配对的存不存在,如果存在,位置也一下子就知道。
先假设所有的数都是大于0的,基于以上思想得出以下代码。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 int* twoSum(int* nums, int numsSize, int target){ 6 int i; 7 int *re = (int *)malloc(2*sizeof(int));//用来存结果的 8 int b[100]; // 将b全部置位-1 9 10 /** 11 *讲一下这个函数 12 * void *memset(void *str, int c, size_t n) 13 * 第一个参数:str,你要赋值的首地址, 14 * 第二个参数:你想要赋的值,只能是一个值。这个值会充满整个地址空间 15 * 第三个参数:值所占的字节数,你就写sizeof(类型) 16 * 17 */ 18 memset(b, -1, sizeof(int)); 19 20 21 for(i=0; i<numsSize; i++){ // 先做一个对应,以便可以快速找到某个数是否存在 22 b[nums[i]] = i; 23 } 24 25 for(i=0; i<numsSize; i++){ 26 if(b[ target-nums[i] ] != -1){//判断另外一个数存不存在 27 re[0] = i; 28 re[1] = b[ target-nums[i] ]; 29 break;//找到了另外一个数就退出循环 30 } 31 } 32 return re; 33 } 34 35 36 //主函数用来测试我的函数 37 38 int main() 39 { 40 int a[]={1,2,4,9}; 41 int *r = twoSum(a, 4, 3); 42 printf("%d,%d", r[0], r[1]); 43 return 0; 44 }
在判断另外一个数是否存在的代码中
if(b[ target-nums[i] ] != -1){//判断另外一个数存不存在
有一种可能就是target<nums[i] ,这时候就会出现b[负数],这指定是不行的,数组中的索引只能是非负数。方括号里的东西可以叫索引。
从题中来看,因为所有数都是正数,大于target的nums[i]也不可能是解。所以,肯定找不到另外一个数。所以需要加一个条件,target-nums[i]>0 。接下来就出现一个问题,这个条件加在哪里的问题。看这个短路特性(这是个链接),看完你就知道加在哪里了。
回头再说说对应的问题。
如果有一个这样的数组{2,2,4},target = 4;我们在对应的时候,数组中的两个2会对应到一个地方。如下:
b数组中b[2]的值为啥是1而不是0,是因为你是从头到尾遍历a数组从而实现对应的。后边对应的时候讲前边覆盖了。当a[i]=第一个2的时候,另外一个数就是target-a[i]=2。而b[2]正好是第二2。
这个题我们做的假设是所有的数都是正数,那如果正数负数都有会怎么样呢。我们说,在a和b对应的时候,是将a的值和位置,互换,然后对应到b中。如果a的值是负数,那不就出现了b[负数],如下图,
这咋能行,数组的索引不可能是负的啊。
那我们就想一个办法,都把它变成正的。一个很好地办法就是把每一个数都加上一个整数,使得加完之后都是正的。
那加这个正数是多少呢?我也不知道。我们可以找到最小的数,然后加上这个最小的数的绝对值。这样最小的数变成0了。其他的数不就都是正的了。
这样,上边那个图就变成了
都变成正数了,就可以按照我们一开始的想法往下做了。
先把上边思路看懂再看代码
1 /** 2 * Note: The returned array must be malloced, assume caller calls free(). 3 */ 4 int* twoSum(int* nums, int numsSize, int target) { 5 int i; 6 int *re = (int *)malloc(2*sizeof(int)); 7 long long b[25536]= {0};//用作对应,类型设为longlong是为了防止数据求和之后范围超出整形表示范围。整形表示范围就是[-25536-25535] 8 // 比如,最小数据为-25536,最大数据是25535.求和之后就会出现25535+25536。 9 long long m=nums[0];//m存储最小值,以便将所有的数都变成正数。m为什么也用long long呢,我们是要对m求绝对值的,如果,m=-25536,求绝对值之后就变成了了25536,超过了整型表达范围。 10 int top=-1; 11 for(i=0; i<numsSize; i++) { 12 if(nums[i]<m) { 13 m=nums[i];//用来找最小值 14 } 15 if(2*nums[i]==target) {//这这么写是因为我发现它数据有一个超过了整型表达范围,而我又无法申请那么多空间。你可以不用管。 16 re[++top]=i; 17 } 18 } 19 if(top==1) { 20 return re; 21 } 22 m = abs(m); 23 for(i=0; i<numsSize; i++) { 24 b[nums[i]+m] = i+1;//做对应,由于无法将空间批量赋值为-1,所以就批量赋值为0了,这样,我们保存位置的时候,就不能从0开始,就加一保存。 25 } 26 for(i=0; i<numsSize; i++) { 27 //往下自己看 28 if(2*(nums[i]) == target || target+m-nums[i]<= 0) { 29 continue; 30 } 31 if(b[target+m-nums[i]]!=0) { 32 re[0] = i; 33 re[1] = b[target+m-nums[i]]-1; 34 break; 35 } 36 } 37 return re; 38 }
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 int cmp (const void * a, const void * b) 5 { 6 return ( *(int*)a - *(int*)b ); 7 } 8 9 int findb(int* nums, int numsSize,int a){ 10 int i; 11 for(i=0; i<numsSize; i++){ 12 if(nums[i]==a){ 13 break; 14 } 15 } 16 return i; 17 } 18 19 int finde(int* nums, int numsSize,int a){ 20 int i; 21 for(i=numsSize-1; i>=0; i--){ 22 if(nums[i]==a){ 23 break; 24 } 25 } 26 return i; 27 } 28 29 int* twoSum(int* nums, int numsSize, int target) { 30 int *sub = (int *)malloc(numsSize*sizeof(int)); 31 int i; 32 for(i=0; i<numsSize; i++){ 33 sub[i]=nums[i]; 34 } 35 int *re = (int *)malloc(2*sizeof(int)); 36 qsort(sub, numsSize, sizeof(int), cmp); 37 int end =numsSize-1; 38 while(sub[end]+sub[0] > target){ 39 end--; 40 } 41 int f, s; 42 int e; 43 for(i=0; i<end; i++){ 44 for(e=end; e>i; e--){ 45 if(sub[i]+sub[e]==target){ 46 f=sub[i]; 47 s=sub[e]; 48 break; 49 } 50 } 51 if(e!=i){ 52 break; 53 } 54 } 55 56 re[0]=findb(nums, numsSize,f); 57 re[1]=finde(nums, numsSize,s); 58 if(re[0]>re[1]){ 59 int t=re[0]; 60 re[0]=re[1]; 61 re[1]=t; 62 } 63 return re; 64 } 65 66 int main() 67 { 68 int a[]= {-1,-2,-3,-5}; 69 int *r = twoSum(a, 5, 1-8); 70 printf("%d,%d", r[0], r[1]); 71 return 0; 72 }