如何查詢總和等於給定數字的整數陣列中的所有對

jdon發表於2019-01-25

在任何程式設計面試中,練習編碼問題都非常重要。你應該儘量使用陣列,連結串列和字串這樣的資料結構來清除任何程式設計訪問。這是一個透過編碼學習的漫長過程,而這正是這些小編碼問題所幫助的所在。現在,我們將從陣列中看另一個有趣的程式設計問題;編寫一個程式來查詢總和等於給定數字的所有整數對。例如,如果輸入整數陣列是{2,6,3,9,11}並且給定的和是9,則輸出應為{6,3}。聽起來很簡單?也許,但這個問題曾出現在亞馬遜,微軟,Facebook以及其他五家科技公司過去的技術面試中。很多人可能已經聽說過這個問題,你們中的一些人可能已經知道了這個問題的解決方案,但僅僅知道答案是不夠的。在程式設計面試中,除了正確的解決方案外,許多事情都很重要。例如,候選人是否可以提出正確的問題。因此,在直接進行編碼之前,請花一兩秒鐘來思考問題並清除您可能遇到的任何疑問。例如,您可以根據上面給出的問題陳述提出以下問題:陣列只包含正數或負數嗎?如果同一對重複兩次,我們應該每次都列印一次嗎?是否可以接受對的反轉,例如如果給定的和是5,我們可以列印(4,1)和(1,4)。我們是否只需列印不同的一對? (3,3)是有效的一對,是否為6?陣列有多大?許多程式設計師不敢提問而是他們喜歡假設,但在程式設計面試期間,恕我直言,提問相對更好。首先,它表明你沒有搶救答案,其次表明你有能力思考問題,這是任何專業程式設計師的一個非常重要的品質。現在讓我們回到問題,為簡單起見,我們可以假設我們只需要根據它們的出現列印一對或兩次整數,但是對必須是不同的,(2,2)或(3,3)不是有效的一對。求陣列中和為給定數的整數對的三種解法:我想到的第一個解決辦法是我們的朋友蠻力,天真而熱誠。從陣列中取出一個數字,然後迴圈遍歷陣列和輸出對,這些對等於給定的和。對第一個陣列中的所有數字都這樣做,如下面的Java程式所示:

import java.util.Arrays;
/**
 * Java Program to find pairs on integer array whose sum is equal to k
 *
 * @author WINDOWS 8
 */public class ProblemInArray{
 
    public static void main(String args[]) {
        int[] numbers = { 2, 4, 3, 5, 7, 8, 9 };
        int[] numbersWithDuplicates = { 2, 4, 3, 5, 6, -2, 4, 7, 8, 9 };
        prettyPrint(numbers, 7);
        prettyPrint(numbersWithDuplicates, 7);
    }
 
    /**
     * Prints all pair of integer values from given array whose sum is is equal to given number.
     * complexity of this solution is O(n^2)
     */
    public static void printPairs(int[] array, int sum) {
 
        for (int i = 0; i < array.length; i++) {
            int first = array[i];
            for (int j = i + 1; j < array.length; j++) {
                int second = array[j];
 
                if ((first + second) == sum) {
                    System.out.printf("(%d, %d) %n", first, second);
                }
            }
 
        }
    }
    /**
     * Utility method to print input and output for better explanation.
     */
    public static void prettyPrint(int[] givenArray, int givenSum){
        System.out.println("Given array : " + Arrays.toString(givenArray));
        System.out.println("Given sum : " + givenSum);
        System.out.println("Integer numbers, whose sum is equal to value : " + givenSum);
        printPairs(givenArray, givenSum);
    }
 
}
Output:Given sum : 7Integer numbers, whose sum is equal to value : 7
(2, 5)
(4, 3) Given array : [2, 4, 3, 5, 6, -2, 4, 7, 8, 9]Given sum : 7Integer numbers, whose sum is equal to value : 7
(2, 5)
(4, 3)
(3, 4)
(-2, 9)

 
這個解決方案是正確的,但它的時間複雜度非常高,O(n^2),這意味著面試官一定會要求你改進你的答案,並提出複雜度為O(1)、O(n)或O(nlog(n))的解決方案。所以,讓我們更深入地研究,以改進這個答案。為了在一個求和等於給定值的陣列中找到兩個數字,我們可能不需要對每個數字進行比較。我們在這裡可以做的是將所有數字儲存在雜湊表中,並檢查它是否在一對中包含第二個值。例如,如果給定的和是4,成對的一個數是3,那麼另一個數必須是1或-7。您還記得我們問的第一個問題嗎?如果陣列只包含正數,那麼我們不需要在map中檢查負值。該解決方案如何優於前一個解決方案?這將需要較少的比較。只有n可以迭代陣列並在集合中插入值,因為在雜湊表中,add()和contains()都是0(1)操作。所以解決方案的總複雜度是O(n)。這裡是一個Java程式,它找到陣列中的一對值,其值等於雜湊表或集合的k。在這個程式中,我們還編寫了在Java中給定範圍內生成隨機數的實用方法。您可以使用此方法對隨機輸入進行測試。順便說一下,隨機數只適合演示,不要在單元測試中使用它們。從printpairsusingset()方法可以學到的另一個好處是預驗證,檢查輸入是否有效,以便進一步進行。
 

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
 * Java Program to find two elements in an array that sum to k.
 *
 * @author WINDOWS 8
 */public class ArraySumUsingSet {
 
    public static void main(String args[]) {
       prettyPrint(getRandomArray(9), 11);
       prettyPrint(getRandomArray(10), 12);
    }
 
    /**
     * Given an array of integers finds two elements in the array whose sum is equal to n.
     * @param numbers
     * @param n
     */
    public static void printPairsUsingSet(int[] numbers, int n){
        if(numbers.length < 2){
            return;
        }        
        Set set = new HashSet(numbers.length);
        
        for(int value : numbers){
            int target = n - value;
            
            // if target number is not in set then add
            if(!set.contains(target)){
                set.add(value);
            }else {
                System.out.printf("(%d, %d) %n", value, target);
            }
        }
    }
    
    /*
     * Utility method to find two elements in an array that sum to k.
     */
    public static void prettyPrint(int[] random, int k){
        System.out.println("Random Integer array : " + Arrays.toString(random));
        System.out.println("Sum : " + k);
        System.out.println("pair of numbers from an array whose sum equals " + k);
        printPairsUsingSet(random, k);
    }
    
    /**
     * Utility method to return random array of Integers in a range of 0 to 15
     */
    public static int[] getRandomArray(int length){
        int[] randoms = new int[length];
        for(int i=0; i<length; i++){
            randoms[i] = (int) (Math.random()*15);
        }
        return randoms;
    }
 
}
Output:Random Integer array : [0, 14, 0, 4, 7, 8, 3, 5, 7]
Sum : 11
pair of numbers from an array whose sum equals 11
(7, 4)
(3, 8)
(7, 4) Random Integer array : [10, 9, 5, 9, 0, 10, 2, 10, 1, 9]
Sum : 12
pair of numbers from an array whose sum equals 12
(2, 10)

