剑指Offer:和为s的两个数字VS和为s的连续正数序列

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25343557/article/details/79388384

题目一:输入一个递增排序的数组和一个数字 s,在数组中查找两个数,得它们的和正好是 s。如果有多对数字的和等于 s,输出任意一对即可。

      例如输入数组 {1,2,4,7,11,15} 和数字 15。由于 4+11=15,因此输出4和11 。

      最普通的解法就是一个个的遍历数组中的元素,比如第一次找到数字1作为一个加数,再在数组中查找数字1后面的数字并与1相加,判断是否符合我们要求的数字。如果相等说明找到了两个数字,否则找数字1后面的数字2作为加数再次遍历查找符合数字。这种解法的时间复杂度为O(n2)。

两端逼近,时间复杂度为O(n)

      使用两个指针分别指向数组的头部和尾部元素,将两个指针的和记为sum。如果sum==s,说明找到符合条件的两数,直接返回;如果sum<s,说明两数不够,左边的指针向中间走一步,这样sum增大(因为数组是递增的),再次判断;如果sum>s;说明两数太大,右边指针向中间走一步,这样sum减小,再次判断。 ![这里写图片描述](https://img-blog.csdn.net/20180227115648293?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMjUzNDM1NTc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

实现代码:

private static int[] FindNumsWithSum(int[] arr,int s){
	if(arr==null||arr.length==0){
		throw new RuntimeException("Array is null or length==0!!");
	}
	int sum = 0;
	int left=0,right=arr.length-1;
	while(left<right){
		sum = arr[left]+arr[right];
		if(sum==s){
			return new int[]{arr[left],arr[right]};
		}
		if(sum<s){
			left++;
		}else{
			right--;
		}
	}
	return null;
}

题目二:输入一个正数 s,打印出所有和为 s 的连续正数序列 (至少两个数)。例如输入 15,由于 1+2+3+4+5=4+5+6=7+8=15,所以结果打出3 个连续序列15、46和 7~8。

      有了前面题目的方法我们在这道题目中可以使用类似的思路,只不过不再是两端逼近了。
      我们使用两个变量start(初始化为1)end(初始化为2),因为序列长度至少为2。使用sum记录start至end的和。

找出s=9时的连续子序列:
开始时sum=3<s,sum需要增大,end++(为3),sum=6<s,sum需要增大,end++(为4),sum=10>s,sum需要减小,start++(为2),sum=9s,找到一个子序列2~4。
接下来end++(为5),sum=14>9,sum需要减小,start++(为3),sum=12>9,sum需要减小,start++(为4),sum=9
s,找到一个子序列4~5。
接下来无论是start如果再自增为5,我们不可能再找到任何子序列使其和为9。所以start的边界为(1+s)/2。
这里写图片描述
实现代码:

private static void FindContinueSeq(int s){
	if(s<3){
		return;
	}
	int start=1,end=2,sum=3;
	while(start<(1+s)/2){
		if(sum==s){
			System.out.print("找到一个子序列:");
			int i=start;
			while(i<=end){
				System.out.print(i+" ");
				i++;
			}
			System.out.println();
		}
		if(sum<s){
			end++;
			sum += end;
		}else{
			sum -= start;
			start++;
		}
	}
}
展开阅读全文

没有更多推荐了,返回首页