我觉得写得很清晰,希望没有侵犯作者的著作权,原文地址http://blog.csdn.net/hackbuteer1/article/details/6699642

快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值,为了简化起见,我们假设这个数组中肯定存在至少一组符合要求的解。

假如有如下的两个数组,如图所示:

5,6,1,4,7,9,8

给定Sum= 10

1,5,6,7,8,9

给定Sum= 10

分析与解法

这个题目不是很难,也很容易理解。但是要得出高效率的解法,还是需要一番思考的。

解法一

一个直接的解法就是穷举:从数组中任意取出两个数字,计算两者之和是否为给定的数字。

显然其时间复杂度为N(N-1)/2即O(N^2)。这个算法很简单,写起来也很容易,但是效率不高。一般在程序设计里面,要尽可能降低算法的时间和空间复杂度,所以需要继续寻找效率更高的解法。

解法二

求两个数字之和,假设给定的和为Sum。一个变通的思路,就是对数组中的每个数字arr[i]都判别Sum-arr[i]是否在数组中,这样,就变通成为一个查找的算法。

在一个无序数组中查找一个数的复杂度是O(N),对于每个数字arr[i],都需要查找对应的Sum-arr[i]在不在数组中,很容易得到时间复杂度还是O(N^2)。这和最原始的方法相比没有改进。但是如果能够提高查找的效率,就能够提高整个算法的效率。怎样提高查找的效率呢?

学过编程的人都知道,提高查找效率通常可以先将要查找的数组排序,然后用二分查找等方法进行查找,就可以将原来O(N)的查找时间缩短到O(log2N),这样对于每个arr[i],都要花O(log2N)去查找对应的Sum-arr[i]在不在数组中,总的时间复杂度降低为N* log2N。当让将长度为N的数组进行排序本身也需要O(N*log2N)的时间,好在只须要排序一次就够了,所以总的时间复杂度依然是O(N*log2N)。这样,就改进了最原始的方法。

到这里,有的读者可能会更进一步地想,先排序再二分查找固然可以将时间从O(N^2)缩短到O(N*log2N),但是还有更快的查找方法:hash表。因为给定一个数字,根据hash表映射查找另一个数字是否在数组中,只需要O(1)时间。这样的话,总体的算法复杂度可以降低到O(N),但这种方法需要额外增加O(N)的hash表存储空间。某些情况下,用空间换时间也不失为一个好方法。

解法三

还可以换个角度来考虑问题,假设已经有了这个数组的任意两个元素之和的有序数组(长为N^2)。那么利用二分查找法,只需用O(2*log2N)就可以解决这个问题。当然不太可能去计算这个有序数组,因为它需要O(N^2)的时间。但这个思考仍启发我们,可以直接对两个数字的和进行一个有序的遍历,从而降低算法的时间复杂度。

首先对数组进行排序,时间复杂度为(N*log2N)。

然后令i = 0,j = n-1,看arr[i] + arr[j] 是否等于Sum,如果是,则结束。如果小于Sum,则i = i + 1;如果大于Sum,则 j = j – 1。这样只需要在排好序的数组上遍历一次,就可以得到最后的结果,时间复杂度为O(N)。两步加起来总的时间复杂度O(N*log2N),下面这个程序就利用了这个思想,代码如下所示:

[cpp] view plaincopy
  1. int getSumNum(int[] arr,int Sum),   //arr为数组,Sum为和
  2. {
  3. int i,j;
  4. for(i = 0, j = n-1; i < j ; )
  5. {
  6. if(arr[i] + arr[j] == Sum)
  7. return ( i , j );
  8. else if(arr[i] + arr[j] < Sum)
  9. i++;
  10. else
  11. j--;
  12. }
  13. return ( -1 , -1 );
  14. }

它的时间复杂度是O(N)。

刚开始一直无法理解这样一定可以找到这个和吗?难道不会漏掉了解的位置。可以这么理解,假如排好序后的数组为1,3,6,a,9,12,17,28,b,35,46  ,那么i最初指向1的位置,j最初指向46的位置,比如所求的是Sum=a+b,a<b,a和b在数组中的某位置上。那么i和j在变化过程中,只考虑i遇到了a或者j遇到了b的时候,必定有一个先遇到,比如i遇到了a,那么这个时候j必定还没指到b,故这时j指到的值比b大,从而j减小直到b位置。同理若j先指到了b位置,那么i必定还没指到a(这是我们的前提),然后i现在指到的值比a小,故i增加,直到a位置。

扩展问题

1、如果把这个问题中的“两个数字”改成“三个数字”或“任意个数字”时,你的解是什么呢?

三个数字:首先还是先对数组进行排序,然后从i=0到n-1进行遍历,遍历arr[i]时,在调用上面的函数getSumNum(arr , Sum-arr[i])即可。

任意m个数字的想法:

首先还是先对数组进行排序,然后从i=0到n-1个元素遍历,遍历arr[i]时,在剩下的n-1个元素中调用getSumNum(arr,Sum-arr[i]),此时为求m-1个元素和为Sum-arr[i];接下来,同样的方法,从j=0到n-2个元素遍历,遍历arr[j]时在arr上递归调用getSumNum(arr,Sum-arr[i]-arr[j]),此时为求m-2个元素和为Sum-arr[i]-arr[j];依次递归,直到为求2个元素和为Sum-?-?-?...时为止。

