和为s的两个数,一串连续数 41

?
?

引言

?
?

第一问题很简单,leetcode上也有相应的题,2Sum问题,leetcode上还有2Sum的进阶版3Sum,只是在这个问题上增加了一层循环而已,另外还有3Sum_Close问题,就是和接近s的三个数,本文将依次介绍2Sum问题,3Sum问题,以及3Sum_close,以及一串连续数问题

?
?

对于3Sum的问题,本文除了常用的退化为2Sum的n平方的解法外,还提供了一种hash的方法,使得利用hash的方法使得4Sum问题不再是n三次方的时间复杂度,可以降到n平方的时间复杂度

?
?

2Sum问题

?
?

这里需要注意的就是要用两个指针来减少对数组的遍历,不然用常规的方法就要遍历两遍数组,两层循环,n平方的时间复杂度

?
?

用一个pointHead从前往后走,用一个pointEnd从后往前走,两个如果没遇到就一直这么走下去,判断这两个数加起来是否满足条件,如果满足,输出这两个数,如果不满足,看和是大于还是小于,大于,说明和需要减小才行,于是pointEnd往前走,如果小于,说明和需要被增大才行,于是pointHead往后走

?
?

public void findNumbersWithSum(int[] sortedArray, int number)

{

if(sortedArray==null)

return ;

int pointHead=0;

int pointEnd=sortedArray.length-1;

while(pointEnd>pointHead)

{

long curSum=sortedArray[pointEnd]+sortedArray[pointHead];

if(curSum==number)

{

System.out.println(sortedArray[pointHead]);

System.out.println(sortedArray[pointEnd]);

break;

}

else

{

if(curSum>number)

pointEnd--;

else

pointHead++;

}

}

}

?
?

以上代码有个问题就是找到一个结果就break了,如果希望找到所有的呢,那么将break变为pointHead++,pointEnd--继续走下去即可

?
?

就算改了还是有一个问题,就是结果会不会有重复的情况呢,答案是有的,比如说

int[] array={1,2,4,7,7,8,8,11,15};

?
?

那么如果解决重复的问题,一个简单的不增加循环的方法就是在每次循环体的开始检查一下pointHead的那个值是否和pointHead-1的那个值相等,如果相等,则pointHead++并且continue,同样的适合pointEnd

?
?

if (pointEnd<sortedArray.length-1&&sortedArray[pointEnd]==sortedArray[pointEnd+1]) {

pointEnd--;

continue;

}

if (pointHead>0&&sortedArray[pointHead]==sortedArray[pointHead-1]) {

pointHead++;

continue;

}

?
?

3Sum问题

?
?

外加一层循环,遍历数组所有数,这个数记为first,那么问题转换为在之后的数中找两个树second以及third,使得first+second+third=target结束循环,在first固定的每层循环中如果小就second+,如果大就third-

?
?

同样需要考虑的一个问题是找到的结果是否会出现重复的情况,除了上面说到的那种方法之外,还有另外一种方法就是用一个hashmap中去重

?
?

将满足条件的结果(三个数字)放入midresult中,midresult是个链表,将midresult放入hashmap中去重

再将hashmap中取出来放入result,result也是个链表,相当于最终的结果是个链表,每个节点是一个解,每个解是一个链表,这个链表中有三个数

?
?

public static ArrayList threeSum(int[] num) {

Arrays.sort(num);

ArrayList result = new ArrayList();

Map hm = new HashMap();

?
?

for (int firstPos = 0; firstPos < num.length; firstPos++) {

int secPos = firstPos + 1;

int thirdPos = num.length - 1;

while (secPos < thirdPos) {

if (num[firstPos] + num[secPos] + num[thirdPos] == 0) {

ArrayList<Integer> midResult = new ArrayList<Integer>();

midResult.add(num[firstPos]);

midResult.add(num[secPos]);

midResult.add(num[thirdPos]);

hm.put(midResult, false);

secPos += 1;

thirdPos -= 1;

} else if (num[firstPos] + num[secPos] + num[thirdPos] < 0) {

secPos += 1;

} else {

thirdPos -= 1;

}

}

}

Iterator it = hm.entrySet().iterator();

while (it.hasNext()) {

//????????????????????????Entry entry =(Entry) it.next();

//????????????????????????result.add(entry.getKey());

result.add(it.next());

}

return result;

?
?

}

?
?

2Sum和3Sum的时间复杂度分析

?
?

我们可以很轻易的就知道2sum的算法复杂度是O(NlogN),因为排序用了NlogN,头尾指针的搜索是线性的,所以总体是O(NlogN)

?
?

考虑3sum, 3sum的算法复杂度就是O(N^2), 注意这里复杂度是N平方,而不是O(N^2 log N),很容易在这里犯错误

?
?

仔细想想可以知道因为你排序只需要排一次,后面的工作都是取出一个数字,然后找剩下的两个数字,找两个数字是2sum用头尾指针线性扫。

?
?

推广下去4sum也就可以退化成3sum问题,那么以此类推,K-sum一步一步退化,最后也就是解决一个2sum的问题,K sum的复杂度是O(n^(K-1))

?
?

3Sum_close问题

?
?

close问题需要维护一个距离dis,也就是得到的和与真实想要的和之间的误差,如果新的比旧的小,则更新结果,另外还要维护一个真实的和ret

?
?

int dis = Integer.MAX_VALUE;

int ret = 0;

int sum = num[i] + num[j] + num[k];

int minus = sum - target;

int d = Math.abs(minus);

if (d < dis) {

dis = d;

ret = sum;

}

if (minus == 0)

return target;

if (minus < 0) {

j++;

} else {

k--;

}

?
?

算法提升

?
?

这里的算法提升主要是用到hash,用hash的话check某个值存在不存在就是常数时间,那么2sum的解法可以是线性的

?
?

比如用hashmap,给定一个sum, 只要线性扫描, 对每一个number判断sum – num存在不存在就可以了。

?
?

注意这个算法对有重复元素的序列也是适用的。比如 2 3 3 4 那么hashmap可以使 hash(2) = 1; hash(3) = 1, hash(4) =1其他都是0, 那么check的时候,扫到两次3都是check sum-3在不在hashmap中,注意最后返回所有符合的pair的时候也还是要去重。

?
?

这样推广的话 3sum 其实也有O(N^2)的类似hash算法,这点和之前是没有提高的,但是4sum就会有更快的一个算法。

?
?

4sum的hash算法

?
?

首先用O(N^2)的时间把所有pair存入hash表,一个pair也就是两两数组成的一对pair,一共有n(n-1)/2个,所以需要n平方的时间复杂度

?
?

根据什么来做hash呢,也就是说hashmap中的key值是什么,我们将一个pair的和作为key值,而value值就是这两个树组成的pair的list数据结构,map[hashvalue] = list,每个list中的元素就是一个pair, hashvalue=这个pair的和

?
?

那么接下来求4sum就变成了在所有的pair value中求 2sum,这个就成了线性算法了,注意这里的线性又是针对pair数量(N^2)的线性,所以整体上这个算法是O(N^2),而且因为我们挂了list, 所以只要符合4sum的我们都可以找到对应的是哪四个数字。

?
?

一连串数问题

?
?

因为是一串连续的数,那么结果就可以用一个small和一个big来界定连续数的第一个和最后一个数

?
?

int small=1;

int big=2;

?
?

另外samll的大小不必循环遍历到n,因为s/2+s/2+1>s,所以small<(s+1)/2

?
?

curSum可以用求和公式求出来(small+big)/2

?
?

如果相等,输出结果,如果大于small++,如果小于big++

?
?

while(small<(s+1)/2)

{

int curSum=0;

for(int i=small;i<=big;i++)

curSum+=i;

if(curSum==s)

{

System.out.println("find one");

for(int i=small;i<=big;i++)

System.out.println(i);

small++;

}

else

{

if(curSum>s)

small++;

else

big++;

}

}

?
?

另外要注意判断一下target的s是否小于3,如果小于3,那么直接返回,因为输入的小于3的无意义,因为1+2就等于3了,而且至少输入两个数

时间: 2024-08-09 10:27:30

和为s的两个数,一串连续数 41的相关文章

leetcode 1: 找出两个数相加等于给定数 two sum

问题描述 对于一个给定的数组,找出2个数,它们满足2个数的和等于一个特定的数,返回这两个数的索引.(从1开始) 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,

用swap 交换两个数

#include<stdio.h>main(){ void swap(int *p1,int *p2);//定义swap函数,用于交换.  int *p,*q; int a,b; //定义两个指针变量和两个数a和b  printf("请输入两个数:");  scanf("%d,%d",&a,&b);//输入两个数  p=&a; q=&b;//将p和q分别指向a和b的地址  swap(p,q);//调用swap函数对p和q执

用链表实现两个数相加

说明:使用链表实现两个数的和,数的高位存储在链表的头部,最后输出结果.注:使用了翻转链表的功能. #include<stdio.h> #include<stdlib.h> struct Node { int value; Node *next; }; Node *reverseList(Node *head) { Node *pCur=head; Node *pPre=NULL; Node *rHead=NULL; while(pCur!=NULL) { Node *pNext=p

c语言求两个数的最大公约数和最小公倍数

#include <stdio.h> int main() { // 两个数的最大公约数求法:最大公约数是这两个数之间公共最大的约数,我们可以先找到这两个数的比较小的数: int num1, num2, gys, gbs; scanf("%d,%d", &num1, &num2); int ji = num2*num1; if (num1>num2) { // 找到两个数较小的数 int temp; temp = num1; num1 = num2;

在给定数组中,找出最先满足两个数的和等于给定数,输出这两个元素的下表

leetcode上的一道题目,虽然不难,但是考察了数据结构中很多的知识 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

一串数字中有两个只出现一次的数字其余都是成对相同,求这两个数

当然如果这个问题是只有一个不同的数,其他数字成对相同,那么就是把所有数字异或就得出这个数了 这次是有两个只出现一次的数字,其他数字都成对相同 1)先把所有数都异或得到数t 2)算出t的二进制第一个1的位置flag 3)将所有数根据二进制flag位置是否为1分成两组b1[],b2[] (此时每组数字的个数一定是奇数) 4)将b1组异或得到ans1,将b2组异或得到ans2 #include<iostream> #include<stdio.h> using namespace std

输入一串数字找出其中缺少的最小的两个数

Description There is a permutation without two numbers in it, and now you know what numbers the permutation has. Please find the two numbers it lose. Input There is a number Tshows there are T test cases below. (T<=10)  For each test case , the first

考题一:研究对全排列着色的性质 问题 A: 首先需要生成 n 的全排列然后对 n 的全排列进行着色, 使得相邻的两个数只需用最少颜色就可以把相邻的两个数用那区分开. (这里相邻包含两层含义:同时在自然

问题: (用C++实现)     **研究对全排列着色的性质. 首先需要生成n的全排列 然后对n的全排列进行着色, 使得相邻的两个数只需用最少颜色就可以把相邻的两个数用那区分开.  (这里相邻包含两层含义:同时在自然顺序和在当前排列的顺序中) 最后, 对着色的结果进行统计 结果需要 给定n,找出所有需要2种颜色的排列. 需要3种颜色的排列 需要4种颜色的排列 (已经证明最多只需要4色) (在第一问基础上)第二问: 需要找出需要4色的规律. 发现需要 4色的排列里面 有一些可以用以下个模型来表示(

如何不运用第三方变量实现两个数的交换

当遇到交换两个变量的时候,我们通常使用的是借助第三方变量实现两个数的交换.那么如果不使用第三方变量时,该怎么操作呢. 下面有两种方法: 1.通过已有的两个变量之间的加或减实现: 这种方法对于两个都是无限接进int取值范围,而相加超出int取值范围的情况不适用. 2.通过异或实现(异或符号为^) ******************************************************************************************************