วิธีผสานสองอาร์เรย์ที่เรียงลำดับไว้ในอาร์เรย์ที่เรียงลำดับแล้ว [ปิด]


160

นี่เป็นคำถามของฉันในการสัมภาษณ์และนี่คือวิธีการแก้ปัญหาที่ฉันมีให้:

public static int[] merge(int[] a, int[] b) {

    int[] answer = new int[a.length + b.length];
    int i = 0, j = 0, k = 0;
    while (i < a.length && j < b.length)
    {
        if (a[i] < b[j])
        {
            answer[k] = a[i];
            i++;
        }
        else
        {
            answer[k] = b[j];
            j++;
        }
        k++;
    }

    while (i < a.length)
    {
        answer[k] = a[i];
        i++;
        k++;
    }

    while (j < b.length)
    {
        answer[k] = b[j];
        j++;
        k++;
    }

    return answer;
}

มีวิธีที่มีประสิทธิภาพมากกว่านี้หรือไม่?

แก้ไข: วิธีแก้ไขความยาวที่ถูกต้อง


30
ดูเหมือนคำตอบที่ดีงามสำหรับฉัน ปัญหานี้จะมีความซับซ้อน O (n) ที่ดีที่สุดและคำตอบของคุณประสบความสำเร็จ สิ่งอื่นใดก็คือ
Drew Hall

3
คุณทำได้ดี! นี่เป็นส่วนหนึ่งของการจัดเรียงผสาน: ผสานสองสตรีมที่เรียงลำดับแล้ว (จากเทปหรือดิสก์) เข้ากับสตรีมที่เรียงลำดับอื่น
Vladimir Dyuzhev

9
คุณมีงานทำหรือไม่?
ไช่

5
นอกจากนี้คุณสามารถใช้ผู้ประกอบการที่สาม: while (i < a.length && j < b.length) answer[k++] = a[i] < b[j] ? a[i++] : b[j++]; ข้อกำหนดภาษา Java: ผู้ประกอบการตามเงื่อนไข? : .
Anton Dozortsev

1
คุณลืมที่จะแสดงความคิดเห็น !!!
LiziPizi

คำตอบ:


33

การปรับปรุงเล็กน้อย แต่หลังจากวนรอบหลักคุณสามารถใช้System.arraycopyเพื่อคัดลอกส่วนท้ายของอินพุตอาร์เรย์ทั้งสองเมื่อคุณไปถึงจุดสิ้นสุดของอีกด้านหนึ่ง แต่นั่นจะไม่เปลี่ยนO(n)ลักษณะการทำงานของโซลูชันของคุณ


109
public static int[] merge(int[] a, int[] b) {

    int[] answer = new int[a.length + b.length];
    int i = 0, j = 0, k = 0;

    while (i < a.length && j < b.length)  
       answer[k++] = a[i] < b[j] ? a[i++] :  b[j++];

    while (i < a.length)  
        answer[k++] = a[i++];

    while (j < b.length)    
        answer[k++] = b[j++];

    return answer;
}

มีขนาดกะทัดรัดขึ้นเล็กน้อย แต่เหมือนกันทุกประการ!


สำหรับผู้ที่กล่าวว่าสิ่งนี้ทำให้ดัชนีไม่อยู่ในขอบเขตยกเว้นอินพุตที่คุณใช้อยู่ มันทำงานได้ในทุกกรณีสำหรับฉัน
Mike Saull

1
ใช้ a สำหรับวนรอบเพื่อรวมบรรทัดที่ประกาศตัวแปรลูปและการควบคุมลูป ใช้บรรทัดว่างสองเส้นเท่าที่จำเป็น - ดูไม่ถูกเรียกระหว่าง "หางสำเนา" แบบสมมาตร
greybeard

58

ฉันประหลาดใจที่ไม่มีใครพูดถึงการใช้งานที่ยอดเยี่ยมมีประสิทธิภาพและกะทัดรัดขนาดนี้:

public static int[] merge(int[] a, int[] b) {
    int[] answer = new int[a.length + b.length];
    int i = a.length - 1, j = b.length - 1, k = answer.length;

    while (k > 0)
        answer[--k] =
                (j < 0 || (i >= 0 && a[i] >= b[j])) ? a[i--] : b[j--];
    return answer;
}

จุดสนใจ

  1. ขอให้สังเกตว่ามันทำจำนวนการดำเนินการเท่ากันหรือน้อยกว่าอัลกอริทึมO (n)อื่น ๆแต่ในคำสั่งเดียวอย่างแท้จริงในหนึ่งเดียวในขณะที่วง!
  2. ถ้าสองอาร์เรย์มีขนาดเท่ากันโดยประมาณค่าคงที่สำหรับ O (n) จะเท่ากัน อย่างไรก็ตามถ้าอาเรย์นั้นมีความไม่สมดุลจริง ๆ แล้วเวอร์ชั่นที่มีSystem.arraycopyจะชนะเพราะภายในนั้นสามารถทำได้ด้วยคำสั่งแอสเซมบลี x86 เดี่ยว
  3. แจ้งให้ทราบล่วงหน้าa[i] >= b[j]แทน a[i] > b[j]สิ่งนี้รับประกันได้ว่า "ความมั่นคง" ที่ถูกนิยามว่าเมื่อองค์ประกอบของ a และ b เท่ากันเราต้องการองค์ประกอบจาก a ก่อน b

นี่เป็นวิธีที่ดีจริงๆ ฉันมีปัญหาในการรับมาตรฐานที่ดีในอัลกอริทึมการเรียงลำดับการผสานของฉันใน Swift lang การแปลงสิ่งนี้ทำให้ฉันได้ในสิ่งที่ฉันต้องการขอบคุณมาก
Chackle

อะไรคือจุดของ (j <0) ในขณะที่ห่วง? Btw, +1, มันเจ๋งจริงๆ! ขอบคุณสำหรับการแบ่งปัน.
Hengameh

2
@Hengameh ในกรณีj < 0, bหมดแล้วดังนั้นเราจึงเก็บเพิ่มที่เหลือaองค์ประกอบกับanswer อาร์เรย์
Natan Streppel

6
ในใจฉัน "ฉลาด" และอ่านยาก ฉันต้องการอ่านรหัสได้ง่ายขึ้นโดยเฉพาะอย่างยิ่งเนื่องจากคุณไม่ได้รับการปรับปรุงประสิทธิภาพมากนักด้วยรหัสนี้
Kevin M

1
เครื่องหมาย
Yan Khonski

16

การปรับปรุงใด ๆ ที่สามารถทำได้คือการเพิ่มประสิทธิภาพแบบไมโครขั้นตอนวิธีโดยรวมนั้นถูกต้อง


ถ้า a มีขนาดใหญ่และ b มีขนาดเล็กแล้วอัลกอริทึมนี้ไม่ถูกต้อง
แจ็ค

7
มันไม่ผิด แต่ไม่มีประสิทธิภาพ
แจ็ค

@jack วิธีที่คุณสามารถทำได้เร็วกว่า O (n) เมื่อคุณสร้างอาร์เรย์ของรายการ n?
จะ

@ System.arrayCopy()จะเร็วอย่างน่าประหลาดใจเนื่องจากใช้การmemcpyโทรที่เหมาะกับ CPU ดังนั้นจึงมีขอบเขตในการปรับปรุงประสิทธิภาพโดยการคัดลอกชิ้นข้อมูล นอกจากนี้ยังมีขอบเขตการค้นหาแบบไบนารีสำหรับขอบเขต
บาง

โดยเฉพาะอย่างยิ่งถ้าคุณสามารถใช้ลักษณะที่เรียงลำดับเพื่อข้ามรายการส่วนใหญ่และไม่เปรียบเทียบ คุณสามารถเอาชนะ O (n) ได้จริง
Tatarize

10

โซลูชันนี้คล้ายกับโพสต์อื่น ๆ ยกเว้นว่าจะใช้ System.arrayCopy เพื่อคัดลอกองค์ประกอบอาเรย์ที่เหลือ

private static int[] sortedArrayMerge(int a[], int b[]) {
    int result[] = new int[a.length +b.length];
    int i =0; int j = 0;int k = 0;
    while(i<a.length && j <b.length) {
        if(a[i]<b[j]) {
            result[k++] = a[i];
            i++;
        } else {
            result[k++] = b[j];
            j++;
        }
    }
    System.arraycopy(a, i, result, k, (a.length -i));
    System.arraycopy(b, j, result, k, (b.length -j));
    return result;
}

7

นี่คือฟังก์ชั่นการปรับปรุง มันลบรายการที่ซ้ำกันหวังว่าบางคนจะพบว่าใช้งานได้:

public static long[] merge2SortedAndRemoveDublicates(long[] a, long[] b) {
    long[] answer = new long[a.length + b.length];
    int i = 0, j = 0, k = 0;
    long tmp;
    while (i < a.length && j < b.length) {
        tmp = a[i] < b[j] ? a[i++] : b[j++];
        for ( ; i < a.length && a[i] == tmp; i++);
        for ( ; j < b.length && b[j] == tmp; j++);
        answer[k++] = tmp;
    }
    while (i < a.length) {
        tmp = a[i++];
        for ( ; i < a.length && a[i] == tmp; i++);
        answer[k++] = tmp;
    }
    while (j < b.length) {
        tmp = b[j++];
        for ( ; j < b.length && b[j] == tmp; j++);
        answer[k++] = tmp;
    }
    return Arrays.copyOf(answer, k);
}

+1, ขอบคุณสำหรับการแบ่งปัน คำถามหนึ่ง: ทำไมคุณถึงเลือกประเภทของอาร์เรย์และชนิดของตัวแปร 'temp' ยาว?
Hengameh

(ผมมีข้อสงสัยเกี่ยวกับวิธีที่ชื่อ.)
greybeard

5

มันสามารถทำได้ในงบ 4 ดังต่อไปนี้

 int a[] = {10, 20, 30};
 int b[]= {9, 14, 11};
 int res[]=new int[a.legth+b.length]; 
 System.arraycopy(a,0, res, 0, a.length); 
 System.arraycopy(b,0,res,a.length, b.length);
 Array.sort(res)


5
ฉันไม่เข้าใจว่าทำไมคำตอบนี้จึงมีคะแนนเป็นลบ มันเป็นความจริงที่มันไม่มีประสิทธิภาพ แต่บางครั้งสิ่งที่คุณต้องทำก็คือทำให้งานเสร็จเร็วที่สุด หากคุณกำลังจัดการกับอาร์เรย์ขนาดเล็กมากพูดน้อยกว่า 100 องค์ประกอบฉันต้องการใช้โค้ดด้านบนแทนที่จะเขียนโค้ดยาวที่จะไม่ปรับปรุงประสิทธิภาพที่สำคัญ ดังนั้นขอขอบคุณ Sudhir ที่มอบโซลูชั่นที่ง่ายและ SANN3 สำหรับการแก้ไข
Ahmedov

2
หลักฐานที่ไม่ได้เขียนไว้คือsortฟังก์ชันไม่สามารถใช้ตัวเองเป็นวิธีการเรียงลำดับได้ นั่นจะเป็นการถดถอยที่ไม่มีที่สิ้นสุดแทนการสอบถามซ้ำ นอกจากนี้ยังมีหลักฐานอื่น ๆ ที่ merge_array เป็นฟังก์ชั่นที่ดำเนินการเรียงลำดับ ดังนั้นคำตอบนี้ใช้ไม่ได้ในบริบทที่เป็นไปได้มากที่สุด
Aki Suihkonen

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

คำถามที่ระบุว่าอาร์เรย์อยู่ในลำดับที่เรียงแล้ว หากอาร์เรย์มีขนาดใหญ่มากวิธีนี้จะหยุดชะงักและทำงานได้ไม่ดี ดังนั้นคุณต้องได้ผลลัพธ์ที่ต้องการ แต่แอพจะไม่ทำงานและคุณจะไม่ได้งานถ้าฉันถูกสัมภาษณ์
Kevin M

ฟังก์ชั่น Array.sort () ใช้ TimSort ซึ่งจะพบว่าการวิ่งเรียงและใช้การเรียงแบบผสานกับพวกมัน ผิดปกติรหัสนี้ไม่สามารถแม้แต่จะเป็นความผิดพลาดสำหรับ "ไม่มีประสิทธิภาพ" แต่จริง ๆ แล้วมันจะตกอยู่ในเวลา O (n) เนื่องจากการเรียงลำดับที่ทำงาน คุณสามารถเรียกใช้การวัดมาตรฐานจำนวนมากโอกาสที่ดีที่มันจะชนะรหัส OP ค่อนข้างบ่อย
Tatarize

4

ฉันต้องเขียนมันด้วยจาวาสคริปต์นี่คือ:

function merge(a, b) {
    var result = [];
    var ai = 0;
    var bi = 0;
    while (true) {
        if ( ai < a.length && bi < b.length) {
            if (a[ai] < b[bi]) {
                result.push(a[ai]);
                ai++;
            } else if (a[ai] > b[bi]) {
                result.push(b[bi]);
                bi++;
            } else {
                result.push(a[ai]);
                result.push(b[bi]);
                ai++;
                bi++;
            }
        } else if (ai < a.length) {
            result.push.apply(result, a.slice(ai, a.length));
            break;
        } else if (bi < b.length) {
            result.push.apply(result, b.slice(bi, b.length));
            break;
        } else {
            break;
        }
    }
    return result;
}

4

คอลเลกชัน Apache รองรับวิธีเรียงลำดับตั้งแต่รุ่น 4; คุณสามารถทำได้โดยใช้collateวิธีการใน:

org.apache.commons.collections4.CollectionUtils

อ้างจากที่นี่ javadoc:

collate(Iterable<? extends O> a, Iterable<? extends O> b, Comparator<? super O> c)

ผสานสองคอลเล็กชันที่เรียงลำดับแล้วaและbรวมไว้ในรายการเดี่ยวที่เรียงลำดับเช่นเดียวกับการเรียงลำดับขององค์ประกอบตาม Comparator c

อย่าประดิษฐ์วงล้อใหม่! การอ้างอิงเอกสาร: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

GallopSearch Merge: O (บันทึก (n) * บันทึก (i))มากกว่า O (n)

ฉันไปข้างหน้าและดำเนินการตามคำแนะนำของ greybeard ในความคิดเห็น ส่วนใหญ่เป็นเพราะฉันต้องการรุ่นที่สำคัญภารกิจที่มีประสิทธิภาพสูงของรหัสนี้

  • รหัสใช้ gallopSearch ซึ่งเป็น O (log (i)) โดยที่ i คือระยะทางจากดัชนีปัจจุบันซึ่งมีดัชนีที่เกี่ยวข้องอยู่
  • รหัสใช้ binarySearch สำหรับหลังจากการค้นหาควบระบุช่วงที่เหมาะสม เนื่องจาก gallop จำกัด สิ่งนี้ให้อยู่ในช่วงที่เล็กลง binarySearch ที่ได้คือ O (log (i))
  • ควบม้าและผสานจะดำเนินการไปข้างหลัง สิ่งนี้ไม่ได้ดูเหมือนภารกิจสำคัญ แต่จะช่วยให้สามารถรวมอาร์เรย์ได้ หากหนึ่งในอาร์เรย์ของคุณมีพื้นที่เพียงพอที่จะเก็บค่าผลลัพธ์คุณสามารถใช้มันเป็นอาร์เรย์ผสานและอาร์เรย์ผลลัพธ์ได้ คุณต้องระบุช่วงที่ถูกต้องภายในอาร์เรย์ในกรณีดังกล่าว
  • ไม่ต้องการการจัดสรรหน่วยความจำในกรณีนั้น (ประหยัดได้มากในการดำเนินการที่สำคัญ) มันทำให้แน่ใจว่ามันไม่ได้และไม่สามารถเขียนทับค่าที่ไม่ได้ประมวลผลใด ๆ (ซึ่งสามารถทำได้ย้อนหลังเท่านั้น) ในความเป็นจริงคุณใช้อาร์เรย์เดียวกันสำหรับทั้งอินพุตและผลลัพธ์ มันจะไม่มีผลร้ายใด ๆ
  • ฉันใช้ Integer.compare () อย่างสม่ำเสมอดังนั้นจึงสามารถเปลี่ยนได้เพื่อวัตถุประสงค์อื่น
  • มีโอกาสที่ฉันอาจได้เข้าใจข้อมูลเล็กน้อยและไม่ได้ใช้ประโยชน์ที่ฉันได้พิสูจน์มาแล้ว เช่นการค้นหาไบนารีในช่วงของสองค่าซึ่งหนึ่งค่าถูกตรวจสอบแล้ว อาจมีวิธีที่ดีกว่าในการระบุลูปหลัก, ค่า c การพลิกไม่จำเป็นถ้าพวกมันรวมกันเป็นสองการดำเนินงานตามลำดับ เมื่อคุณรู้ว่าคุณจะทำอย่างอื่นทุกครั้ง มีห้องสำหรับขัดบางอย่าง

นี่ควรเป็นวิธีที่มีประสิทธิภาพที่สุดในการทำสิ่งนี้ด้วยความซับซ้อนของเวลาของO (log (n) * log (i))มากกว่า O (n) และความซับซ้อนของเวลากรณีที่เลวร้ายที่สุดของ O (n) หากอาร์เรย์ของคุณมีลักษณะเป็นก้อนและมีค่าความยาวรวมกันสิ่งนี้จะทำให้คนอื่นแคระอย่างอื่นไม่เช่นนั้นมันจะดีกว่าพวกมัน

มันมีค่าการอ่านสองค่าที่ส่วนท้ายของอาร์เรย์ที่ผสานและค่าการเขียนภายในอาร์เรย์ผลลัพธ์ หลังจากค้นหาว่าค่าใดมีค่าน้อยกว่าจะทำการค้นหาควบเข้าไปในอาร์เรย์นั้น 1, 2, 4, 8, 16, 32, ฯลฯ เมื่อพบช่วงที่ค่าการอ่านของอาร์เรย์อื่นมีค่ามากกว่า มันค้นหาไบนารีในช่วงนั้น (ลดช่วงครึ่ง, ค้นหาครึ่งที่ถูกต้อง, ทำซ้ำจนกระทั่งค่าเดียว) จากนั้นอาร์เรย์จะคัดลอกค่าเหล่านั้นไปยังตำแหน่งเขียน โปรดจำไว้ว่าการย้ายสำเนานั้นจำเป็นที่จะไม่สามารถเขียนทับค่าเดียวกันจากอาร์เรย์การอ่าน (ซึ่งหมายความว่าอาร์เรย์การเขียนและอาร์เรย์การอ่านอาจเป็นค่าเดียวกัน) จากนั้นทำการดำเนินการเดียวกันสำหรับอาเรย์อื่นซึ่งขณะนี้ทราบว่าน้อยกว่าค่าการอ่านใหม่ของอาเรย์อื่น

static public int gallopSearch(int current, int[] array, int v) {
    int d = 1;
    int seek = current - d;
    int prevIteration = seek;
    while (seek > 0) {
        if (Integer.compare(array[seek], v) <= 0) {
            break;
        }
        prevIteration = seek;
        d <<= 1;
        seek = current - d;
        if (seek < 0) {
            seek = 0;
        }
    }
    if (prevIteration != seek) {
        seek = binarySearch(array, seek, prevIteration, v);
        seek = seek >= 0 ? seek : ~seek;
    }
    return seek;
}

static public int binarySearch(int[] list, int fromIndex, int toIndex, int v) {
    int low = fromIndex;
    int high = toIndex - 1;
    while (low <= high) {
        int mid = (low + high) >>> 1;
        int midVal = list[mid];
        int cmp = Integer.compare(midVal, v);
        if (cmp < 0) {
            low = mid + 1;
        } else if (cmp > 0) {
            high = mid - 1;
        } else {
            return mid;// key found
        }
    }
    return -(low + 1);// key not found.
}

static public int[] sortedArrayMerge(int[] a, int[] b) {
    return sortedArrayMerge(null, a, a.length, b, b.length);
}

static public int[] sortedArrayMerge(int[] results, int[] a, int aRead, int b[], int bRead) {
    int write = aRead + bRead, length, gallopPos;
    if ((results == null) || (results.length < write)) {
        results = new int[write];
    }
    if (aRead > 0 && bRead > 0) {
        int c = Integer.compare(a[aRead - 1], b[bRead - 1]);
        while (aRead > 0 && bRead > 0) {
            switch (c) {
                default:
                    gallopPos = gallopSearch(aRead, a, b[bRead-1]);
                    length = (aRead - gallopPos);
                    write -= length;
                    aRead = gallopPos;
                    System.arraycopy(a, gallopPos--, results, write, length);
                    c = -1;
                    break;
                case -1:
                    gallopPos = gallopSearch(bRead, b, a[aRead-1]);
                    length = (bRead - gallopPos);
                    write -= length;
                    bRead = gallopPos;
                    System.arraycopy(b, gallopPos--, results, write, length);
                    c = 1;
                    break;
            }
        }
    }
    if (bRead > 0) {
        if (b != results) {
            System.arraycopy(b, 0, results, 0, bRead);
        }
    } else if (aRead > 0) {
        if (a != results) {
            System.arraycopy(a, 0, results, 0, aRead);
        }
    }
    return results;
}

นี่ควรเป็นวิธีที่มีประสิทธิภาพที่สุดในการทำ


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

static public int removeDuplicates(int[] list, int size) {
    int write = 1;
    for (int read = 1; read < size; read++) {
        if (list[read] == list[read - 1]) {
            continue;
        }
        list[write++] = list[read];
    }
    return write;
}

อัปเดต: คำตอบก่อนหน้าไม่ใช่รหัสที่น่ากลัว แต่ด้อยกว่าอย่างชัดเจน

การเพิ่มประสิทธิภาพไฮเปอร์ที่ไม่จำเป็นอีกอย่างหนึ่ง ไม่เพียงเรียกใช้ arraycopy สำหรับบิตสุดท้าย แต่ยังรวมถึงการเริ่มต้นด้วย การประมวลผลเบื้องต้นที่ไม่ทับซ้อนใน O (บันทึก (n)) โดย binarySearch เป็นข้อมูล O (log (n) + n) คือ O (n) และในบางกรณีเอฟเฟกต์จะเด่นชัดโดยเฉพาะอย่างยิ่งในกรณีที่ไม่มีการซ้อนทับกันระหว่างอาร์เรย์ที่รวมกัน

private static int binarySearch(int[] array, int low, int high, int v) {
    high = high - 1;
    while (low <= high) {
        int mid = (low + high) >>> 1;
        int midVal = array[mid];
        if (midVal > v)
            low = mid + 1;
        else if (midVal < v)
            high = mid - 1;
        else
            return mid; // key found
    }
    return low;//traditionally, -(low + 1);  // key not found.
}

private static int[] sortedArrayMerge(int a[], int b[]) {
    int result[] = new int[a.length + b.length];
    int k, i = 0, j = 0;
    if (a[0] > b[0]) {
        k = i = binarySearch(b, 0, b.length, a[0]);
        System.arraycopy(b, 0, result, 0, i);
    } else {
        k = j = binarySearch(a, 0, a.length, b[0]);
        System.arraycopy(a, 0, result, 0, j);
    }
    while (i < a.length && j < b.length) {
        result[k++] = (a[i] < b[j]) ? a[i++] : b[j++];
    }
    if (j < b.length) {
        System.arraycopy(b, j, result, k, (b.length - j));
    } else {
        System.arraycopy(a, i, result, k, (a.length - i));
    }
    return result;
}

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

นั่นคือสิ่งที่ Arrays.sort ที่นำมาใช้ทำทั้งหมด มันเป็นการรวมที่แย่ที่สุด ฉันคิดว่าพวกเขาสลับ 2 องค์ประกอบที่จำเป็น แต่ตกอยู่ใน arraycopy มากกว่า 2 องค์ประกอบ ฉันไม่แน่ใจว่าคุณจะตรวจสอบองค์ประกอบถัดไปเป็นเส้นตรงหรือค้นหาแบบไบนารีหรือไม่ จะมีข้อได้เปรียบที่ค่อนข้างใหญ่ในการตรวจสอบอย่างถี่ถ้วนว่าคุณสามารถวิ่งไปไกลกว่านี้ได้หรือไม่ถ้าคุณวิ่งไปได้ เช่นเดียวกับการตรวจสอบ 8 ล่วงหน้าและหากคุณสามารถคัดลอกสิ่งที่คุณบันทึกไว้ 7 การดำเนินการของสิ่งที่คุณไม่จำเป็นต้องดู
Tatarize

@greybeard ... และทำเสร็จแล้ว ย้อนกลับไปด้วยดังนั้นฉันจึงสามารถใช้หน่วยความจำเดียวกันนี้ได้อีกครั้ง
Tatarize

สิ่งที่ดีที่คุณมีแรงจูงใจที่จะเป็นขีปนาวุธ จะมีการดูแลอย่างใกล้ชิดในเวลากลางวัน
greybeard

That is totally what the implemented Arrays.sort does( นั่นคือจากการแก้ไขครั้งแรกของคำตอบของคุณ - หรือ - จากความคิดเห็นที่ 19 กุมภาพันธ์ของฉัน?) - ไม่สามารถหาได้ใน JDK 8 ของ Sunsoft: การใช้งานแบบArrays.sortใดที่คุณอ้างถึง?
greybeard

2

นี่คือรูปแบบย่อที่เขียนด้วย javascript:

function sort( a1, a2 ) {

    var i = 0
        , j = 0
        , l1 = a1.length
        , l2 = a2.length
        , a = [];

    while( i < l1 && j < l2 ) {

        a1[i] < a2[j] ? (a.push(a1[i]), i++) : (a.push( a2[j]), j++);
    }

    i < l1 && ( a = a.concat( a1.splice(i) ));
    j < l2 && ( a = a.concat( a2.splice(j) ));

    return a;
}

1
    public class Merge {

    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    public static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {

        // precondition: a[lo .. mid] and a[mid+1 .. hi] are sorted subarrays
        assert isSorted(a, lo, mid);
        assert isSorted(a, mid+1, hi);

        // copy to aux[]
        for (int k = lo; k <= hi; k++) {
            aux[k] = a[k]; 
        }

        // merge back to a[]
        int i = lo, j = mid+1;
        for (int k = lo; k <= hi; k++) {
            if      (i > mid)              a[k] = aux[j++];
            else if (j > hi)               a[k] = aux[i++];
            else if (less(aux[j], aux[i])) a[k] = aux[j++];
            else                           a[k] = aux[i++];
        }

        // postcondition: a[lo .. hi] is sorted
        assert isSorted(a, lo, hi);
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
        if (hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, aux, lo, mid);
        sort(a, aux, mid + 1, hi);
        merge(a, aux, lo, mid, hi);
    }

    public static void sort(Comparable[] a) {
        Comparable[] aux = new Comparable[a.length];
        sort(a, aux, 0, a.length-1);
        assert isSorted(a);
    }


   /***********************************************************************
    *  Helper sorting functions
    ***********************************************************************/

    // is v < w ?
    private static boolean less(Comparable v, Comparable w) {
        return (v.compareTo(w) < 0);
    }

    // exchange a[i] and a[j]
    private static void exch(Object[] a, int i, int j) {
        Object swap = a[i];
        a[i] = a[j];
        a[j] = swap;
    }


   /***********************************************************************
    *  Check if array is sorted - useful for debugging
    ***********************************************************************/
    private static boolean isSorted(Comparable[] a) {
        return isSorted(a, 0, a.length - 1);
    }

    private static boolean isSorted(Comparable[] a, int lo, int hi) {
        for (int i = lo + 1; i <= hi; i++)
            if (less(a[i], a[i-1])) return false;
        return true;
    }


   /***********************************************************************
    *  Index mergesort
    ***********************************************************************/
    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    private static void merge(Comparable[] a, int[] index, int[] aux, int lo, int mid, int hi) {

        // copy to aux[]
        for (int k = lo; k <= hi; k++) {
            aux[k] = index[k]; 
        }

        // merge back to a[]
        int i = lo, j = mid+1;
        for (int k = lo; k <= hi; k++) {
            if      (i > mid)                    index[k] = aux[j++];
            else if (j > hi)                     index[k] = aux[i++];
            else if (less(a[aux[j]], a[aux[i]])) index[k] = aux[j++];
            else                                 index[k] = aux[i++];
        }
    }

    // return a permutation that gives the elements in a[] in ascending order
    // do not change the original array a[]
    public static int[] indexSort(Comparable[] a) {
        int N = a.length;
        int[] index = new int[N];
        for (int i = 0; i < N; i++)
            index[i] = i;

        int[] aux = new int[N];
        sort(a, index, aux, 0, N-1);
        return index;
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort(Comparable[] a, int[] index, int[] aux, int lo, int hi) {
        if (hi <= lo) return;
        int mid = lo + (hi - lo) / 2;
        sort(a, index, aux, lo, mid);
        sort(a, index, aux, mid + 1, hi);
        merge(a, index, aux, lo, mid, hi);
    }

    // print array to standard output
    private static void show(Comparable[] a) {
        for (int i = 0; i < a.length; i++) {
            StdOut.println(a[i]);
        }
    }

    // Read strings from standard input, sort them, and print.
    public static void main(String[] args) {
        String[] a = StdIn.readStrings();
        Merge.sort(a);
        show(a);
    }
}

สำเนานี้มีa[mid+1 .. hi]ไว้auxเพื่ออะไร
greybeard

1

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


1
public int[] merge(int[] a, int[] b) {
    int[] result = new int[a.length + b.length];
    int aIndex, bIndex = 0;

    for (int i = 0; i < result.length; i++) {
        if (aIndex < a.length && bIndex < b.length) {
            if (a[aIndex] < b[bIndex]) {
                result[i] = a[aIndex];
                aIndex++;
            } else {
                result[i] = b[bIndex];
                bIndex++;
            }
        } else if (aIndex < a.length) {
            result[i] = a[aIndex];
            aIndex++;
        } else {
            result[i] = b[bIndex];
            bIndex++;
        }
    }

    return result;
}

2
คำอธิบายบางอย่างจะดี :)
gsamaras

1
public static int[] merge(int[] a, int[] b) {
    int[] mergedArray = new int[(a.length + b.length)];
    int i = 0, j = 0;
    int mergedArrayIndex = 0;
    for (; i < a.length || j < b.length;) {
        if (i < a.length && j < b.length) {
            if (a[i] < b[j]) {
                mergedArray[mergedArrayIndex] = a[i];
                i++;
            } else {
                mergedArray[mergedArrayIndex] = b[j];
                j++;
            }
        } else if (i < a.length) {
            mergedArray[mergedArrayIndex] = a[i];
            i++;
        } else if (j < b.length) {
            mergedArray[mergedArrayIndex] = b[j];
            j++;
        }
        mergedArrayIndex++;
    }
    return mergedArray;
}

พระคุณที่ช่วยให้รอดของสิ่งนี้คืออะไร? มันสามารถหดfor (int i, j, k = i = j = 0 ; k < c.length ; ) c[k++] = b.length <= j || i < a.length && a[i] < b[j] ? a[i++] : b[j++];ได้ มันแตกต่างจากคำตอบของ Andrew ในปี 2014อย่างไร?
greybeard

1

อัลกอริทึมสามารถปรับปรุงได้หลายวิธี ตัวอย่างเช่นมีเหตุผลที่จะตรวจสอบว่าa[m-1]<b[0]หรือb[n-1]<a[0]หรือในกรณีเหล่านี้ไม่จำเป็นต้องทำการเปรียบเทียบเพิ่มเติม อัลกอริทึมสามารถคัดลอกอาร์เรย์ต้นทางในผลลัพธ์ที่ได้ในลำดับที่ถูกต้อง

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


สำหรับการปรับปรุงนี้มันจะเป็นการดีกว่าที่จะตรวจสอบว่าองค์ประกอบแรกจะตกอยู่ในอาร์เรย์ที่สองด้วย binarysearch แล้ว arraycopy ข้อมูลที่จะเริ่มต้น จากนั้นในกรณีที่หนึ่งในเช็คเหล่านั้นเป็นจริงมันก็จะมีทุกอย่างอาเรย์คัดลอกแล้วอาเรย์คัดลอก ternary และคุณได้รับผลลัพธ์เดียวกัน แต่ในกรณีที่มีการเหลื่อมกันเล็กน้อยคุณจะต้องทำอัลกอริธึมที่เหมาะสมระหว่างการทับซ้อนและไม่ต้องใช้เวลาอื่น เนื่องจากคุณติดอยู่กับ O (n) โดยใช้คำสั่ง O (logn) ด่วนก่อนจะไม่เสียค่าใช้จ่ายใด ๆ
Tatarize

1

ปัญหานี้เกี่ยวข้องกับอัลกอริธึมการผสานซึ่งจะรวมสองอาร์เรย์ย่อยที่เรียงลำดับไว้ในอาร์เรย์ย่อยเรียงลำดับเดียว CLRSหนังสือให้เห็นตัวอย่างของขั้นตอนวิธีและทำความสะอาดขึ้นความจำเป็นในการตรวจสอบหากท้ายที่สุดได้รับการเข้าถึงได้โดยการเพิ่มมูลค่าแมวมอง (บางสิ่งบางอย่างที่เปรียบเทียบและ "มากกว่าค่าอื่น ๆ") การสิ้นสุดของแต่ละอาร์เรย์

ฉันเขียนสิ่งนี้ใน Python แต่ควรแปลเป็นภาษา Java อย่างดีเช่นกัน:

def func(a, b):
    class sentinel(object):
        def __lt__(*_):
            return False

    ax, bx, c = a[:] + [sentinel()], b[:] + [sentinel()], []
    i, j = 0, 0

    for k in range(len(a) + len(b)):
        if ax[i] < bx[j]:
            c.append(ax[i])
            i += 1
        else:
            c.append(bx[j])
            j += 1

    return c

จำนวนมากคัดลอกองค์ประกอบอีกครั้งเพื่อ (ฝีมือ) ใช้แมวมอง…
greybeard

1

คุณสามารถใช้ 2 เธรดเพื่อเติมอาเรย์ผลลัพธ์หนึ่งรายการจากด้านหน้าหนึ่งรายการจากด้านหลัง

สิ่งนี้สามารถทำงานได้โดยไม่มีการซิงโครไนซ์ในกรณีของตัวเลขเช่นถ้าแต่ละเธรดแทรกค่าครึ่งหนึ่ง


0
//How to merge two sorted arrays into a sorted array without duplicates?
//simple C Coding
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

main()
{
    int InputArray1[] ={1,4,5,7,8,9,12,13,14,17,40};
    int InputArray2[] ={4,5,11,14,15,17,18,19,112,122,122,122,122};
    int n=10;
    int OutputArray[30];
    int i=0,j=0,k=0;
    //k=OutputArray
    while(i<11 && j<13)
    {
        if(InputArray1[i]<InputArray2[j])
        {
            if (k == 0 || InputArray1[i]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray1[i];
            }
            i=i+1;
        }
        else if(InputArray1[i]>InputArray2[j])
        {
            if (k == 0 || InputArray2[j]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray2[j];
            }
            j=j+1;
        }
        else
        {
            if (k == 0 || InputArray1[i]!= OutputArray[k-1])
            {
                OutputArray[k++] = InputArray1[i];
            }
            i=i+1;
            j=j+1;
        }
    };
    while(i<11)
    {
        if(InputArray1[i]!= OutputArray[k-1])
            OutputArray[k++] = InputArray1[i++];
        else
            i++;
    }
    while(j<13)
    {
        if(InputArray2[j]!= OutputArray[k-1])
            OutputArray[k++] = InputArray2[j++];
        else
            j++;
    }
    for(i=0; i<k; i++)
    {
        printf("sorted data:%d\n",OutputArray[i]);
    };
}

0
public static int[] merge(int[] listA, int[] listB) {
        int[] mergedList = new int[ listA.length + listB.length];
        int i = 0; // Counter for listA
        int j = 0; // Counter for listB
        int k = 0; // Counter for mergedList
        while (true) {
            if (i >= listA.length && j >= listB.length) {
                break;
            }
            if (i < listA.length && j < listB.length) { // If both counters are valid.
                if (listA[i] <= listB[j]) {
                    mergedList[k] = listA[i];
                    k++;
                    i++;
                } else {
                    mergedList[k] = listB[j];
                    k++;
                    j++;
                }
            } else if (i < listA.length && j >= listB.length) { // If only A's counter is valid.
                mergedList[k] = listA[i];
                k++;
                i++;
            } else if (i <= listA.length && j < listB.length) { // If only B's counter is valid
                mergedList[k] = listB[j];
                k++;
                j++;
            }
        }
        return mergedList;
    }

0
var arrCombo = function(arr1, arr2){
  return arr1.concat(arr2).sort(function(x, y) {
    return x - y;
  });
};

2
คำตอบนี้ใช้ไม่ได้กับภาษาการเขียนโปรแกรม Java แม้ว่ามันจะเป็นคำตอบที่ดีสำหรับจาวาสคริปต์
gknicker

นี่เป็นส่วนหนึ่งของการสัมภาษณ์งาน ในกรณีเหล่านี้คุณไม่ได้คาดหวังว่าจะเขียนรหัส "ปกติ" ดังกล่าวข้างต้น พวกเขากำลังมองหาโค้ด "มีประสิทธิภาพ" และการสาธิตที่คุณเข้าใจอัลกอริทึมที่เกี่ยวข้อง
d11wtq

0

ภาษาการเขียนโปรแกรมที่ฉันชอบคือ JavaScript

function mergeSortedArrays(a, b){
    var result = [];

    var sI = 0;
    var lI = 0;
    var smallArr;
    var largeArr;
    var temp;

    if(typeof b[0] === 'undefined' || a[0]<b[0]){
        smallArr = a;
        largeArr = b;
    } else{
        smallArr = b;
        largeArr = a;
    }

    while(typeof smallArr[sI] !== 'undefined'){
        result.push(smallArr[sI]);
        sI++;

        if(smallArr[sI]>largeArr[lI] || typeof smallArr[sI] === 'undefined'){
            temp = smallArr;
            smallArr = largeArr;
            largeArr = temp;
            temp = sI;
            sI = lI;
            lI = temp;
        }
    }
    return result;
}

0

อาจจะใช้ System.arraycopy

public static byte[] merge(byte[] first, byte[] second){
    int len = first.length + second.length;
    byte[] full = new byte[len];
    System.arraycopy(first, 0, full, 0, first.length);
    System.arraycopy(second, 0, full, first.length, second.length);
    return full;
}

3
คุณเป็นเพียงการรวมพวกเขา; อาร์เรย์ที่เป็นผลลัพธ์ของคุณนั้นไม่ได้จัดเรียงซึ่งเป็นข้อกำหนด
Sanjeev Dhiman

0
public static void main(String[] args) {
    int[] arr1 = {2,4,6,8,10,999};
    int[] arr2 = {1,3,5,9,100,1001};

    int[] arr3 = new int[arr1.length + arr2.length];

    int temp = 0;

    for (int i = 0; i < (arr3.length); i++) {
        if(temp == arr2.length){
            arr3[i] = arr1[i-temp];
        }
        else if (((i-temp)<(arr1.length)) && (arr1[i-temp] < arr2[temp])){
                arr3[i] = arr1[i-temp];
        }
        else{
            arr3[i] = arr2[temp];
            temp++;
        }
    }

    for (int i : arr3) {
        System.out.print(i + ", ");
    }
}

ผลลัพธ์คือ:

1, 2, 3, 4, 5, 6, 8, 9, 10, 100, 999, 1001,


ทำให้เกิดความสับสนในการตั้งชื่อดัชนีลงarr2ไม่ได้แต่ind2 temp
greybeard

0

คุณสามารถใช้ตัวดำเนินการที่สามเพื่อทำให้รหัสมีขนาดกะทัดรัดขึ้นอีกเล็กน้อย

public static int[] mergeArrays(int[] a1, int[] a2) {
    int[] res = new int[a1.length + a2.length];
    int i = 0, j = 0;

    while (i < a1.length && j < a2.length) {
        res[i + j] = a1[i] < a2[j] ? a1[i++] : a2[j++];
    }

    while (i < a1.length) {
        res[i + j] = a1[i++];
    }

    while (j < a2.length) {
        res[i + j] = a2[j++];
    }

    return res;
}

0
public static int[] mergeSorted(int[] left, int[] right) {
    System.out.println("merging " + Arrays.toString(left) + " and " + Arrays.toString(right));
    int[] merged = new int[left.length + right.length];
    int nextIndexLeft = 0;
    int nextIndexRight = 0;
    for (int i = 0; i < merged.length; i++) {
        if (nextIndexLeft >= left.length) {
            System.arraycopy(right, nextIndexRight, merged, i, right.length - nextIndexRight);
            break;
        }
        if (nextIndexRight >= right.length) {
            System.arraycopy(left, nextIndexLeft, merged, i, left.length - nextIndexLeft);
            break;
        }
        if (left[nextIndexLeft] <= right[nextIndexRight]) {
            merged[i] = left[nextIndexLeft];
            nextIndexLeft++;
            continue;
        }
        if (left[nextIndexLeft] > right[nextIndexRight]) {
            merged[i] = right[nextIndexRight];
            nextIndexRight++;
            continue;
        }
    }
    System.out.println("merged : " + Arrays.toString(merged));
    return merged;
}

แตกต่างจากโซลูชันเดิมเล็กน้อย


0

ในการมาร์จที่เรียงลำดับสองแถวใน O (m + n) ความซับซ้อนของเวลาใช้วิธีการด้านล่างโดยใช้ลูปเดียวเท่านั้น m และ n คือความยาวของอาร์เรย์แรกและอาร์เรย์ที่สอง

public class MargeSortedArray {
    public static void main(String[] args) {
        int[] array = new int[]{1,3,4,7};
        int[] array2 = new int[]{2,5,6,8,12,45};
        int[] newarry = margeToSortedArray(array, array2);
        //newarray is marged array
    }

    // marge two sorted array with o(a+n) time complexity
    public static int[] margeToSortedArray(int[] array, int[] array2) {
        int newarrlen = array.length+array2.length;
        int[] newarr = new int[newarrlen];

        int pos1=0,pos2=0;
        int len1=array.length, len2=array2.length;

        for(int i =0;i<newarrlen;i++) {     
            if(pos1>=len1) {
                newarr[i]=array2[pos2];
                pos2++;
                continue;
            }
            if(pos2>=len2) {
                newarr[i]=array[pos1];
                pos1++;
                continue;
            }

            if(array[pos1]>array2[pos2]) {
                newarr[i]=array2[pos2];
                pos2++;
            } else {
                newarr[i]=array[pos1];
                pos1++;
            }   
        }

        return newarr;
    }

}

0
var arr1 = [2,10,20,30,100];
var arr2 = [2,4,5,6,7,8,9];
var j = 0;
var i =0;
var newArray = [];

for(var x=0;x< (arr1.length + arr2.length);x++){
    if(arr1[i] >= arr2[j]){                //check if element arr2 is equal and less than arr1 element
        newArray.push(arr2[j]);
      j++;
    }else if(arr1[i] < arr2[j]){            //check if element arr1 index value  is less than arr2 element
        newArray.push(arr1[i]);
        i++;
    }
    else if(i == arr1.length || j < arr2.length){    // add remaining arr2 element
        newArray.push(arr2[j]);
        j++
    }else{                                                   // add remaining arr1 element
        newArray.push(arr1[i]); 
        i++
    }

}

console.log(newArray);

-1

เนื่องจากคำถามไม่ได้สมมติภาษาเฉพาะใด ๆ นี่คือทางออกใน Python สมมติว่าอาร์เรย์ถูกเรียงลำดับแล้ว

วิธีที่ 1 - ใช้อาร์เรย์ numpy: import numpy

arr1 = numpy.asarray([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 11, 14, 15, 55])
arr2 = numpy.asarray([11, 32, 43, 45, 66, 76, 88])

array = numpy.concatenate((arr1,arr2), axis=0)
array.sort()

วิธีที่ 2 - การใช้รายการสมมติว่ามีการเรียงลำดับรายการ

list_new = list1.extend(list2)
list_new.sort()

Since the question doesn't assume any specific languageจาก 2011/5/11/19: 43 มันเป็นที่ติดแท็กจาวา
greybeard

วิธีการแก้ปัญหาของคุณไม่ได้ใช้ประโยชน์จากรายการความจริงที่ถูกจัดเรียงแล้วและรันไทม์ของมันไม่ได้ O (n) เนื่องจาก.sort()เป็นO(n log n)ที่ที่ดีที่สุด
dark_ruby

-1

นี่คือการใช้ java ของฉันที่ลบซ้ำ

public static int[] mergesort(int[] a, int[] b) {
    int[] c = new int[a.length + b.length];
    int i = 0, j = 0, k = 0, duplicateCount = 0;

    while (i < a.length || j < b.length) {
        if (i < a.length && j < b.length) {
            if (a[i] == b[j]) {
                c[k] = a[i];
                i++;j++;duplicateCount++;
            } else {
                c[k] = a[i] < b[j] ? a[i++] : b[j++];
            }
        } else if (i < a.length) {
            c[k] = a[i++];
        } else if (j < a.length) {
            c[k] = b[j++];
        }
        k++;
    }

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