不论是求3个数字还好是m个数字,总是能比较穷举法少一个数量级n,比先排序然后二分查找求Sum-arr[i]也要快。

快速找出一个数组中的两个数字,让这两个数字之和等于一个给定的值相关推荐

  1. 算法导论:快速找出无序数组中第k小的数

    题目描述: 给定一个无序整数数组,返回这个数组中第k小的数. 解析: 最平常的思路是将数组排序,最快的排序是快排,然后返回已排序数组的第k个数,算法时间复杂度为O(nlogn),空间复杂度为O(1). ...

  2. 如何快速找出Linux系统中的大文件?

    今日主题: 如何快速找出Linux系统中的大文件? 在前面的文章中,我们讲到了压测时磁盘空间被占满的问题,可以跟今天的文章结合着看. 在性能测试中,我们经常要关注系统磁盘空间,防止因磁盘空间占满而导致 ...

  3. 找出整形数组中的元素最大值。

    // 121218 第八章例8.4.cpp : 定义控制台应用程序的入口点. // /* * Copyright (c) 2012, 烟台大学计算机学院 * All rights reserved. ...

  4. 找出如下数组中最大的元素和最小的元素, a[][]={{3,2,6},{6,8,2,10},{5},{12,3,23}}

    package com.homework.zw; //解题思路:先遍历二维数组的所有元素,用这些元素组成新的一维数组.然后利用冒泡排序找出一维数组的最大元素和最小元素. public class wo ...

  5. 算法题:找出整数数组中两个只出现一次的数字

    问题:一个整数数组里除了两个数字之外,其他的数字都出现了两次.请写程序找出这两个只出现一次的数字.要求时间复杂度为O(n),空间复杂度为O(1). 分析:这是一个很新颖的关于位运算的题目. 首先考虑这 ...

  6. 【Linux】使用du、df 和 sort 命令快速找出Linux系统中的大文件

    在性能测试中,我们经常要关注系统磁盘空间,防止因磁盘空间占满而导致的报错,那么具体怎么查看磁盘空间的大小呢?怎么找到占用空间最大的文件呢? 使用df.du并结合sort,可以快速找到系统中的大文件! ...

  7. 找出有序数组中绝对值最小的数

    假设数组是从小到大排序,数值可能为负数.0.正数. 思路一 可以一次性遍历一遍,找出绝对值最小值,此时时间复杂度为O(N),缺点是没有利用数组是有序的这一特点. int getMinAbs(int[] ...

  8. 找出无序数组中最小的k个数(top k问题)

    2019独角兽企业重金招聘Python工程师标准>>> 给定一个无序的整型数组arr,找到其中最小的k个数 该题是互联网面试中十分高频的一道题,如果用普通的排序算法,排序之后自然可以 ...

  9. 2021-06-29快速找出Excel表格中两列数据不同内容的3种方法!

    https://baijiahao.baidu.com/s?id=1633587400784135995&wfr=spider&for=pc

  10. 找出递增数组中所有相加为m的组合c语言,组合(1-m中选n个数)(示例代码)

    #include int a[100],b[100]={0},m,n; bool p(int k)//定义一个bool类型函数 ,用来判断 { if(b[k]==1) { return false; ...

最新文章

  1. 旅游资源及线路管理系统
  2. python【蓝桥杯vip练习题库】ADV-9 递归倒置字符数组
  3. CSU 1081集训队分组(搜索)
  4. uva 1631——Locker
  5. 小程序 字号设置 slider滚动改变大小_SteerMouse for mac(鼠标设置工具) v5.4.3
  6. 京东排行第一,近 4 万好评,这本 Python 书究竟好在哪?
  7. 使用了Spring boot devtools, dozer转换嵌套对象失败解决记录
  8. 【Xamarin.Android】探索android的底部导航视图
  9. 教你用Appium搭建Android自动化测试框架(详细教程)
  10. grub4dos 引导linux,Grub4dos系统引导
  11. 文字转语音真人发声免费的有哪些?文字转语音有这两款软件就够了
  12. 开根号的笔算算法图解_开根号手算方法
  13. 物联网:Android端控制ZigBee实现生产环境自动控制
  14. office 2010 word文档,插入复选框,方框打勾(打对号)的方法
  15. ARM920T MMU and Cache
  16. macOS 安装 aircrack-ng 破解 wifi,hashcat ,OnlineHashCrack,在线破解握手包
  17. vue中activated
  18. springboot整合poi基于excel模板下载的功能实现
  19. MATLAB 查找互素(质)对
  20. Android | 说说Presentation

热门文章

  1. 掌门1对1《啊呜!卡通人》引强烈共鸣 智能科技赋能高效教学收获点赞
  2. ROS2+Qt5 开发问题汇总
  3. 561.Array Partition I--Python
  4. 一个投资者想要的真正的DAO-Revolution DAO
  5. 如何交换数据:10 分钟为 MQL5 创建 DLL
  6. Redis缓存击穿解决方案之互斥锁
  7. c语言教改课题开题报告,教改项目结题及新项目开题报告会简讯
  8. arcmap按图斑批量出图_ArcGIS实践教程(34)ArcGIS/ArcMap提取图斑转折点坐标(37)ArcGIS/ArcMap多个图层(要素类)的合并...
  9. Rocket 要以硬件隔离引潮流,Docker说” NO”
  10. java 中对象的称呼