1. 程式人生 > >劍指Offer面試題41:求和為s的兩個數字;求和為s的連續正數序列 Java實現

劍指Offer面試題41:求和為s的兩個數字;求和為s的連續正數序列 Java實現

題目一:輸入一個遞增排序的陣列和一個數字s,在陣列中找兩個數,使得他們的和剛好是s.如果有多對數字的和等於s,則輸出任意一對即可。例如,輸入陣列{1,2,4,7,11,15}和數字15,則輸出4和11(4+11=15)。 演算法分析: 首先,我們會想到在陣列中固定一個數字,再一次判斷陣列中其餘n-1個數字與它的和是不是等於S。不過這個方法的時間複雜度為O(n^2),會不會有更快的方法? 接著我們提出時間更快的演算法,我們現在陣列中選擇兩個數字,如果它們的和等於輸入的S,我們就找到了要找的兩個數字。如果小於S呢?我們希望兩個數字的和再大一點。由於陣列已經排序好了,我們可以考慮選擇較小的數字後面的數字,因為排在後面的數字要大一些,那麼兩個數字的和也要大一些,就有可能等於輸入的數字S了。同樣,當兩個數字的和大於輸入的數字的時候,我麼可以選擇較大數字前面的數字,因為排在陣列前面的數字要小一些。 我們以陣列{1,2,4,7,11,15}及期待的和為15為例詳細分析一下這個過程。首先定義兩個指標,第一個指標指向陣列的第一個數字1,第二個指標指向陣列的最後一個數字15.這兩個數字的和為16大於15,因此我們把第二個指標向前移動一個數字,讓它指向11.這個時候兩個數字1與11的和為12,小於15,接下來我們把第一個指標向後移動一個數字指向2.此時兩個數字2與11的和為13,還是小於15.我們再一次向後移動第一個指標,讓它指向數字4.數字4與11的和是15,正是我們期盼的結果。 題目二:輸入一個正數s,打印出所有和為s的連續正數序列(至少含有兩個數)。例如,輸入15,由於1+2+3+4+5=4+5+6=7+8=15,所以打印出三個連續序列:1,2,3,4,5;4,5,6;7,8.
演算法分析: 有了前面的經驗,我們也考慮用兩個樹small和big分別表示序列的最小值和最大值。首先把small初始化為1,big初始化為2.如果從small到big的序列的和大於s,我們可以從序列中去掉較小的值,也就是增大small的值。如果從small到big的序列的和小於s,我們可以增大big,讓這個序列包含更多的數字。因為這個序列至少要有兩個數字,我們一直增加small 到(1+s)/2為止。
以求和為9 的所有連續序列為例,我們先把small 初始化為1, big 初始化為2。此時介於small 和big 之間的序列是{1,2},序列的和為3,小於9,所以我們下一步要讓序列包含更多的數字。我們把big 增加1 變成3,此時序列為{ I, 2,坷。由於序列的和是6,仍然小於9,我們接下來再增加big 變成4,介於small 和big 之間的序列也隨之變成{ l, 2, 3, 4}。由於列的和10 大於9,我們要刪去去序列中的一些數字, 於是我們增加small 變成2,此時得到的序列是{2, 3, 4}, 序列的和E好是9。我們找到了第一個和為9 的連續序列,把它打印出來。接下來我們再增加big,重複前面的過程,可以找到第二個和為9 的連續序列{4,5}。

題目1源程式:

</pre><pre name="code" class="java">/**************************************************************      
* Copyright (c) 2016, 
* All rights reserved.                   
* 版 本 號:v1.0                   
* 題目描述:求和為s的兩個數字
*		        輸入一個遞增排序的陣列和一個數字s,在陣列中找兩個數,使得他們的和剛好是s.
*			如果有多對數字的和等於s,則輸出任意一對即可。例如,輸入陣列{1,2,4,7,11,15}和數字15,則輸出4和11(4+11=15)
* 輸入描述:請輸入一個升序陣列(以空格隔開):
*			1 2 3 5 6 7 8 9
*			請輸入要查詢的數字K:
*			8
* 程式輸出:和為8的兩個數是:1和7
* 問題分析: 無
* 演算法描述:	接著我們提出時間更快的演算法,我們現在陣列中選擇兩個數字,如果它們的和等於輸入的S,我們就找到了要找的兩個數字。
* 			如果小於S呢?我們希望兩個數字的和再大一點。由於陣列已經排序好了,我們可以考慮選擇較小的數字後面的數字,
* 			因為排在後面的數字要大一些,那麼兩個數字的和也要大一些,就有可能等於輸入的數字S了。同樣,
* 			當兩個數字的和大於輸入的數字的時候,我麼可以選擇較大數字前面的數字,因為排在陣列前面的數字要小一些。
* 完成日期:2016-09-25
***************************************************************/
package org.marsguo.offerproject41;

import java.util.Scanner;

class FindSumNumberClass{
	public String FindFunction(int[] numarray,int k){
		if(numarray == null)
			return null;
		
		int i = 0;
		int j = numarray.length-1;
		int sum = 0;
		
		while(sum != k){
			sum = numarray[i] + numarray[j];
			if(sum > k){
				j--;
			}
			if(sum < k){
				i++;
			}
		}
		return numarray[i] + "和" + numarray[j];
	}
}
public class FindNumberWithSum {
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		System.out.println("請輸入一個升序陣列(以空格隔開):");
		String str = scanner.nextLine();
		System.out.println("請輸入要查詢的數字K:");
		int k = scanner.nextInt();
		scanner.close();
		
		String[] temp = str.split(" ");
		int[] numarray = new int[temp.length];
		for(int i = 0; i < temp.length; i++){
			numarray[i] = Integer.parseInt(temp[i]);
		}
		
		FindSumNumberClass findclass = new FindSumNumberClass();
		System.out.print("和為" + k + "的兩個數是:");
		System.out.println(findclass.FindFunction(numarray, k));
	}
}


題目二源程式:

/**************************************************************      
* Copyright (c) 2016, 
* All rights reserved.                   
* 版 本 號:v1.0                   
* 題目描述:求和為s的連續正數序列
*		        輸入一個正數s,打印出所有和為s的連續正數序列(至少含有兩個數)。例如,輸入15,由於1+2+3+4+5=4+5+6=7+8=15,
*			所以打印出三個連續序列:1,2,3,4,5;4,5,6;7,8.
* 輸入描述:請輸入要求和的數字k:15
* 程式輸出:和為15連續序列有:
*			1,2,3,4,5,
*			4,5,6,
*			7,8,
* 問題分析: 無
* 演算法描述:	我們也考慮用兩個樹small和big分別表示序列的最小值和最大值。首先把small初始化為1,big初始化為2.
* 			如果從small到big的序列的和大於s,我們可以從序列中去掉較小的值,也就是增大small的值。
* 			如果從small到big的序列的和小於s,我們可以增大big,讓這個序列包含更多的數字。
* 			因為這個序列至少要有兩個數字,我們一直增加small 到(1+s)/2為止。
* 完成日期:2016-09-25
***************************************************************/

package org.marsguo.offerproject41;

import java.util.Scanner;

class FindSequenceClass{
	public void findSequenceFunction(int k){
		if(k <= 0)
			return;
		
		int small = 1;
		int big = 2;
		int end =( k + 1)/2;			//用於結束迴圈,當small接近K的一半時則不用再繼續向後找了
		int sum = 0;					//初始和值設為0
		
		System.out.println("和為" + k +"連續序列有:");
		/*
		找出所有滿足條件的序列,直到small到大k值的一半
		*/
		while(small <= end){
			sum = 0;
			for(int j = big; j >= small; j--)		//求big到small中間的所有序列和
				sum = sum + j;
			
			if(sum == k){
				for(int j = small; j <= big; j++)		//找到滿足條件的和值後,輸出該序列
					System.out.print(j + ",");
				System.out.println();
				big++;						//big++,繼續向後尋找
			}
			else if(sum > k){
				/*如果sum大於要找的K,則將small向後移動,減小sum的值*/
				small++;
			}
			else if(sum < k){
				/*如果sum小於要找的K,則將big向後移動,增大sum的值*/
				big++;
			}
		}
	}
}

public class FindContinuousSequence {
	public static void main(String[] args){
		Scanner scanner = new Scanner(System.in);
		
		System.out.println("請輸入要求和的數字k:");
		int k = scanner.nextInt();
		scanner.close();
		
		FindSequenceClass findsequence = new FindSequenceClass();
		findsequence.findSequenceFunction(k);
	}
}

程式執行結果: