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


107

นี่คือคำถามการบ้าน พวกเขาบอกว่ามันจะใช้เวลาO(logN + logM)ที่NและMมีความยาวอาร์เรย์

มาตั้งชื่ออาร์เรย์aและb. เห็นได้ชัดว่าเราสามารถเพิกเฉยได้ทั้งหมดa[i]และb[i]ที่ไหน i> k
แรกให้เปรียบเทียบและa[k/2] b[k/2]ให้b[k/2]> a[k/2]. ดังนั้นเราสามารถทิ้งทั้งหมดได้b[i]โดยที่ i> k / 2

ตอนนี้เรามีทั้งหมดa[i]แล้วโดยที่ i <k และทั้งหมดb[i]ที่ฉัน <k / 2 เพื่อค้นหาคำตอบ

ขั้นตอนต่อไปคืออะไร?


6
ขั้นตอนทั้งหมดนี้รวมอยู่ในงานหรือไม่หรือขั้นตอนข้างต้นเป็นจุดเริ่มต้นของอัลกอริทึมของคุณ
Kendrick

19
ขั้นตอนข้างต้นเป็นของฉัน
Michael

เป็นO(logN + logM)เพียงหมายถึงเวลาที่ใช้ในการหาองค์ประกอบ KTH หรือไม่ สามารถทำการประมวลผลล่วงหน้ากับสหภาพก่อนได้หรือไม่?
David Weiser

1
@ เดวิด. ไม่คาดว่าจะมีการประมวลผลล่วงหน้า
Michael

3
อนุญาตให้มีการทำซ้ำในอาร์เรย์หรือไม่
David Weiser

คำตอบ:


49

คุณมีแล้วก็ทำต่อไป! และระวังดัชนี ...

เพื่อให้ง่ายขึ้นเล็กน้อยฉันจะถือว่า N และ M เป็น> k ดังนั้นความซับซ้อนในที่นี้คือ O (log k) ซึ่งก็คือ O (log N + log M)

รหัสหลอก:

i = k/2
j = k - i
step = k/4
while step > 0
    if a[i-1] > b[j-1]
        i -= step
        j += step
    else
        i += step
        j -= step
    step /= 2

if a[i-1] > b[j-1]
    return a[i-1]
else
    return b[j-1]

สำหรับการสาธิตคุณสามารถใช้ loop invariant i + j = k แต่ฉันจะไม่ทำการบ้านทั้งหมด :)


14
นี่ไม่ใช่ข้อพิสูจน์ที่แท้จริง แต่แนวคิดเบื้องหลังอัลกอริทึมคือเรารักษา i + j = k และค้นหา i และ j ดังกล่าวเพื่อให้ a [i-1] <b [j-1] <a [i] ( หรืออีกทางหนึ่ง) ตอนนี้เนื่องจากมีองค์ประกอบ i ใน 'a' เล็กกว่า b [j-1] และองค์ประกอบ j-1 ใน 'b' เล็กกว่า b [j-1], b [j-1] จึงเป็น i + j-1 + 1 = kth องค์ประกอบที่เล็กที่สุด ในการค้นหาเช่นนั้น j อัลกอริทึมจะทำการค้นหาแบบไดโนโทมิคบนอาร์เรย์ มีเหตุผล?
Jules Olléon

8
O (log k) เป็น O (log n + log m) ได้อย่างไร?
Rajendra Uppal

7
สิ่งนี้จะไม่ได้ผลหากค่าทั้งหมดในอาร์เรย์ 1 มาก่อนค่าในอาร์เรย์ 2
John Kurlak

3
ทำไมคุณถึงใช้ k / 4 เป็นขั้นตอนในตอนแรก?
Maggie

2
ดังที่ @JohnKurlak กล่าวว่ามันใช้ไม่ได้กับค่าที่ทั้ง a เล็กกว่า b ดูrepl.it/HMYf/0
Jeremy S.

71

ฉันหวังว่าฉันจะไม่ตอบการบ้านของคุณเพราะเป็นเวลานานกว่าหนึ่งปีแล้วที่คำถามนี้ถูกถาม นี่คือวิธีแก้ปัญหาหางซ้ำที่จะใช้เวลาบันทึก (len (a) + len (b))

สมมติฐาน: อินพุตถูกต้อง เช่น k อยู่ในช่วง [0, len (a) + len (b)]

กรณีฐาน:

  • ถ้าความยาวของอาร์เรย์หนึ่งเป็น 0 คำตอบคือองค์ประกอบ kth ของอาร์เรย์ที่สอง

ขั้นตอนการลด:

  • หากดัชนีกลางของa+ ดัชนีกลางของbมีค่าน้อยกว่าk
    • หากองค์ประกอบกลางของaมากกว่าองค์ประกอบกลางของbเราสามารถเพิกเฉยต่อครึ่งแรกของbปรับkได้
    • อื่นละเว้นครึ่งแรกของปีปรับak
  • ถ้าkน้อยกว่าผลรวมของดัชนีกลางของaและb:
    • หากองค์ประกอบกลางaมากกว่าองค์ประกอบกลางbเราสามารถเพิกเฉยต่อครึ่งหลังของa
    • มิฉะนั้นเราสามารถเพิกเฉยต่อครึ่งหลังของ b

รหัส:

def kthlargest(arr1, arr2, k):
    if len(arr1) == 0:
        return arr2[k]
    elif len(arr2) == 0:
        return arr1[k]

    mida1 = len(arr1)/2
    mida2 = len(arr2)/2
    if mida1+mida2<k:
        if arr1[mida1]>arr2[mida2]:
            return kthlargest(arr1, arr2[mida2+1:], k-mida2-1)
        else:
            return kthlargest(arr1[mida1+1:], arr2, k-mida1-1)
    else:
        if arr1[mida1]>arr2[mida2]:
            return kthlargest(arr1[:mida1], arr2, k)
        else:
            return kthlargest(arr1, arr2[:mida2], k)

โปรดทราบว่าโซลูชันของฉันกำลังสร้างสำเนาใหม่ของอาร์เรย์ขนาดเล็กในทุก ๆ การโทรซึ่งสามารถกำจัดได้อย่างง่ายดายโดยส่งเฉพาะดัชนีเริ่มต้นและสิ้นสุดในอาร์เรย์เดิม


4
ทำไมคุณเรียกมันว่าkthlargest()มันจะกลับ(k+1)องค์ประกอบ -th เล็กที่สุดเช่น1เป็นองค์ประกอบที่เล็กที่สุดเป็นอันดับสองในเช่นผลตอบแทนการทำงานของคุณ0,1,2,3 sorted(a+b)[k]
jfs

2
ฉันได้แปลงรหัสของคุณเป็น C ++แล้ว ดูเหมือนว่าจะได้ผล
jfs

1
คุณช่วยอธิบายได้ไหมว่าทำไมการเปรียบเทียบผลรวมของดัชนีกลางของ a และ b กับ k จึงสำคัญ
Maggie

3
ในขั้นตอนการลดสิ่งสำคัญคือต้องกำจัดองค์ประกอบจำนวนหนึ่งในอาร์เรย์หนึ่งตามสัดส่วนกับความยาวเพื่อให้ลอการิทึมรันไทม์ (เรากำลังกำจัดครึ่งหนึ่ง) ในการทำเช่นนั้นเราต้องเลือกหนึ่งอาร์เรย์ซึ่งหนึ่งในครึ่งหนึ่งที่เราสามารถละเว้นได้อย่างปลอดภัย เราจะทำอย่างนั้นได้อย่างไร? การกำจัดครึ่งหนึ่งอย่างมั่นใจเรารู้แน่นอนว่าจะไม่มีองค์ประกอบ kth
lambdapilgrim

1
การเปรียบเทียบ k กับผลรวมของครึ่งความยาวของอาร์เรย์ทำให้เราทราบข้อมูลว่าครึ่งหนึ่งของอาร์เรย์ใดที่สามารถกำจัดได้ ถ้า k มีค่ามากกว่าผลรวมของครึ่งความยาวเราจะรู้ว่าครึ่งแรกของอาร์เรย์หนึ่งสามารถกำจัดได้ ตรงข้ามถ้า k เล็กกว่า โปรดทราบว่าเราไม่สามารถกำจัดครึ่งหนึ่งจากแต่ละอาร์เรย์พร้อมกันได้ สำหรับการตัดสินใจว่าจะกำจัดอาร์เรย์ครึ่งใดเราใช้ประโยชน์จากข้อเท็จจริงที่ว่าอาร์เรย์ทั้งสองถูกจัดเรียงดังนั้นหาก k มีค่ามากกว่าผลรวมของครึ่งความยาวเราสามารถกำจัดครึ่งแรกของอาร์เรย์ที่องค์ประกอบตรงกลางมีขนาดเล็กกว่า สององค์ประกอบตรงกลาง ในทางกลับกัน
lambdapilgrim

36

หลายคนตอบคำถาม "องค์ประกอบที่เล็กที่สุด kth จากอาร์เรย์ที่เรียงลำดับสองรายการ" นี้ แต่โดยปกติจะใช้เพียงแนวคิดทั่วไปไม่ใช่รหัสการทำงานที่ชัดเจนหรือการวิเคราะห์เงื่อนไขขอบเขต

ที่นี่ฉันต้องการอธิบายอย่างละเอียดเกี่ยวกับวิธีที่ฉันทำเพื่อช่วยให้สามเณรบางคนเข้าใจด้วยรหัส Java ที่ใช้งานได้ถูกต้อง A1และA2เป็นอาร์เรย์จากน้อยไปหามากสองชุดโดยมีsize1และsize2ตามความยาวตามลำดับ เราต้องหาองค์ประกอบที่เล็กที่สุดเป็นลำดับที่ k จากการรวมกันของอาร์เรย์ทั้งสอง ในที่นี้เราคิดอย่างสมเหตุสมผล(k > 0 && k <= size1 + size2)ซึ่งหมายความว่าA1และA2ต้องไม่ว่างเปล่า

ขั้นแรกให้เข้าใกล้คำถามนี้ด้วยอัลกอริทึม O (k) ที่ช้า วิธีการคือการเปรียบเทียบองค์ประกอบแรกของอาร์เรย์ทั้งสองและA1[0] A2[0]เอาอันที่เล็กกว่าพูดA1[0]ใส่กระเป๋าเรา จากนั้นเปรียบเทียบA1[1]กับA2[0]และอื่น ๆ ทำซ้ำการกระทำนี้จนกว่ากระเป๋าของเราจะถึงkองค์ประกอบ สำคัญมาก: ในขั้นตอนแรกเราสามารถทุ่มเทA1[0]ในกระเป๋าของเราเท่านั้น เราไม่สามารถรวมหรือยกเว้นA2[0]!!!

รหัส O (k) ต่อไปนี้ให้องค์ประกอบหนึ่งก่อนคำตอบที่ถูกต้อง ที่นี่ฉันใช้มันเพื่อแสดงความคิดของฉันและการวิเคราะห์เงื่อนไขขอบเขต ฉันมีรหัสที่ถูกต้องหลังจากนี้:

private E kthSmallestSlowWithFault(int k) {
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0;
    // base case, k == 1
    if (k == 1) {
        if (size1 == 0) {
            return A2[index2];
        } else if (size2 == 0) {
            return A1[index1];
        } else if (A1[index1].compareTo(A2[index2]) < 0) {
            return A1[index1];
        } else {
            return A2[index2];
        }
    }

    /* in the next loop, we always assume there is one next element to compare with, so we can
     * commit to the smaller one. What if the last element is the kth one?
     */
    if (k == size1 + size2) {
        if (size1 == 0) {
            return A2[size2 - 1];
        } else if (size2 == 0) {
            return A1[size1 - 1];
        } else if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) {
            return A1[size1 - 1];
        } else {
            return A2[size2 - 1];
        }
    }

    /*
     * only when k > 1, below loop will execute. In each loop, we commit to one element, till we
     * reach (index1 + index2 == k - 1) case. But the answer is not correct, always one element
     * ahead, because we didn't merge base case function into this loop yet.
     */
    int lastElementFromArray = 0;
    while (index1 + index2 < k - 1) {
        if (A1[index1].compareTo(A2[index2]) < 0) {
            index1++;
            lastElementFromArray = 1;
            // commit to one element from array A1, but that element is at (index1 - 1)!!!
        } else {
            index2++;
            lastElementFromArray = 2;
        }
    }
    if (lastElementFromArray == 1) {
        return A1[index1 - 1];
    } else {
        return A2[index2 - 1];
    }
}

แนวคิดที่ทรงพลังที่สุดคือในแต่ละลูปเราจะใช้แนวทางกรณีฐานเสมอ หลังจากมุ่งมั่นกับองค์ประกอบที่เล็กที่สุดในปัจจุบันเราจะเข้าใกล้เป้าหมายมากขึ้นหนึ่งขั้นนั่นคือองค์ประกอบที่เล็กที่สุดลำดับที่ k อย่ากระโดดเข้ามาตรงกลางและทำให้ตัวเองสับสนและหลงทาง!

โดยสังเกตกรณีฐานรหัสด้านบนk == 1, k == size1+size2และรวมกับสิ่งนั้นA1และA2ต้องไม่ว่างเปล่าทั้งคู่ เราสามารถเปลี่ยนตรรกะให้เป็นรูปแบบที่กระชับกว่านี้ได้

นี่คือรหัสการทำงานที่ช้า แต่ถูกต้อง:

private E kthSmallestSlow(int k) {
    // System.out.println("this is an O(k) speed algorithm, very concise");
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0;
    while (index1 + index2 < k - 1) {
        if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
            index1++; // here we commit to original index1 element, not the increment one!!!
        } else {
            index2++;
        }
    }
    // below is the (index1 + index2 == k - 1) base case
    // also eliminate the risk of referring to an element outside of index boundary
    if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
        return A1[index1];
    } else {
        return A2[index2];
    }
}

ตอนนี้เราสามารถลองใช้อัลกอริทึมที่เร็วขึ้นที่ O (log k) ในทำนองเดียวกันเปรียบเทียบA1[k/2]กับA2[k/2]; หากA1[k/2]มีขนาดเล็กกว่าองค์ประกอบทั้งหมดจากA1[0]ถึงA1[k/2]ควรอยู่ในกระเป๋าของเรา แนวคิดคือไม่เพียงแค่ผูกมัดกับองค์ประกอบเดียวในแต่ละลูป ขั้นตอนแรกประกอบด้วยk/2องค์ประกอบ อีกครั้งที่เราไม่สามารถรวมหรือไม่รวมA2[0]ไปA2[k/2]อยู่ดี ดังนั้นในขั้นแรกเราไม่สามารถไปมากกว่าk/2องค์ประกอบได้ สำหรับขั้นตอนที่สองเราไม่สามารถไปได้มากกว่าk/4องค์ประกอบ ...

หลังจากแต่ละขั้นตอนเราเข้าใกล้องค์ประกอบ k-th มากขึ้น ในขณะเดียวกันในแต่ละขั้นตอนได้รับการขนาดเล็กและขนาดเล็กจนกว่าจะถึงซึ่งเป็น(step == 1) (k-1 == index1+index2)จากนั้นเราสามารถอ้างถึงกรณีฐานที่เรียบง่ายและมีประสิทธิภาพอีกครั้ง

นี่คือรหัสที่ใช้งานได้ถูกต้อง:

private E kthSmallestFast(int k) {
    // System.out.println("this is an O(log k) speed algorithm with meaningful variables name");
    int size1 = A1.length, size2 = A2.length;

    int index1 = 0, index2 = 0, step = 0;
    while (index1 + index2 < k - 1) {
        step = (k - index1 - index2) / 2;
        int step1 = index1 + step;
        int step2 = index2 + step;
        if (size1 > step1 - 1
                && (size2 <= step2 - 1 || A1[step1 - 1].compareTo(A2[step2 - 1]) < 0)) {
            index1 = step1; // commit to element at index = step1 - 1
        } else {
            index2 = step2;
        }
    }
    // the base case of (index1 + index2 == k - 1)
    if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
        return A1[index1];
    } else {
        return A2[index2];
    }
}

บางคนอาจกังวลว่าถ้า(index1+index2)กระโดดข้าม k-1 ล่ะ? เราพลาดกรณีฐาน(k-1 == index1+index2)หรือไม่? นั่นเป็นไปไม่ได้ คุณสามารถบวก 0.5 + 0.25 + 0.125 ... และคุณจะไม่เกิน 1

แน่นอนว่ามันง่ายมากที่จะเปลี่ยนรหัสด้านบนให้เป็นอัลกอริทึมแบบเรียกซ้ำ:

private E kthSmallestFastRecur(int k, int index1, int index2, int size1, int size2) {
    // System.out.println("this is an O(log k) speed algorithm with meaningful variables name");

    // the base case of (index1 + index2 == k - 1)
    if (index1 + index2 == k - 1) {
        if (size1 > index1 && (size2 <= index2 || A1[index1].compareTo(A2[index2]) < 0)) {
            return A1[index1];
        } else {
            return A2[index2];
        }
    }

    int step = (k - index1 - index2) / 2;
    int step1 = index1 + step;
    int step2 = index2 + step;
    if (size1 > step1 - 1 && (size2 <= step2 - 1 || A1[step1 - 1].compareTo(A2[step2 - 1]) < 0)) {
        index1 = step1;
    } else {
        index2 = step2;
    }
    return kthSmallestFastRecur(k, index1, index2, size1, size2);
}

หวังว่าการวิเคราะห์ข้างต้นและโค้ด Java จะช่วยให้คุณเข้าใจ แต่อย่าลอกรหัสของฉันไปเป็นการบ้านนะ! ไชโย;)


1
ขอบคุณมากสำหรับคำอธิบายและคำตอบที่ดี +1 :)
Hengameh

ในรหัสแรกไม่ควรelse if (A1[size1 - 1].compareTo(A2[size2 - 1]) < 0) แทนelse if (A1[size1 - 1].compareTo(A2[size2 - 1]) > 0)? (ในรหัส kthSm maximumSlowWithFault)
Hengameh

ขอบคุณ @ เฟย. คำอธิบายที่ยอดเยี่ยม เป็นที่น่าประหลาดใจว่ามีคำตอบผิดมากมายที่แพร่กระจายไปทั่วอินเทอร์เน็ตเกี่ยวกับปัญหานี้ มันน่าประหลาดใจยิ่งกว่าที่คำตอบที่เธอยอมรับใน SO เกี่ยวกับคำถามนี้มักจะผิดเสมอ ดูเหมือนไม่มีใครสนใจที่จะทดสอบคำตอบ
Captain Fogetti

อาจจะตัดไปที่โซลูชัน O (k) หลังจากขั้นตอนบางอย่าง (กล่าวว่า 15) เนื่องจากช่วงขั้นตอนลดลงค่อนข้างเร็ว
ท้องฟ้า

1
ไม่มีการเรียกแบบเรียกซ้ำขนาด A1 หรือ A2 จะลดลง
Aditya Joshee

5

นี่คือโซลูชันของ@ lambdapilgrimรุ่น C ++ ซ้ำ(ดูคำอธิบายของอัลกอริทึมที่นั่น):

#include <cassert>
#include <iterator>

template<class RandomAccessIterator, class Compare>
typename std::iterator_traits<RandomAccessIterator>::value_type
nsmallest_iter(RandomAccessIterator firsta, RandomAccessIterator lasta,
               RandomAccessIterator firstb, RandomAccessIterator lastb,
               size_t n,
               Compare less) {
  assert(issorted(firsta, lasta, less) && issorted(firstb, lastb, less));
  for ( ; ; ) {
    assert(n < static_cast<size_t>((lasta - firsta) + (lastb - firstb)));
    if (firsta == lasta) return *(firstb + n);
    if (firstb == lastb) return *(firsta + n);

    size_t mida = (lasta - firsta) / 2;
    size_t midb = (lastb - firstb) / 2;
    if ((mida + midb) < n) {
      if (less(*(firstb + midb), *(firsta + mida))) {
        firstb += (midb + 1);
        n -= (midb + 1);
      }
      else {
        firsta += (mida + 1);
        n -= (mida + 1);
      }
    }
    else {
      if (less(*(firstb + midb), *(firsta + mida)))
        lasta = (firsta + mida);
      else
        lastb = (firstb + midb);
    }
  }
}

ใช้ได้กับ0 <= n < (size(a) + size(b))ดัชนีทั้งหมดและมีO(log(size(a)) + log(size(b)))ความซับซ้อน

ตัวอย่าง

#include <functional> // greater<>
#include <iostream>

#define SIZE(a) (sizeof(a) / sizeof(*a))

int main() {
  int a[] = {5,4,3};
  int b[] = {2,1,0};
  int k = 1; // find minimum value, the 1st smallest value in a,b

  int i = k - 1; // convert to zero-based indexing
  int v = nsmallest_iter(a, a + SIZE(a), b, b + SIZE(b),
                         SIZE(a)+SIZE(b)-1-i, std::greater<int>());
  std::cout << v << std::endl; // -> 0
  return v;
}

4

ความพยายามของฉันสำหรับหมายเลข k แรกหมายเลข k ในอาร์เรย์ที่เรียงลำดับ 2 อาร์เรย์และในอาร์เรย์ที่เรียงลำดับ n:

// require() is recognizable by node.js but not by browser;
// for running/debugging in browser, put utils.js and this file in <script> elements,
if (typeof require === "function") require("./utils.js");

// Find K largest numbers in two sorted arrays.
function k_largest(a, b, c, k) {
    var sa = a.length;
    var sb = b.length;
    if (sa + sb < k) return -1;
    var i = 0;
    var j = sa - 1;
    var m = sb - 1;
    while (i < k && j >= 0 && m >= 0) {
        if (a[j] > b[m]) {
            c[i] = a[j];
            i++;
            j--;
        } else {
            c[i] = b[m];
            i++;
            m--;
        }
    }
    debug.log(2, "i: "+ i + ", j: " + j + ", m: " + m);
    if (i === k) {
        return 0;
    } else if (j < 0) {
        while (i < k) {
            c[i++] = b[m--];
        }
    } else {
        while (i < k) c[i++] = a[j--];
    }
    return 0;
}

// find k-th largest or smallest number in 2 sorted arrays.
function kth(a, b, kd, dir){
    sa = a.length; sb = b.length;
    if (kd<1 || sa+sb < kd){
        throw "Mission Impossible! I quit!";
    }

    var k;
    //finding the kd_th largest == finding the smallest k_th;
    if (dir === 1){ k = kd;
    } else if (dir === -1){ k = sa + sb - kd + 1;}
    else throw "Direction has to be 1 (smallest) or -1 (largest).";

    return find_kth(a, b, k, sa-1, 0, sb-1, 0);
}

// find k-th smallest number in 2 sorted arrays;
function find_kth(c, d, k, cmax, cmin, dmax, dmin){

    sc = cmax-cmin+1; sd = dmax-dmin+1; k0 = k; cmin0 = cmin; dmin0 = dmin;
    debug.log(2, "=k: " + k +", sc: " + sc + ", cmax: " + cmax +", cmin: " + cmin + ", sd: " + sd +", dmax: " + dmax + ", dmin: " + dmin);

    c_comp = k0-sc;
    if (c_comp <= 0){
        cmax = cmin0 + k0-1;
    } else {
        dmin = dmin0 + c_comp-1;
        k -= c_comp-1;
    }

    d_comp = k0-sd;
    if (d_comp <= 0){
        dmax = dmin0 + k0-1;
    } else {
        cmin = cmin0 + d_comp-1;
        k -= d_comp-1;
    }
    sc = cmax-cmin+1; sd = dmax-dmin+1;

    debug.log(2, "#k: " + k +", sc: " + sc + ", cmax: " + cmax +", cmin: " + cmin + ", sd: " + sd +", dmax: " + dmax + ", dmin: " + dmin + ", c_comp: " + c_comp + ", d_comp: " + d_comp);

    if (k===1) return (c[cmin]<d[dmin] ? c[cmin] : d[dmin]);
    if (k === sc+sd) return (c[cmax]>d[dmax] ? c[cmax] : d[dmax]);

    m = Math.floor((cmax+cmin)/2);
    n = Math.floor((dmax+dmin)/2);

    debug.log(2, "m: " + m + ", n: "+n+", c[m]: "+c[m]+", d[n]: "+d[n]);

    if (c[m]<d[n]){
        if (m === cmax){ // only 1 element in c;
            return d[dmin+k-1];
        }

        k_next = k-(m-cmin+1);
        return find_kth(c, d, k_next, cmax, m+1, dmax, dmin);
    } else {
        if (n === dmax){
            return c[cmin+k-1];
        }

        k_next = k-(n-dmin+1);
        return find_kth(c, d, k_next, cmax, cmin, dmax, n+1);
    }
}

function traverse_at(a, ae, h, l, k, at, worker, wp){
    var n = ae ? ae.length : 0;
    var get_node;
    switch (at){
        case "k": get_node = function(idx){
                var node = {};
                var pos = l[idx] + Math.floor(k/n) - 1;
                if (pos<l[idx]){ node.pos = l[idx]; }
                else if (pos > h[idx]){ node.pos = h[idx];}
                else{ node.pos = pos; }

                node.idx = idx;
                node.val = a[idx][node.pos];
                debug.log(6, "pos: "+pos+"\nnode =");
                debug.log(6, node);
                return node;
            };
            break;
        case "l": get_node = function(idx){
                debug.log(6, "a["+idx+"][l["+idx+"]]: "+a[idx][l[idx]]);
                return a[idx][l[idx]];
            };
            break;
        case "h": get_node = function(idx){
                debug.log(6, "a["+idx+"][h["+idx+"]]: "+a[idx][h[idx]]);
                return a[idx][h[idx]];
            };
            break;
        case "s": get_node = function(idx){
                debug.log(6, "h["+idx+"]-l["+idx+"]+1: "+(h[idx] - l[idx] + 1));
                return h[idx] - l[idx] + 1;
            };
            break;
        default: get_node = function(){
                debug.log(1, "!!! Exception: get_node() returns null.");
                return null;
            };
            break;
    }

    worker.init();

    debug.log(6, "--* traverse_at() *--");

    var i;
    if (!wp){
        for (i=0; i<n; i++){
            worker.work(get_node(ae[i]));
        }    
    } else {
        for (i=0; i<n; i++){
            worker.work(get_node(ae[i]), wp);
        }
    }

    return worker.getResult();
}

sumKeeper = function(){
    var res = 0;
    return {
        init     : function(){ res = 0;},
        getResult: function(){
                debug.log(5, "@@ sumKeeper.getResult: returning: "+res);
                return res;
            },
        work     : function(node){ if (node!==null) res += node;}
    };
}();

maxPicker = function(){
    var res = null;
    return {
        init     : function(){ res = null;},
        getResult: function(){
                debug.log(5, "@@ maxPicker.getResult: returning: "+res);
                return res;
            },
        work     : function(node){
            if (res === null){ res = node;}
            else if (node!==null && node > res){ res = node;}
        }
    };    
}();

minPicker = function(){
    var res = null;
    return {
        init     : function(){ res = null;},
        getResult: function(){
                debug.log(5, "@@ minPicker.getResult: returning: ");
                debug.log(5, res);
                return res;
            },
        work     : function(node){
            if (res === null && node !== null){ res = node;}
            else if (node!==null &&
                node.val !==undefined &&
                node.val < res.val){ res = node; }
            else if (node!==null && node < res){ res = node;}
        }
    };  
}();

// find k-th smallest number in n sorted arrays;
// need to consider the case where some of the subarrays are taken out of the selection;
function kth_n(a, ae, k, h, l){
    var n = ae.length;
    debug.log(2, "------**  kth_n()  **-------");
    debug.log(2, "n: " +n+", k: " + k);
    debug.log(2, "ae: ["+ae+"],  len: "+ae.length);
    debug.log(2, "h: [" + h + "]");
    debug.log(2, "l: [" + l + "]");

    for (var i=0; i<n; i++){
        if (h[ae[i]]-l[ae[i]]+1>k) h[ae[i]]=l[ae[i]]+k-1;
    }
    debug.log(3, "--after reduction --");
    debug.log(3, "h: [" + h + "]");
    debug.log(3, "l: [" + l + "]");

    if (n === 1)
        return a[ae[0]][k-1]; 
    if (k === 1)
        return traverse_at(a, ae, h, l, k, "l", minPicker);
    if (k === traverse_at(a, ae, h, l, k, "s", sumKeeper))
        return traverse_at(a, ae, h, l, k, "h", maxPicker);

    var kn = traverse_at(a, ae, h, l, k, "k", minPicker);
    debug.log(3, "kn: ");
    debug.log(3, kn);

    var idx = kn.idx;
    debug.log(3, "last: k: "+k+", l["+kn.idx+"]: "+l[idx]);
    k -= kn.pos - l[idx] + 1;
    l[idx] = kn.pos + 1;
    debug.log(3, "next: "+"k: "+k+", l["+kn.idx+"]: "+l[idx]);
    if (h[idx]<l[idx]){ // all elements in a[idx] selected;
        //remove a[idx] from the arrays.
        debug.log(4, "All elements selected in a["+idx+"].");
        debug.log(5, "last ae: ["+ae+"]");
        ae.splice(ae.indexOf(idx), 1);
        h[idx] = l[idx] = "_"; // For display purpose only.
        debug.log(5, "next ae: ["+ae+"]");
    }

    return kth_n(a, ae, k, h, l);
}

function find_kth_in_arrays(a, k){

    if (!a || a.length<1 || k<1) throw "Mission Impossible!";

    var ae=[], h=[], l=[], n=0, s, ts=0;
    for (var i=0; i<a.length; i++){
        s = a[i] && a[i].length;
        if (s>0){
            ae.push(i); h.push(s-1); l.push(0);
            ts+=s;
        }
    }

    if (k>ts) throw "Too few elements to choose from!";

    return kth_n(a, ae, k, h, l);
}

/////////////////////////////////////////////////////
// tests
// To show everything: use 6.
debug.setLevel(1);

var a = [2, 3, 5, 7, 89, 223, 225, 667];
var b = [323, 555, 655, 673];
//var b = [99];
var c = [];

debug.log(1, "a = (len: " + a.length + ")");
debug.log(1, a);
debug.log(1, "b = (len: " + b.length + ")");
debug.log(1, b);

for (var k=1; k<a.length+b.length+1; k++){
    debug.log(1, "================== k: " + k + "=====================");

    if (k_largest(a, b, c, k) === 0 ){
      debug.log(1, "c = (len: "+c.length+")");
      debug.log(1, c);
    }

    try{
        result = kth(a, b, k, -1);
        debug.log(1, "===== The " + k + "-th largest number: " + result);
    } catch (e) {
        debug.log(0, "Error message from kth(): " + e);
    }
    debug.log("==================================================");
}

debug.log(1, "################# Now for the n sorted arrays ######################");
debug.log(1, "####################################################################");

x = [[1, 3, 5, 7, 9],
     [-2, 4, 6, 8, 10, 12],
     [8, 20, 33, 212, 310, 311, 623],
     [8],
     [0, 100, 700],
     [300],
     [],
     null];

debug.log(1, "x = (len: "+x.length+")");
debug.log(1, x);

for (var i=0, num=0; i<x.length; i++){
    if (x[i]!== null) num += x[i].length;
}
debug.log(1, "totoal number of elements: "+num);

// to test k in specific ranges:
var start = 0, end = 25;
for (k=start; k<end; k++){
    debug.log(1, "=========================== k: " + k + "===========================");

    try{
        result = find_kth_in_arrays(x, k);
        debug.log(1, "====== The " + k + "-th smallest number: " + result);
    } catch (e) {
        debug.log(1, "Error message from find_kth_in_arrays: " + e);
    }
    debug.log(1, "=================================================================");
}
debug.log(1, "x = (len: "+x.length+")");
debug.log(1, x);
debug.log(1, "totoal number of elements: "+num);

คุณสามารถดูโค้ดที่สมบูรณ์พร้อมเครื่องมือแก้จุดบกพร่องได้ที่: https://github.com/brainclone/teasers/tree/master/kth


3

นี่คือรหัสของฉันตามโซลูชันของ Jules Olleon:

int getNth(vector<int>& v1, vector<int>& v2, int n)
{
    int step = n / 4;

    int i1 = n / 2;
    int i2 = n - i1;

    while(!(v2[i2] >= v1[i1 - 1] && v1[i1] > v2[i2 - 1]))
    {                   
        if (v1[i1 - 1] >= v2[i2 - 1])
        {
            i1 -= step;
            i2 += step;
        }
        else
        {
            i1 += step;
            i2 -= step;
        }

        step /= 2;
        if (!step) step = 1;
    }

    if (v1[i1 - 1] >= v2[i2 - 1])
        return v1[i1 - 1];
    else
        return v2[i2 - 1];
}

int main()  
{  
    int a1[] = {1,2,3,4,5,6,7,8,9};
    int a2[] = {4,6,8,10,12};

    //int a1[] = {1,2,3,4,5,6,7,8,9};
    //int a2[] = {4,6,8,10,12};

    //int a1[] = {1,7,9,10,30};
    //int a2[] = {3,5,8,11};
    vector<int> v1(a1, a1+9);
    vector<int> v2(a2, a2+5);


    cout << getNth(v1, v2, 5);
    return 0;  
}  

1
วิธีนี้จะใช้ไม่ได้ในบางกรณี ตัวอย่างเช่น int a2 [] = {1,2,3,4, 5}; int a1 [] = {5,6,8,10,12}; getNth (a1, a2, 7) ดัชนีของอาร์เรย์จะออกนอกขอบเขต
Jay

2

นี่คือการใช้งานของฉันใน C คุณสามารถอ้างถึงคำอธิบายของ @Jules Olléonสำหรับอัลกอริทึม: แนวคิดเบื้องหลังอัลกอริทึมคือเรารักษา i + j = k และค้นหา i และ j ดังกล่าวเพื่อให้ a [i-1] <b [j-1] <a [i] (หรืออีกทางหนึ่ง) ตอนนี้เนื่องจากมีองค์ประกอบ i ใน 'a' เล็กกว่า b [j-1] และองค์ประกอบ j-1 ใน 'b' เล็กกว่า b [j-1], b [j-1] จึงเป็น i + j-1 + 1 = kth องค์ประกอบที่เล็กที่สุด หากต้องการค้นหา i ดังกล่าว j อัลกอริทึมจะทำการค้นหาแบบไดโนมิคบนอาร์เรย์

int find_k(int A[], int m, int B[], int n, int k) {
   if (m <= 0 )return B[k-1];
   else if (n <= 0) return A[k-1];
   int i =  ( m/double (m + n))  * (k-1);
   if (i < m-1 && i<k-1) ++i;
   int j = k - 1 - i;

   int Ai_1 = (i > 0) ? A[i-1] : INT_MIN, Ai = (i<m)?A[i]:INT_MAX;
   int Bj_1 = (j > 0) ? B[j-1] : INT_MIN, Bj = (j<n)?B[j]:INT_MAX;
   if (Ai >= Bj_1 && Ai <= Bj) {
       return Ai;
   } else if (Bj >= Ai_1 && Bj <= Ai) {
       return Bj;
   }
   if (Ai < Bj_1) { // the answer can't be within A[0,...,i]
       return find_k(A+i+1, m-i-1, B, n, j);
   } else { // the answer can't be within A[0,...,i]
       return find_k(A, m, B+j+1, n-j-1, i);
   }
 }

2

นี่คือทางออกของฉัน รหัส C ++ พิมพ์ค่าที่เล็กที่สุด kth รวมทั้งจำนวนการวนซ้ำเพื่อให้ได้ค่าที่น้อยที่สุด kth โดยใช้ลูปซึ่งในความคิดของฉันอยู่ในลำดับของบันทึก (k) อย่างไรก็ตามรหัสต้องการให้ k มีขนาดเล็กกว่าความยาวของอาร์เรย์แรกซึ่งเป็นข้อ จำกัด

#include <iostream>
#include <vector>
#include<math.h>
using namespace std;

template<typename comparable>
comparable kthSmallest(vector<comparable> & a, vector<comparable> & b, int k){

int idx1; // Index in the first array a
int idx2; // Index in the second array b
comparable maxVal, minValPlus;
float iter = k;
int numIterations = 0;

if(k > a.size()){ // Checks if k is larger than the size of first array
    cout << " k is larger than the first array" << endl;
    return -1;
}
else{ // If all conditions are satisfied, initialize the indexes
    idx1 = k - 1;
    idx2 = -1;
}

for ( ; ; ){
    numIterations ++;
    if(idx2 == -1 || b[idx2] <= a[idx1] ){
        maxVal = a[idx1];
        minValPlus = b[idx2 + 1];
        idx1 = idx1 - ceil(iter/2); // Binary search
        idx2 = k - idx1 - 2; // Ensures sum of indices  = k - 2
    }
    else{
        maxVal = b[idx2];
        minValPlus = a[idx1 + 1];
        idx2 = idx2 - ceil(iter/2); // Binary search
        idx1 = k - idx2 - 2; // Ensures sum of indices  = k - 2
    }
    if(minValPlus >= maxVal){ // Check if kth smallest value has been found
        cout << "The number of iterations to find the " << k << "(th) smallest value is    " << numIterations << endl;
        return maxVal;

    }
    else
        iter/=2; // Reduce search space of binary search
   }
}

int main(){
//Test Cases
    vector<int> a = {2, 4, 9, 15, 22, 34, 45, 55, 62, 67, 78, 85};
    vector<int> b = {1, 3, 6, 8, 11, 13, 15, 20, 56, 67, 89};
    // Input k < a.size()
    int kthSmallestVal;
    for (int k = 1; k <= a.size() ; k++){
        kthSmallestVal = kthSmallest<int>( a ,b ,k );
        cout << k <<" (th) smallest Value is " << kthSmallestVal << endl << endl << endl;
    }
}

1

รหัสหลอกแรกที่ให้ไว้ข้างต้นใช้ไม่ได้กับหลายค่า ตัวอย่างเช่นนี่คือสองอาร์เรย์ int [] ก = {1, 5, 6, 8, 9, 11, 15, 17, 19}; int [] b = {4, 7, 8, 13, 15, 18, 20, 24, 26};

มันใช้ไม่ได้กับ k = 3 และ k = 9 ในนั้น ฉันมีทางออกอื่น ได้รับด้านล่าง

private static void traverse(int pt, int len) {
int temp = 0;

if (len == 1) {
    int val = 0;
    while (k - (pt + 1) - 1 > -1 && M[pt] < N[k - (pt + 1) - 1]) {

    if (val == 0)
        val = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1]
            : M[pt];
    else {
        int t = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1]
            : M[pt];
        val = val < t ? val : t;

    }

    ++pt;
    }

    if (val == 0)
    val = M[pt] < N[k - (pt + 1) - 1] ? N[k - (pt + 1) - 1] : M[pt];

    System.out.println(val);
    return;
}

temp = len / 2;

if (M[pt + temp - 1] < N[k - (pt + temp) - 1]) {
    traverse(pt + temp, temp);

} else {
    traverse(pt, temp);
}

}

แต่ ... มันยังใช้ไม่ได้กับ k = 5 มีการจับ k คู่ / คี่ซึ่งไม่ปล่อยให้มันง่าย


1
public class KthSmallestInSortedArray {

    public static void main(String[] args) {
        int a1[] = {2, 3, 10, 11, 43, 56},
                a2[] = {120, 13, 14, 24, 34, 36},
                k = 4;

        System.out.println(findKthElement(a1, a2, k));

    }

    private static int findKthElement(int a1[], int a2[], int k) {

        /** Checking k must less than sum of length of both array **/
        if (a1.length + a2.length < k) {
            throw new IllegalArgumentException();
        }

        /** K must be greater than zero **/
        if (k <= 0) {
            throw new IllegalArgumentException();
        }

        /**
         * Finding begin, l and end such that
         * begin <= l < end
         * a1[0].....a1[l-1] and
         * a2[0]....a2[k-l-1] are the smallest k numbers
         */
        int begin = Math.max(0, k - a2.length);
        int end = Math.min(a1.length, k);

        while (begin < end) {
            int l = begin + (end - begin) / 2;

            /** Can we include a1[l] in the k smallest numbers */
            if ((l < a1.length) &&
                    (k - l > 0) &&
                    (a1[l] < a2[k - l - 1])) {

                begin = l + 1;

            } else if ((l > 0) &&
                    (k - l < a2.length) &&
                    (a1[l - 1] > a2[k - 1])) {

                /**
                 * This is the case where we can discard
                 * a[l-1] from the set of k smallest numbers
                 */
                end = l;

            } else {

                /**
                 * We found our answer since both inequalities were
                 * false
                 */
                begin = l;
                break;
            }
        }

        if (begin == 0) {
            return a2[k - 1];
        } else if (begin == k) {
            return a1[k - 1];
        } else {
            return Math.max(a1[begin - 1], a2[k - begin - 1]);
        }
    }
}

1

นี่คือทางออกของฉันใน java จะพยายามปรับให้เหมาะสมต่อไป

  public class FindKLargestTwoSortedArray {

    public static void main(String[] args) {
        int[] arr1 = { 10, 20, 40, 80 };
        int[] arr2 = { 15, 35, 50, 75 };

    FindKLargestTwoSortedArray(arr1, 0, arr1.length - 1, arr2, 0,
            arr2.length - 1, 6);
    }


    public static void FindKLargestTwoSortedArray(int[] arr1, int start1,
            int end1, int[] arr2, int start2, int end2, int k) {

        if ((start1 <= end1 && start1 >= 0 && end1 < arr1.length)
                && (start2 <= end2 && start2 >= 0 && end2 < arr2.length)) {

            int midIndex1 = (start1 + (k - 1) / 2);
            midIndex1 = midIndex1 >= arr1.length ? arr1.length - 1 : midIndex1;
            int midIndex2 = (start2 + (k - 1) / 2);
            midIndex2 = midIndex2 >= arr2.length ? arr2.length - 1 : midIndex2;


            if (arr1[midIndex1] == arr2[midIndex2]) {
                System.out.println("element is " + arr1[midIndex1]);
            } else if (arr1[midIndex1] < arr2[midIndex2]) {

                if (k == 1) {
                    System.out.println("element is " + arr1[midIndex1]);
                    return;
                } else if (k == 2) {
                    System.out.println("element is " + arr2[midIndex2]);
                    return;
                }else if (midIndex1 == arr1.length-1 || midIndex2 == arr2.length-1 ) {
                    if(k==(arr1.length+arr2.length)){
                    System.out.println("element is " + arr2[midIndex2]);
                    return;
                    }else if(k==(arr1.length+arr2.length)-1){
                        System.out.println("element is " + arr1[midIndex1]);
                        return;
                    }

                }

                int remainingElementToSearch = k - (midIndex1-start1);
                FindKLargestTwoSortedArray(
                        arr1,
                        midIndex1,
                        (midIndex1 + remainingElementToSearch) >= arr1.length ? arr1.length-1
                                : (midIndex1 + remainingElementToSearch), arr2,
                        start2, midIndex2, remainingElementToSearch);

            } else if (arr1[midIndex1] > arr2[midIndex2]) {
                FindKLargestTwoSortedArray(arr2, start2, end2, arr1, start1,
                        end1, k);
            }

        } else {
            return;
        }

    }
}

สิ่งนี้ได้รับแรงบันดาลใจจาก Algo ที่วิดีโอ youtubeที่ยอดเยี่ยม


1

เชื่อมโยงกับความซับซ้อนของรหัส (log (n) + log (m))

ลิงค์ไปยัง Code (log (n) * log (m))

การใช้งานโซลูชัน (log (n) + log (m))

ฉันต้องการเพิ่มคำอธิบายของฉันในปัญหา นี่เป็นปัญหาคลาสสิกที่เราต้องใช้ข้อเท็จจริงที่ว่าอาร์เรย์ทั้งสองถูกจัดเรียง เราได้รับอาร์เรย์ที่เรียงลำดับสองอาร์เรย์ arr1 ขนาด sz1 และ arr2 ของขนาด sz2

ก) สมมติว่าถ้า

กำลังตรวจสอบว่า k ถูกต้องหรือไม่

k คือ> (sz1 + sz2)

จากนั้นเราไม่พบองค์ประกอบที่เล็กที่สุด kth ในการรวมกันของอาร์เรย์ที่เรียงลำดับทั้งสอง ryt ดังนั้นส่งคืนข้อมูลที่ไม่ถูกต้อง b) ตอนนี้ถ้าเงื่อนไขข้างต้นเป็นเท็จและเรามีค่า k ที่ถูกต้องและเป็นไปได้

การจัดการเคส Edge

เราจะต่อท้ายอาร์เรย์ทั้งสองด้วยค่า -infinity ที่ด้านหน้าและ + ค่าอินฟินิตี้ในตอนท้ายเพื่อให้ครอบคลุมกรณีขอบของ k = 1,2 และ k = (sz1 + sz2-1), (sz1 + sz2) เป็นต้น

ตอนนี้อาร์เรย์ทั้งสองมีขนาด(sz1 + 2) และ(sz2 + 2)ตามลำดับ

อัลกอริทึมหลัก

ตอนนี้เราจะทำการค้นหาไบนารีใน arr1 เราจะทำการค้นหาไบนารีใน arr1 เพื่อค้นหาดัชนี i, startIndex <= i <= endIndex

เช่นนั้นหากเราพบดัชนีที่สอดคล้องกัน j ใน arr2 โดยใช้ข้อ จำกัด {(i + j) = k} แล้วถ้า

ถ้า(arr2 [j-1] <arr1 [i] <arr2 [j])ดังนั้น arr1 [i] คือ kth ที่เล็กที่สุด (กรณีที่ 1)

อื่นถ้า(arr1 [i-1] <arr2 [j] <arr1 [i])ดังนั้น arr2 [i] คือ kth ที่เล็กที่สุด (กรณีที่ 2)

อื่นหมายถึงarr1 [i] <arr2 [j-1] <arr2 [j] (Case3)

หรือarr2 [j-1] <arr2 [j] <arr1 [i] (Case4)

เนื่องจากเรารู้ว่าองค์ประกอบที่เล็กที่สุด kth มีองค์ประกอบ (k-1) ที่เล็กกว่าในการรวมกันของทั้งสองอาร์เรย์ไรท์? ดังนั้น,

ในกรณีที่ 1 สิ่งที่เราทำเรามั่นใจว่ามีองค์ประกอบที่เล็กกว่า (k-1) ทั้งหมดถึง arr1 [i] เนื่องจากองค์ประกอบที่มีขนาดเล็กกว่า arr1 [i] ในอาร์เรย์ arr1 มีจำนวน i-1 มากกว่าที่เรารู้ (arr2 [ j-1] <arr1 [i] <arr2 [j]) และจำนวนองค์ประกอบที่เล็กกว่า arr1 [i] ใน arr2 คือ j-1 เนื่องจากพบ j โดยใช้ (i-1) + (j-1) = (k -1) ดังนั้นองค์ประกอบที่เล็กที่สุด kth จะเป็น arr1 [i]

แต่คำตอบอาจไม่ได้มาจากอาร์เรย์แรกเช่น arr1 เสมอไปดังนั้นเราจึงตรวจสอบกรณีที่ 2 ซึ่งตรงกับกรณีที่ 1 เช่นกันเพราะ (i-1) + (j-1) = (k-1) ทีนี้ถ้าเรามี (arr1 [i-1] <arr2 [j] <arr1 [i]) เรามีองค์ประกอบ k-1 ทั้งหมดที่มีขนาดเล็กกว่า arr2 [j] ในการรวมกันของทั้งสองอาร์เรย์ดังนั้นมันจึงเป็นองค์ประกอบที่เล็กที่สุด kth

ในกรณีที่ 3ในการสร้างเป็นกรณีที่ 1 หรือกรณีที่ 2 เราจำเป็นต้องเพิ่ม i และ j จะพบตามข้อ จำกัด {(i + j) = k} เช่นในการค้นหาแบบไบนารีย้ายไปทางขวาเช่นทำให้ startIndex = middleIndex

ในcase4ในรูปแบบมัน ๆ ของกรณีที่ 1 หรือกรณีที่ 2 เราต้องพร่อง i และ j จะพบตามการใช้ข้อ จำกัด {(i + J) = k} เช่นในการย้ายการค้นหาไบนารีส่วนด้านซ้ายคือทำให้ endIndex = middleIndex .

ตอนนี้วิธีการตัดสินใจ startIndex และ endIndex ที่จุดเริ่มต้นของการค้นหาไบนารีใน arr1 ด้วย startindex = 1 และ endIndex = ?? เราจำเป็นต้องตัดสินใจ

ถ้า k> sz1, endIndex = (sz1 + 1), อื่น endIndex = k;

เนื่องจากถ้า k มากกว่าขนาดของอาร์เรย์แรกเราอาจต้องทำการค้นหาไบนารีในอาร์เรย์ทั้งหมด arr1 อื่น ๆ เราจำเป็นต้องใช้องค์ประกอบ k แรกของมันเท่านั้นเนื่องจากองค์ประกอบ sz1-k ไม่สามารถมีส่วนช่วยในการคำนวณ kth ที่เล็กที่สุด

รหัสที่แสดงด้านล่าง

// Complexity    O(log(n)+log(m))

#include<bits/stdc++.h>
using namespace std;
#define f(i,x,y) for(int i = (x);i < (y);++i)
#define F(i,x,y) for(int i = (x);i > (y);--i)
int max(int a,int b){return (a > b?a:b);}
int min(int a,int b){return (a < b?a:b);}
int mod(int a){return (a > 0?a:((-1)*(a)));}
#define INF 1000000




int func(int *arr1,int *arr2,int sz1,int sz2,int k)

{

if((k <= (sz1+sz2))&&(k > 0))

{
int s = 1,e,i,j;
if(k > sz1)e = sz1+1;
else e = k;
while((e-s)>1)
{
  i = (e+s)/2;
  j = ((k-1)-(i-1)); 
  j++;
  if(j > (sz2+1)){s = i;}
  else if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
  else if((arr2[j] >= arr1[i-1])&&(arr2[j] <= arr1[i]))return arr2[j];
  else if(arr1[i] < arr2[j-1]){s = i;}
  else if(arr1[i] > arr2[j]){e = i;}
  else {;}
}
i = e,j = ((k-1)-(i-1));j++;
if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
else if((arr2[j] >= arr1[i-1])&&(arr2[j] <= arr1[i]))return arr2[j];
else
{
  i = s,j = ((k-1)-(i-1));j++;
  if((arr1[i] >= arr2[j-1])&&(arr1[i] <= arr2[j]))return arr1[i];
  else return arr2[j];
}

  }

 else

{
cout << "Data Invalid" << endl;
return -INF;

}

}





int main()

{
int n,m,k;
cin >> n >> m >> k;
int arr1[n+2];
int arr2[m+2];
f(i,1,n+1)
cin >> arr1[i];
f(i,1,m+1)
cin >> arr2[i];
arr1[0] = -INF;
arr2[0] = -INF;
  arr1[n+1] = +INF;  
arr2[m+1] = +INF; 
int val = func(arr1,arr2,n,m,k);
if(val != -INF)cout << val << endl;   
return 0;

}

สำหรับการแก้ปัญหาความซับซ้อน (log (n) * log (m))

ฉันพลาดการใช้ประโยชน์จากข้อเท็จจริงที่ว่าสำหรับ i แต่ละตัวสามารถพบ j ได้โดยใช้ข้อ จำกัด {(i-1) + (j-1) = (k-1)} ดังนั้นสำหรับแต่ละ ii จึงใช้การค้นหาไบนารีเพิ่มเติมในอาร์เรย์ที่สอง เพื่อหา j เช่นนั้น arr2 [j] <= arr1 [i] ดังนั้นโซลูชันนี้จึงสามารถปรับให้เหมาะสมเพิ่มเติม


1

โดยทั่วไปด้วยวิธีนี้คุณสามารถทิ้งองค์ประกอบ k / 2 ในแต่ละขั้นตอนได้ K จะเปลี่ยนซ้ำจาก k => k / 2 => k / 4 => ... จนมาถึง 1 ดังนั้นความซับซ้อนของเวลาคือO (logk)

ที่ k = 1 เราจะได้ค่าต่ำสุดของสองอาร์เรย์

รหัสต่อไปนี้อยู่ใน JAVA โปรดทราบว่าเรากำลังลบ 1 (-1) ในโค้ดออกจากดัชนีเนื่องจากดัชนีของอาร์เรย์ Java เริ่มต้นจาก 0 และไม่ใช่ 1 เช่น k = 3 แสดงโดยองค์ประกอบในดัชนีที่ 2 ของอาร์เรย์

private int kthElement(int[] arr1, int[] arr2, int k) {
        if (k < 1 || k > (arr1.length + arr2.length))
            return -1;
        return helper(arr1, 0, arr1.length - 1, arr2, 0, arr2.length - 1, k);
    }


private int helper(int[] arr1, int low1, int high1, int[] arr2, int low2, int high2, int k) {
    if (low1 > high1) {
        return arr2[low2 + k - 1];
    } else if (low2 > high2) {
        return arr1[low1 + k - 1];
    }
    if (k == 1) {
        return Math.min(arr1[low1], arr2[low2]);
    }
    int i = Math.min(low1 + k / 2, high1 + 1);
    int j = Math.min(low2 + k / 2, high2 + 1);
    if (arr1[i - 1] > arr2[j - 1]) {
        return helper(arr1, low1, high1, arr2, j, high2, k - (j - low2));
    } else {
        return helper(arr1, i, high1, arr2, low2, high2, k - (i - low1));
    }
}

1
#include <bits/stdc++.h>
using namespace std;

int findKthElement(int a[],int start1,int end1,int b[],int start2,int end2,int k){

    if(start1 >= end1)return b[start2+k-1];
    if(start2 >= end2)return a[start1+k-1];
    if(k==1)return min(a[start1],b[start2]);
    int aMax = INT_MAX;
    int bMax = INT_MAX;
    if(start1+k/2-1 < end1) aMax = a[start1 + k/2 - 1];
    if(start2+k/2-1 < end2) bMax = b[start2 + k/2 - 1];

    if(aMax > bMax){
        return findKthElement(a,start1,end1,b,start2+k/2,end2,k-k/2);
    }
    else{
        return findKthElement(a,start1 + k/2,end1,b,start2,end2,k-k/2);
    }
}

int main(void){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m,k;
        cout<<"Enter the size of 1st Array"<<endl;
        cin>>n;
        int arr[n];
        cout<<"Enter the Element of 1st Array"<<endl;
        for(int i = 0;i<n;i++){
            cin>>arr[i];
        }
        cout<<"Enter the size of 2nd Array"<<endl;
        cin>>m;
        int arr1[m];
        cout<<"Enter the Element of 2nd Array"<<endl;
        for(int i = 0;i<m;i++){
            cin>>arr1[i];
        }
        cout<<"Enter The Value of K";
        cin>>k;
        sort(arr,arr+n);
        sort(arr1,arr1+m);
        cout<<findKthElement(arr,0,n,arr1,0,m,k)<<endl;
    }

    return 0;
}

ความซับซ้อนของเวลาคือ O (บันทึก (นาที (n, m)))


1

คำตอบส่วนใหญ่ที่ฉันพบที่นี่มุ่งเน้นไปที่อาร์เรย์ทั้งสอง แม้ว่าจะดี แต่ก็ยากที่จะนำไปใช้เนื่องจากมีกรณีพิเศษมากมายที่เราต้องดูแล นอกจากนี้การใช้งานส่วนใหญ่ยังเป็นแบบวนซ้ำซึ่งจะเพิ่มความซับซ้อนของพื้นที่ของสแต็กการเรียกซ้ำ ดังนั้นแทนที่จะมุ่งเน้นไปที่อาร์เรย์ทั้งสองฉันตัดสินใจที่จะมุ่งเน้นไปที่อาร์เรย์ที่เล็กกว่าและทำการค้นหาแบบไบนารีเฉพาะอาร์เรย์ที่เล็กกว่าและปรับตัวชี้สำหรับอาร์เรย์ที่สองตามค่าของตัวชี้ในอาร์เรย์แรก โดยการดำเนินการดังต่อไปนี้เรามีความซับซ้อนของO(log(min(n,m))ที่มีO(1)ความซับซ้อนพื้นที่

    public static int kth_two_sorted(int []a, int b[],int k){
    if(a.length > b.length){
        return kth_two_sorted(b,a,k);
    }
    if(a.length + a.length < k){
        throw new RuntimeException("wrong argument");
    }
    int low = 0;
    int high = k;
    if(a.length <= k){
        high = a.length-1;
    }
    while(low <= high){
        int sizeA = low+(high - low)/2;
        int sizeB = k - sizeA;
        boolean shrinkLeft = false;
        boolean extendRight = false;
        if(sizeA != 0){
            if(sizeB !=b.length){
                if(a[sizeA-1] > b[sizeB]){
                    shrinkLeft = true;
                    high = sizeA-1;
                }
            }
        }
        if(sizeA!=a.length){
            if(sizeB!=0){
                if(a[sizeA] < b[sizeB-1]){
                    extendRight = true;
                    low = sizeA;
                }
            }
        }
        if(!shrinkLeft && !extendRight){
            return Math.max(a[sizeA-1],b[sizeB-1]) ;
        }
    }
    throw  new IllegalArgumentException("we can't be here");
}

เรามีช่วงของ[low, high]อาร์เรย์aและเราแคบในช่วงนี้ที่เราไปต่อไปผ่านขั้นตอนวิธี sizeAแสดงให้เห็นว่าหลายรายการจากkรายการจากอาร์เรย์aและมันเกิดขึ้นจากค่าของและlow เป็นคำนิยามเดียวกันยกเว้นเราคำนวณวิธีที่คุ้มค่าดังกล่าวว่า ขึ้นอยู่กับค่าของสองเส้นขอบโดยสรุปว่าเราต้องขยายไปทางด้านขวาในอาร์เรย์หรือย่อขนาดไปทางด้านซ้าย ถ้าเราติดอยู่ในตำแหน่งเดียวกันก็หมายความว่าเราพบวิธีแก้ปัญหาและเราจะกลับมาสูงสุดของค่าในตำแหน่งของจากและจากhighsizeBsizeA+sizeB=kasizeA-1asizeB-1b


0

ตรวจสอบรหัสนี้

import math
def findkthsmallest():

    A=[1,5,10,22,30,35,75,125,150,175,200]
    B=[15,16,20,22,25,30,100,155,160,170]
    lM=0
    lN=0
    hM=len(A)-1
    hN=len(B)-1
    k=17

    while True:
        if k==1:
            return min(A[lM],B[lN])


        cM=hM-lM+1
        cN=hN-lN+1
        tmp = cM/float(cM+cN)
        iM=int(math.ceil(tmp*k))
        iN=k-iM
        iM=lM+iM-1
        iN=lN+iN-1
        if A[iM] >= B[iN]:
            if iN == hN or A[iM] < B[iN+1]:
                return A[iM]
            else:
                k = k - (iN-lN+1)
                lN=iN+1
                hM=iM-1
        if B[iN] >= A[iM]:
            if iM == hM or B[iN] < A[iM+1]:
                return B[iN]
            else:
                k = k - (iM-lM+1)
                lM=iM+1
                hN=iN-1
        if hM < lM:
            return B[lN+k-1]
        if hN < lN:
            return A[lM+k-1]

if __name__ == '__main__':
    print findkthsmallest();

ให้คำอธิบาย
Abhijit Sarkar

0

ด้านล่างรหัส C # เพื่อค้นหาองค์ประกอบที่เล็กที่สุดในลำดับที่สองของอาร์เรย์ที่เรียงลำดับกัน ความซับซ้อนของเวลา: O (logk)

        public static int findKthSmallestElement1(int[] A, int startA, int endA, int[] B, int startB, int endB, int k)
        {
            int n = endA - startA;
            int m = endB - startB;

            if (n <= 0)
                return B[startB + k - 1];
            if (m <= 0)
                return A[startA + k - 1];
            if (k == 1)
                return A[startA] < B[startB] ? A[startA] : B[startB];

            int midA = (startA + endA) / 2;
            int midB = (startB + endB) / 2;

            if (A[midA] <= B[midB])
            {
                if (n / 2 + m / 2 + 1 >= k)
                    return findKthSmallestElement1(A, startA, endA, B, startB, midB, k);
                else
                    return findKthSmallestElement1(A, midA + 1, endA, B, startB, endB, k - n / 2 - 1);
            }
            else
            {
                if (n / 2 + m / 2 + 1 >= k)
                    return findKthSmallestElement1(A, startA, midA, B, startB, endB, k);
                else
                    return findKthSmallestElement1(A, startA, endA, B, midB + 1, endB, k - m / 2 - 1);

            }
        }

ไม่มีข้อผิดพลาดฉันได้ทดสอบรหัสของฉันก่อนที่จะโพสต์ไปที่ SO
Piyush Patel

1
ขอบคุณ sammy333 ฉันได้อัปเดตรหัสแล้ว ตอนนี้มันใช้งานได้
Piyush Patel

(อย่าคำนวณmidAจากendAif k < nตรวจสอบอาร์เรย์แบบสั้นเริ่มต้นด้วยreturn B[startB + k - 1];)
greybeard
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.