การค้นหาองค์ประกอบสามรายการในอาร์เรย์ที่มีผลรวมใกล้เคียงกับจำนวนที่กำหนดมากที่สุด


155

ได้รับอาร์เรย์ของจำนวนเต็ม, A 1 , A 2 , ... , A nรวมทั้งเชิงลบและบวกและอีกจำนวนเต็มเอสตอนนี้เราต้องไปหาสามเลขที่แตกต่างกันในอาร์เรย์ที่มีผลรวมจะใกล้เคียงกับที่ได้รับจำนวนเต็ม S หากมีมากกว่าหนึ่งวิธีการแก้ปัญหาใด ๆ ของพวกเขาก็โอเค

คุณสามารถสมมติว่าจำนวนเต็มทั้งหมดอยู่ในช่วง int32_t และจะไม่มีการคำนวณทางคณิตศาสตร์มากเกินไปเมื่อคำนวณผลรวม S ไม่มีอะไรพิเศษ แต่เป็นหมายเลขที่สุ่มเลือก

มีอัลกอริทึมที่มีประสิทธิภาพนอกเหนือจากการค้นหาแบบ brute force เพื่อหาจำนวนเต็มสามตัวหรือไม่?


1
หากคุณกำลังมองหาผลรวมเท่ากับจำนวน (และไม่ได้ใกล้เคียงที่สุด) นี้จะเป็นปัญหา 3sum
Bernhard Barker

คำตอบ:


186

มีอัลกอริทึมที่มีประสิทธิภาพนอกเหนือจากการค้นหาแบบ brute force เพื่อหาจำนวนเต็มสามตัวหรือไม่?

อ๋อ; เราสามารถแก้ปัญหานี้ในเวลา O (n 2 )! ก่อนอื่นให้พิจารณาว่าปัญหาของคุณPอาจถูกใช้ถ้อยคำอย่างเท่าเทียมกันในวิธีที่แตกต่างกันเล็กน้อยซึ่งไม่จำเป็นต้องใช้ "มูลค่าเป้าหมาย":

ปัญหาดั้งเดิมP:กำหนดอาร์เรย์Aของnจำนวนเต็มและค่าเป้าหมายS, มี 3-tuple จากAผลรวมนั้นSหรือไม่?

ปัญหาที่แก้ไขP':กำหนดอาร์เรย์Aของnจำนวนเต็มมี 3-tuple จากAผลรวมนั้นเป็นศูนย์หรือไม่

ขอให้สังเกตว่าคุณสามารถไปจากรุ่นของปัญหานี้P'มาจากPโดยการลบ S / 3 ของคุณจากองค์ประกอบในแต่ละAแต่ตอนนี้คุณไม่จำเป็นต้องมีค่าเป้าหมายอีกต่อไป

เห็นได้ชัดว่าถ้าเราทดสอบ 3 tuples ที่เป็นไปได้ทั้งหมดเราจะแก้ปัญหาใน O (n 3 ) - นั่นคือพื้นฐานของแรงเดรัจฉาน เป็นไปได้ไหมที่จะทำได้ดีกว่า ถ้าเราเลือกสิ่งอันดับในแบบที่ค่อนข้างฉลาดกว่าล่ะ?

อันดับแรกเราลงทุนเวลาเพื่อเรียงลำดับอาร์เรย์ซึ่งทำให้เราต้องเสียค่าปรับครั้งแรกของ O (n log n) ตอนนี้เราเรียกใช้อัลกอริทึมนี้:

for (i in 1..n-2) {
  j = i+1  // Start right after i.
  k = n    // Start at the end of the array.

  while (k >= j) {
    // We got a match! All done.
    if (A[i] + A[j] + A[k] == 0) return (A[i], A[j], A[k])

    // We didn't match. Let's try to get a little closer:
    //   If the sum was too big, decrement k.
    //   If the sum was too small, increment j.
    (A[i] + A[j] + A[k] > 0) ? k-- : j++
  }
  // When the while-loop finishes, j and k have passed each other and there's
  // no more useful combinations that we can try with this i.
}

อัลกอริทึมนี้ทำงานโดยการวางสามตัวชี้i, jและkตามจุดต่าง ๆ ในอาร์เรย์ iเริ่มต้นที่จุดเริ่มต้นและค่อยๆทำงานจนจบ kชี้ไปที่องค์ประกอบสุดท้าย jชี้ไปยังจุดiเริ่มต้นที่ เราพยายามรวมองค์ประกอบต่างๆตามดัชนีของพวกเขาซ้ำ ๆ และในแต่ละครั้งที่หนึ่งในสิ่งต่อไปนี้เกิดขึ้น:

  • ผลรวมถูกต้องแน่นอน! เราพบคำตอบแล้ว
  • ผลรวมมีขนาดเล็กเกินไป เลื่อนjเข้าใกล้จุดสิ้นสุดเพื่อเลือกหมายเลขที่ใหญ่ที่สุดถัดไป
  • ผลรวมนั้นใหญ่เกินไป เลื่อนkเข้าใกล้จุดเริ่มต้นเพื่อเลือกจำนวนที่น้อยที่สุดถัดไป

สำหรับแต่ละพiอยน์เตอร์ของjและkจะค่อยๆเข้าใกล้กันมากขึ้น ในที่สุดพวกเขาก็จะผ่านกันและกันและ ณ จุดนั้นเราไม่จำเป็นต้องลองอะไรอย่างอื่นอีกiเพราะเราจะรวมองค์ประกอบเดียวกันเข้าด้วยกันในลำดับที่ต่างออกไป หลังจากจุดที่เราลองต่อไปiและทำซ้ำ

ในที่สุดเราจะหมดความเป็นไปได้ที่มีประโยชน์หรือเราจะพบทางออก คุณจะเห็นได้ว่านี่คือ O (n 2 ) เนื่องจากเราดำเนินการลูปด้านนอก O (n) ครั้งและเราดำเนินการลูปภายใน O (n) ครั้ง เป็นไปได้ที่จะทำ sub-quadratically นี้ถ้าคุณนึกออกจริง ๆ โดยการแทนจำนวนเต็มแต่ละตัวเป็นบิตเวกเตอร์และทำการแปลงฟูริเยร์ที่รวดเร็ว แต่มันอยู่นอกเหนือขอบเขตของคำตอบนี้


หมายเหตุ:เนื่องจากนี่เป็นคำถามสัมภาษณ์ฉันจึงโกงนิดหน่อยที่นี่: อัลกอริทึมนี้อนุญาตให้เลือกองค์ประกอบเดียวกันหลาย ๆ ครั้ง นั่นคือ (-1, -1, 2) จะเป็นโซลูชันที่ถูกต้องเช่นเดียวกับ (0, 0, 0) นอกจากนี้ยังค้นหาเฉพาะคำตอบที่แน่นอนไม่ใช่คำตอบที่ใกล้เคียงที่สุดตามที่กล่าวถึงชื่อ ในฐานะที่เป็นแบบฝึกหัดให้กับผู้อ่านฉันจะให้คุณหาวิธีทำให้มันใช้งานได้กับองค์ประกอบที่แตกต่างเท่านั้น (แต่เป็นการเปลี่ยนแปลงที่ง่ายมาก) และคำตอบที่แน่นอน (ซึ่งก็เป็นการเปลี่ยนแปลงที่ง่ายด้วย)


8
ดูเหมือนว่าอัลกอริทึมสามารถค้นหา 3 tuple ที่เท่ากับ S ไม่ใกล้เคียงกับ S.
ZelluX

7
ZelluX: ตามที่ฉันพูดถึงในบันทึกย่อฉันไม่ต้องการให้มากเกินไปเพราะมันเป็นปัญหาในการสัมภาษณ์ หวังว่าคุณสามารถดูวิธีการแก้ไขเพื่อให้ได้คำตอบที่ใกล้เคียงที่สุด (คำแนะนำ: วิธีหนึ่งคือการติดตามคำตอบที่ใกล้เคียงที่สุดเพื่อให้ห่างไกลและเขียนทับมันถ้าคุณพบหนึ่งที่ดีกว่า.)
จอห์น Feminella

12
จะทำอย่างไรถ้าเราไม่แก้ไขคำแถลงปัญหาแทนเราจะค้นหา aj และ ak ที่รวมเป็น ai + S
บูลีน

3
@ZelluX: มันคล้ายกับวิธีการเรียงแบบผสาน (นั่นคือวิธีที่มันคลิกครั้งแรกสำหรับฉัน) สิ่งที่ห่วงด้านในพยายามที่จะทำคือการพิสูจน์ว่าทั้ง [เจ] หรือ A [k] ไม่สามารถเป็นส่วนหนึ่งของการใดวิธีการแก้ปัญหาที่น่าพอใจ ปัญหา ณ จุดใดคือ: "มีคู่ j '> = j และ k' <= k เช่นที่ [j] + A [k] = S - A [i] หรือไม่" ดูที่คู่ปัจจุบัน (i, j) มีความเป็นไปได้ 3 แบบ: ผลรวมคือการตี (หยุด - เราชนะแล้ว) มันต่ำเกินไปหรือสูงเกินไป ถ้ามันต่ำเกินไปผลรวม A [j] + A [k '] จะต้องต่ำเกินไปสำหรับทุก ๆ k' <= k เนื่องจากในผลรวมแต่ละคำเทอมแรก (A [j]) จะเท่ากัน ..
j_random_hacker

1
... และภาคที่สอง (A [k ']) จะเหมือนกันหรือต่ำกว่า A [k] ดังนั้นในกรณีนี้เราได้พิสูจน์แล้วว่า [j] ไม่สามารถมีส่วนร่วมในผลรวมที่น่าพึงพอใจใด ๆ - ดังนั้นเราอาจทิ้งมันเช่นกัน! ซึ่งเราทำได้โดยการตั้งค่า j = j + 1 และเริ่มต้นใหม่ (แม้ว่ามันอาจช่วยในการคิดในแง่ของการแก้ปัญหาย่อยที่เล็กกว่าซ้ำ) ถ้าผลรวม A [j] + A [k] สูงเกินไปเราก็รู้ว่า A [j '] + A [k] จะต้องสูงเกินไปสำหรับทุก ๆ j'> = j เนื่องจาก A [j '] อย่างน้อยต้องใหญ่เท่ากับ A [j] และเราสูงเกินไปแล้ว ซึ่งหมายความว่าเราสามารถทิ้ง A [k] ได้อย่างปลอดภัยโดยการตั้งค่า k = k-1 และเริ่มใหม่
j_random_hacker

28

แน่นอนว่านี่เป็นวิธีแก้ปัญหาที่ดีกว่าเพราะอ่านง่ายกว่าและมีแนวโน้มที่จะเกิดข้อผิดพลาดน้อยลง ปัญหาเดียวคือเราต้องเพิ่มโค้ดสองสามบรรทัดเพื่อหลีกเลี่ยงการเลือกองค์ประกอบเดียวหลายรายการ

โซลูชัน O (n ^ 2) อื่น (โดยใช้ hashset)

// K is the sum that we are looking for
for i 1..n
    int s1 = K - A[i]
    for j 1..i
        int s2 = s1 - A[j]
        if (set.contains(s2))
            print the numbers
    set.add(A[i])

8
ข้อเสียคือที่เก็บ O (N) แทนที่จะเก็บไว้ในสถานที่
Charles Munger

6
การใช้ hashset นั้นไม่เข้มงวด O (n ^ 2) เนื่องจากชุดแฮชสามารถทำให้เสื่อมลงได้ในบางโอกาส
Ext3h

@Charles - โซลูชันของ John ต้องการพื้นที่ O (N) เนื่องจากคุณเปลี่ยนอาร์เรย์เดิมในขณะที่เรียงลำดับ นั่นหมายความว่าผู้ที่โทรเข้าอาจต้องการสำเนาการป้องกันก่อนที่จะใช้ฟังก์ชั่น
gamliela

ฉันคิดว่ามีข้อผิดพลาดในอัลกอริทึมของคุณ s2อาจเป็นองค์ประกอบที่เลือกไว้แล้ว เช่นถ้าเป็นอาร์เรย์0,1,2และKเป็น2มีไม่ควรจะเป็นคำตอบ ฉันคิดว่าอัลกอริทึมของคุณจะแสดงผล0,1,1ที่ไม่ถูกต้องอย่างเห็นได้ชัด
Yamcha

7

วิธีแก้ปัญหาของ John Feminellaมีข้อบกพร่อง

ที่เส้น

if (A[i] + A[j] + A[k] == 0) return (A[i], A[j], A[k])

เราต้องตรวจสอบว่าฉัน, j, k แตกต่างกันทั้งหมด มิฉะนั้นถ้าองค์ประกอบเป้าหมายของฉันคือและถ้าเข้าแถวของฉันมี6 {3,2,1,7,9,0,-4,6}ถ้าฉันพิมพ์ tuples ที่รวมเป็น 6 ดังนั้นฉันก็จะได้0,0,6ผลลัพธ์เหมือนกัน เพื่อหลีกเลี่ยงปัญหานี้เราจำเป็นต้องแก้ไขเงื่อนไขด้วยวิธีนี้

if ((A[i] + A[j] + A[k] == 0) && (i!=j) && (i!=k) && (j!=k)) return (A[i], A[j], A[k])

2
วิธีแก้ปัญหา John Feminella เป็นเพียงการนำเสนออัลกอริทึมในการแก้ปัญหาเขายังได้ระบุว่าวิธีแก้ปัญหาของเขาจะไม่ทำงานสำหรับสภาพจำนวนที่แตกต่างกันและคุณต้องแก้ไขโค้ดข้างบนเล็กน้อยซึ่งเขาได้ทิ้งไว้ให้ผู้อ่าน
EmptyData

3
ที่จริงแล้วฉันจะไม่เป็น j เพราะคุณเริ่มต้นที่ j = i + 1 เสมอเงื่อนไขที่แท้จริงที่คุณควรตรวจสอบคือ j == k อย่างไรก็ตามโดยการตั้งค่าขณะวนลูปเป็น j <k คุณจะสามารถแก้ไขปัญหาได้โดยไม่ต้องใช้ความยาวหากคำสั่งเนื่องจาก k จะมากกว่า j และ j มากกว่าเสมอ
lorenzocastillo

2
นี่ดูเหมือนจะไม่ใช่คำตอบสำหรับคำถาม แต่ชอบความเห็นต่อคำตอบของ John Feminella
Bernhard Barker

6

แล้วเรื่องแบบนี้ซึ่งเป็น O (n ^ 2)

for(each ele in the sorted array)
{
    ele = arr[i] - YOUR_NUMBER;
    let front be the pointer to the front of the array;
    let rear be the pointer to the rear element of the array.;

    // till front is not greater than rear.                    
    while(front <= rear)
    {
        if(*front + *rear == ele)
        {
            print "Found triplet "<<*front<<","<<*rear<<","<<ele<<endl;
            break;
        }
        else
        {
            // sum is > ele, so we need to decrease the sum by decrementing rear pointer.
            if((*front + *rear) > ele)
                decrement rear pointer.
            // sum is < ele, so we need to increase the sum by incrementing the front pointer.
            else
                increment front pointer.
        }
    }

มันจะค้นหาว่าผลรวมของ 3 องค์ประกอบเท่ากับจำนวนของคุณหรือไม่ หากคุณต้องการใกล้เคียงที่สุดคุณสามารถปรับเปลี่ยนเพื่อจดจำเดลต้าที่เล็กที่สุด (ความแตกต่างระหว่างจำนวนแฝดแฝดปัจจุบันของคุณ) และสุดท้ายพิมพ์แฝดที่สอดคล้องกับเดลต้าที่เล็กที่สุด


ถ้าคุณต้องการหาองค์ประกอบ k เพื่อหาผลรวมความซับซ้อนคืออะไร? คุณจัดการกับสิ่งนี้อย่างไร
coder_15

ด้วยวิธีการนี้ความซับซ้อนขององค์ประกอบ k คือ O (n ^ (k-1)) สำหรับ k> = 2 คุณต้องเพิ่มวนรอบนอกสำหรับการสรุปเพิ่มเติมทุกครั้ง
Ext3h

5

โปรดทราบว่าเรามีอาร์เรย์ที่เรียงลำดับแล้ว โซลูชันนี้คล้ายกับโซลูชันของ John เท่านั้นที่จะหาผลรวมและไม่ทำซ้ำองค์ประกอบเดียวกัน

#include <stdio.h>;

int checkForSum (int arr[], int len, int sum) { //arr is sorted
    int i;
    for (i = 0; i < len ; i++) {
        int left = i + 1;
        int right = len - 1;
        while (right > left) {
            printf ("values are %d %d %d\n", arr[i], arr[left], arr[right]);
            if (arr[right] + arr[left] + arr[i] - sum == 0) {
                printf ("final values are %d %d %d\n", arr[i], arr[left], arr[right]);
                return 1;
            }
            if (arr[right] + arr[left] + arr[i] - sum > 0)
                right--;
            else
                left++;
        }
    }
    return -1;
}
int main (int argc, char **argv) {
    int arr[] = {-99, -45, -6, -5, 0, 9, 12, 16, 21, 29};
    int sum = 4;
    printf ("check for sum %d in arr is %d\n", sum, checkForSum(arr, 10, sum));
}

มันเป็นสิ่งจำเป็นในการคำนวณแน่นอนa[r] + a[l] + a[i] - sumความแตกต่างของ ลอง arr = [-1, 2, 1, -4] sum = 1ดู
Dimitry

3

นี่คือรหัส C ++:

bool FindSumZero(int a[], int n, int& x, int& y, int& z)
{
    if (n < 3)
        return false;

    sort(a, a+n);

    for (int i = 0; i < n-2; ++i)
    {
        int j = i+1;
        int k = n-1;

        while (k >= j)
        {
            int s = a[i]+a[j]+a[k];

            if (s == 0 && i != j && j != k && k != i)
            {
                x = a[i], y = a[j], z = a[k];
                return true;
            }

            if (s > 0)
                --k;
            else
                ++j;
        }
    }

    return false;
}

2

ง่ายมาก N ^ 2 * logn วิธีการแก้ปัญหา: การจัดเรียงเข้าแถวแล้วไปผ่านทุกคู่ฉัน , A J (N ^ 2 เวลา) และสำหรับการตรวจสอบแต่ละคู่ไม่ว่าจะเป็น (S - เป็นฉัน - เป็นเจ ) อยู่ในอาร์เรย์ ( เวลาเข้าสู่ระบบ)

อีกโซลูชัน O (S * N) ใช้วิธีการเขียนโปรแกรมแบบไดนามิกแบบดั้งเดิม

ในระยะสั้น:

สร้างอาร์เรย์ 2 มิติ V [4] [S + 1] เติมลงในแบบที่:

V [0] [0] = 1, V [0] [x] = 0;

V 1 [A i ] = 1 สำหรับ i ใด ๆ , V 1 [x] = 0 สำหรับ x อื่น ๆ ทั้งหมด

V [2] [A i + A j ] = 1, สำหรับ i ใด ๆ , j V [2] [x] = 0 สำหรับ x อื่น ๆ

V [3] [ผลรวมขององค์ประกอบใด ๆ 3] = 1

ในการเติมให้ทำซ้ำผ่าน A iสำหรับแต่ละ A i จะวนซ้ำผ่านอาร์เรย์จากขวาไปซ้าย


เปลี่ยนอัลกอริทึมแรกเล็กน้อยหากองค์ประกอบไม่มีอยู่เมื่อสิ้นสุดการค้นหาแบบไบนารีเราจะต้องดูองค์ประกอบทางซ้ายกระแสและขวาเพื่อดูว่าองค์ประกอบใดให้ผลลัพธ์ที่ใกล้เคียงที่สุด .
Anurag

อาร์เรย์มีขนาดใหญ่เกินไปและไม่ใช่ O (s * N) ขั้นตอนนี้คือ O (N ^ 2): V [2] [Ai + Aj] = 1 สำหรับ i, j V [2] [x] = 0 สำหรับ x อื่น ๆ
Richard

1

สิ่งนี้สามารถแก้ไขได้อย่างมีประสิทธิภาพใน O (n log (n)) ดังต่อไปนี้ ฉันให้วิธีแก้ปัญหาซึ่งบอกว่าผลรวมของสามตัวเลขใด ๆ เท่ากับจำนวนที่กำหนด

import java.util.*;
public class MainClass {
        public static void main(String[] args) {
        int[] a = {-1, 0, 1, 2, 3, 5, -4, 6};
        System.out.println(((Object) isThreeSumEqualsTarget(a, 11)).toString());
}

public static boolean isThreeSumEqualsTarget(int[] array, int targetNumber) {

    //O(n log (n))
    Arrays.sort(array);
    System.out.println(Arrays.toString(array));

    int leftIndex = 0;
    int rightIndex = array.length - 1;

    //O(n)
    while (leftIndex + 1 < rightIndex - 1) {
        //take sum of two corners
        int sum = array[leftIndex] + array[rightIndex];
        //find if the number matches exactly. Or get the closest match.
        //here i am not storing closest matches. You can do it for yourself.
        //O(log (n)) complexity
        int binarySearchClosestIndex = binarySearch(leftIndex + 1, rightIndex - 1, targetNumber - sum, array);
        //if exact match is found, we already got the answer
        if (-1 == binarySearchClosestIndex) {
            System.out.println(("combo is " + array[leftIndex] + ", " + array[rightIndex] + ", " + (targetNumber - sum)));
            return true;
        }
        //if exact match is not found, we have to decide which pointer, left or right to move inwards
        //we are here means , either we are on left end or on right end
        else {

            //we ended up searching towards start of array,i.e. we need a lesser sum , lets move inwards from right
            //we need to have a lower sum, lets decrease right index
            if (binarySearchClosestIndex == leftIndex + 1) {
                rightIndex--;
            } else if (binarySearchClosestIndex == rightIndex - 1) {
                //we need to have a higher sum, lets decrease right index
                leftIndex++;
            }
        }
    }
    return false;
}

public static int binarySearch(int start, int end, int elem, int[] array) {
    int mid = 0;
    while (start <= end) {
        mid = (start + end) >>> 1;
        if (elem < array[mid]) {
            end = mid - 1;
        } else if (elem > array[mid]) {
            start = mid + 1;
        } else {
            //exact match case
            //Suits more for this particular case to return -1
            return -1;
        }
    }
    return mid;
}
}

ฉันไม่คิดว่ามันจะไปได้ จริงอยู่ที่คุณมีสองกรณีง่าย ๆ ของวิธีการล่วงหน้าleftIndexหรือrightIndexเมื่อองค์ประกอบทั้งหมดที่อยู่ตรงกลางมีขนาดเล็กหรือใหญ่กว่าจำนวนที่คุณต้องการอย่างเคร่งครัด แต่สิ่งที่เกี่ยวกับกรณีเมื่อการค้นหาแบบไบนารีหยุดที่ไหนสักแห่งที่อยู่ตรงกลาง? คุณจะต้องตรวจสอบทั้งสองสาขา (ที่ไหนrightIndex--และleftIndex++) ในการแก้ปัญหาของคุณคุณเพียงแค่เพิกเฉยต่อสถานการณ์นี้ แต่ฉันไม่คิดว่ามีวิธีที่จะเอาชนะปัญหานี้
Aivean

0

การลดลง: ฉันคิดว่า @John Feminella ทางออก O (n2) นั้นสง่างามที่สุด เรายังสามารถลด A [n] เพื่อค้นหา tuple โดยการสังเกต A [k] เช่นนั้นองค์ประกอบทั้งหมดจะอยู่ใน A [0] - A [k] เมื่ออาร์เรย์การค้นหาของเรามีขนาดใหญ่มากและ SUM เล็กมาก

[0] น้อยที่สุด: - เรียงลำดับจากน้อยไปมาก

s = 2A [0] + A [k]: รับ s และ A [] เราสามารถหา A [k] โดยใช้การค้นหาแบบไบนารีในเวลา log (n)


0

นี่คือโปรแกรมใน java ซึ่งก็คือ O (N ^ 2)

import java.util.Stack;


public class GetTripletPair {

    /** Set a value for target sum */
    public static final int TARGET_SUM = 32;

    private Stack<Integer> stack = new Stack<Integer>();

    /** Store the sum of current elements stored in stack */
    private int sumInStack = 0;
    private int count =0 ;


    public void populateSubset(int[] data, int fromIndex, int endIndex) {

        /*
        * Check if sum of elements stored in Stack is equal to the expected
        * target sum.
        * 
        * If so, call print method to print the candidate satisfied result.
        */
        if (sumInStack == TARGET_SUM) {
            print(stack);
        }

        for (int currentIndex = fromIndex; currentIndex < endIndex; currentIndex++) {

            if (sumInStack + data[currentIndex] <= TARGET_SUM) {
                ++count;
                stack.push(data[currentIndex]);
                sumInStack += data[currentIndex];

                /*
                * Make the currentIndex +1, and then use recursion to proceed
                * further.
                */
                populateSubset(data, currentIndex + 1, endIndex);
                --count;
                sumInStack -= (Integer) stack.pop();
            }else{
            return;
        }
        }
    }

    /**
    * Print satisfied result. i.e. 15 = 4+6+5
    */

    private void print(Stack<Integer> stack) {
        StringBuilder sb = new StringBuilder();
        sb.append(TARGET_SUM).append(" = ");
        for (Integer i : stack) {
            sb.append(i).append("+");
        }
        System.out.println(sb.deleteCharAt(sb.length() - 1).toString());
    }

    private static final int[] DATA = {4,13,14,15,17};

    public static void main(String[] args) {
        GetAllSubsetByStack get = new GetAllSubsetByStack();
        get.populateSubset(DATA, 0, DATA.length);
    }
}

วิธีการที่ดี แต่ฉันไม่สามารถหาจุดที่คุณ จำกัด จำนวนผลลัพธ์ไว้ที่ triplet ตัวอย่างเช่นพิจารณาอินพุต: [1,11,3,4,5,6,7,8, 2] และผลรวม 12 จากโซลูชันของคุณปรากฏว่า [1, 11] [4,8] [1,4, 5,2] และอื่น ๆ ทั้งหมดจะทำงาน
Anupam Saini

0

ปัญหาสามารถแก้ไขได้ใน O (n ^ 2) โดยการขยายปัญหา 2 ผลรวมด้วยการแก้ไขเล็กน้อย A คือเวกเตอร์ที่มีองค์ประกอบและ B เป็นผลรวมที่ต้องการ

int Solution :: threeSumClosest (เวกเตอร์ & A, int B) {

sort(A.begin(),A.end());

int k=0,i,j,closest,val;int diff=INT_MAX;

while(k<A.size()-2)
{
    i=k+1;
    j=A.size()-1;

    while(i<j)
    {
        val=A[i]+A[j]+A[k];
        if(val==B) return B;
        if(abs(B-val)<diff)
        {
            diff=abs(B-val);
            closest=val;
        }
        if(B>val)
        ++i;
        if(B<val) 
        --j;
    }
    ++k;

}
return closest;

0

นี่คือรหัส Python3

class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        result = set()
        nums.sort()
        L = len(nums)     
        for i in range(L):
            if i > 0 and nums[i] == nums[i-1]:
                continue
            for j in range(i+1,L):
                if j > i + 1 and nums[j] == nums[j-1]:
                    continue  
                l = j+1
                r = L -1
                while l <= r:
                    sum = nums[i] + nums[j] + nums[l]
                    result.add(sum)
                    l = l + 1
                    while l<=r and nums[l] == nums[l-1]:
                        l = l + 1
        result = list(result)
        min = result[0]
        for i in range(1,len(result)):
            if abs(target - result[i]) < abs(target - min):
                min = result[i]
        return min

-1

โซลูชันอื่นที่ตรวจสอบและล้มเหลวก่อนกำหนด:

public boolean solution(int[] input) {
        int length = input.length;

        if (length < 3) {
            return false;
        }

        // x + y + z = 0  => -z = x + y
        final Set<Integer> z = new HashSet<>(length);
        int zeroCounter = 0, sum; // if they're more than 3 zeros we're done

        for (int element : input) {
            if (element < 0) {
                z.add(element);
            }

            if (element == 0) {
                ++zeroCounter;
                if (zeroCounter >= 3) {
                    return true;
                }
            }
        }

        if (z.isEmpty() || z.size() == length || (z.size() + zeroCounter == length)) {
            return false;
        } else {
            for (int x = 0; x < length; ++x) {
                for (int y = x + 1; y < length; ++y) {
                    sum = input[x] + input[y]; // will use it as inverse addition
                    if (sum < 0) {
                        continue;
                    }
                    if (z.contains(sum * -1)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

ฉันจะเพิ่มการทดสอบหน่วยบางอย่างที่นี่: GivenArrayReturnTrueIfThreeElementsSumZeroTest

หากตั้งจะใช้พื้นที่มากเกินไปฉันได้อย่างง่ายดายสามารถใช้ java.util.BitSet ที่จะใช้ O (n / w) พื้นที่


-1

โปรแกรมรับองค์ประกอบทั้งสามนี้ ฉันเพิ่งเรียงลำดับอาร์เรย์ / รายการก่อนและพวกเขาปรับปรุงminClosenessตาม triplet แต่ละ

public int[] threeSumClosest(ArrayList<Integer> A, int B) {
    Collections.sort(A);
    int ansSum = 0;
    int ans[] = new int[3];
    int minCloseness = Integer.MAX_VALUE;
    for (int i = 0; i < A.size()-2; i++){
        int j = i+1;
        int k = A.size()-1;
        while (j < k){
            int sum = A.get(i) + A.get(j) + A.get(k);
            if (sum < B){
                j++;
            }else{
                k--;
            }
            if (minCloseness >  Math.abs(sum - B)){
                minCloseness = Math.abs(sum - B);
                ans[0] = A.get(i); ans[1] = A.get(j); ans[2] = A.get(k);
            }
        }
    }
    return ans;
}

-2

ฉันทำสิ่งนี้ใน n ^ 3, รหัสเทียมของฉันอยู่ด้านล่าง;

// สร้าง hashMap ที่มีคีย์เป็นจำนวนเต็มและค่าเป็น ArrayList // ทำซ้ำผ่านรายการโดยใช้ for for loop สำหรับแต่ละค่าในรายการวนซ้ำอีกครั้งโดยเริ่มจากค่าถัดไป

for (int i=0; i<=arr.length-1 ; i++){
    for (int j=i+1; j<=arr.length-1;j++){

// ถ้าผลรวมของ arr [i] และ arr [j] นั้นน้อยกว่าผลรวมที่ต้องการนั่นก็มีความเป็นไปได้ที่จะหาหลักที่สาม

      if (arr[i]+arr[j] < sum){
        for (int k= j+1; k<=arr.length-1;k++)

// ในกรณีนี้เรากำลังมองหาค่าที่สาม; ถ้าผลรวมของ arr [i] และ arr [j] และ arr [k] เป็นผลรวมที่ต้องการจากนั้นเพิ่มค่าเหล่านี้ลงใน HashMap โดยสร้าง arr [i] คีย์แล้วเพิ่ม arr [j] และ arr [k] เป็น ArrayList ในค่าของคีย์นั้น

          if (arr[i]+arr[j]+arr[k] ==  sum){              
              map.put(arr[i],new ArrayList<Integer>());
              map.get(arr[i]).add(arr[j]);
              map.get(arr[i]).add(arr[k]);}

หลังจากนี้คุณมีพจนานุกรมที่มีรายการทั้งหมดที่แสดงถึงสามค่าที่เพิ่มเข้ากับผลรวมที่ต้องการ แยกรายการเหล่านี้ทั้งหมดโดยใช้ฟังก์ชั่น HashMap มันทำงานได้อย่างสมบูรณ์แบบ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.