還有一件事,我們在這裡使用HashSet,但由於Java中的HashSet內部使用HashMap,如果使用這些資料結構,它將沒有任何區別。這個解決方案几乎沒有約束,它需要額外的O階空間( n)在Hashtable或Set中儲存數字,因此如果陣列非常大,你需要額外的空間可能會有問題(記住我們在編寫解決方案之前問過的問題)。對於大型陣列,您需要一個不需要額外空間的解決方案,也稱為就地解決方案。如果面試官會問你如何找到一個陣列中的兩個值是否總和到一個給定值而沒有任何額外的空間,那麼第一個解決方案也不會起作用,因為它的複雜性太高而且對一個大陣列進行排序太長了。複雜的解決方案,例如O(n),O(logN)或O(NLongN)應該可以工作。更有效的就地解決方案是對陣列進行排序並使用兩個指標從兩個方向(即開始和結束)掃描陣列。如果兩個值的總和等於給定數,那麼我們輸出該對並推進它們。如果兩個數之和小於k,那麼我們增加左指標,否則如果總和大於k,我們遞減右指標,直到兩個指標在陣列的某個部分相遇。由於排序,該解決方案的複雜性將是O(NlogN)。請記住使用像quicksort這樣的就地排序演算法來對陣列進行排序,因為我們沒有額外的空間。值得慶幸的是,Arrays.sort()方法使用兩個pivot快速排序演算法來對基元陣列進行排序。

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
 * Java Program to find all pairs on integer array whose sum is equal to k
 *
 * @author WINDOWS 7
 */public class PrintArrayPairs {
 
    public static void main(String args[]) {
       prettyPrint( new int[]{ 12, 14, 17, 15, 19, 20, -11}, 9);
       prettyPrint( new int[]{ 2, 4, 7, 5, 9, 10, -1}, 9);
    }
 
    /**
     * Given a number finds two numbers from an array so that the sum is equal to that number k.
     * @param numbers
     * @param k
     */
    public static void printPairsUsingTwoPointers(int[] numbers, int k){
        if(numbers.length < 2){
            return;
        }
        Arrays.sort(numbers);
        
        int left = 0; int right = numbers.length -1;
        while(left < right){
            int sum = numbers[left] + numbers[right];
            if(sum == k){
                System.out.printf("(%d, %d) %n", numbers[left], numbers[right]);
                left = left + 1;
                right = right -1;
                
            }else if(sum < k){
                left = left +1;
                
            }else if (sum > k) {
                right = right -1;
            }
        }
       
    }
    
    /*
     * Utility method to print two elements in an array that sum to k.
     */
    public static void prettyPrint(int[] random, int k){
        System.out.println("input int array : " + Arrays.toString(random));
        System.out.println("All pairs in an array of integers whose sum is equal to a given value " + k);
        printPairsUsingTwoPointers(random, k);
    }
    
}
Output :
input int array : [12, 14, 17, 15, 19, 20, -11]All pairs in an array of integers whose sum is equal to a given value 9
(-11, 20)
input int array : [2, 4, 7, 5, 9, 10, -1]All pairs in an array of integers whose sum is equal to a given value 9
(-1, 10)
(2, 7)
(4, 5)

在這個基於陣列的面試問題上,找出一個整數陣列中的所有對,其和等於給定整數。我們已經看到了解決這個問題的三種方法,從最簡單的蠻力解決方案到可接受的O(N),再加上額外的空間和O(nlogn)。如果有人想做更多的練習,我建議為這個問題編寫JUnit測試用例,給定一組約束,即使陣列包含重複的,並且在這些解決方案上發現錯誤,也只需要列印唯一的對。或者,您也可以嘗試解決它的表親問題,給定一個整數陣列,檢查是否有3個數字的總和為0或給定的數字。記住,旅行比到達目的地更有趣:)
練習:1)為此問題編寫JUnit測試,並檢查每個解決方案是否透過這些測試。
2)在時間和空間複雜性方面提出更好的解決方案?
3)找出這些解的邊界條件。

相關文章