劍指Offer面試題41:求和為s的兩個數字;求和為s的連續正數序列 Java實現
阿新 • • 發佈:2019-01-07
題目一:輸入一個遞增排序的陣列和一個數字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}。
程式執行結果:
以求和為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);
}
}
程式執行結果: