Làm thế nào để hợp nhất hai mảng được sắp xếp thành một mảng được sắp xếp? [đóng cửa]


160

Điều này đã được hỏi về tôi trong một cuộc phỏng vấn và đây là giải pháp tôi cung cấp:

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;
}

Có cách nào hiệu quả hơn để làm điều này?

Chỉnh sửa: Phương pháp độ dài chính xác.


30
Có vẻ như một câu trả lời khá tốt cho tôi. Vấn đề này sẽ có độ phức tạp O (n) tốt nhất và câu trả lời của bạn đạt được điều đó. Bất cứ điều gì khác sẽ được vi mô hóa.
Hội trường Drew

3
Bạn đã làm tốt! Đây thực chất là một phần của sắp xếp hợp nhất: hợp nhất hai luồng được sắp xếp (từ băng hoặc đĩa) vào một luồng được sắp xếp khác.
Vladimir Dyuzhev

9
Bạn đã có công việc?
Shai

5
Ngoài ra, bạn có thể sử dụng toán tử ternary: while (i < a.length && j < b.length) answer[k++] = a[i] < b[j] ? a[i++] : b[j++]; Đặc tả ngôn ngữ Java: Toán tử có điều kiện? : .
Anton Dozortsev

1
Bạn quên bình luận !!!
LiziPizi

Câu trả lời:


33

Một cải tiến nhỏ, nhưng sau vòng lặp chính, bạn có thể sử dụng System.arraycopyđể sao chép phần đuôi của một trong hai mảng đầu vào khi bạn đi đến cuối của cái khác. Điều đó sẽ không thay đổi các O(n)đặc tính hiệu suất của giải pháp của bạn, mặc dù.


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;
}

Là nhỏ gọn hơn một chút nhưng chính xác như nhau!


Đối với người đã nói điều này gây ra một chỉ số ngoài giới hạn, bạn đang sử dụng đầu vào nào? Nó hoạt động trong tất cả các trường hợp cho tôi.
Mike Saull

1
Sử dụng vòng lặp for để hợp nhất các dòng khai báo các biến vòng lặp và điều khiển vòng lặp. Sử dụng các dòng trống đôi một cách tiết kiệm - trông không bị cản trở giữa các "bản sao đuôi" đối xứng.
greybeard

58

Tôi ngạc nhiên không ai đề cập đến việc thực hiện nhỏ gọn, hiệu quả và gọn gàng hơn nhiều này:

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;
}

Điểm ưa thích

  1. Lưu ý rằng nó thực hiện cùng hoặc ít số lượng hoạt động như bất kỳ thuật toán O (n) nào khác nhưng trong câu lệnh đơn theo nghĩa đen trong một vòng lặp while!
  2. Nếu hai mảng có cùng kích thước thì hằng số cho O (n) là như nhau. Tuy nhiên, nếu các mảng thực sự mất cân bằng thì các phiên bản System.arraycopysẽ giành chiến thắng vì trong nội bộ, nó có thể thực hiện điều này với hướng dẫn lắp ráp x86.
  3. Thông báo a[i] >= b[j]thay vì a[i] > b[j]. Điều này đảm bảo "tính ổn định" được định nghĩa khi các phần tử của a và b bằng nhau, chúng ta muốn các phần tử từ a trước b.

Đây là một cách tiếp cận thực sự tốt đẹp. Tôi gặp khó khăn khi đạt điểm chuẩn tốt trên các thuật toán sắp xếp Hợp nhất của mình trong Swift lang. Chuyển đổi điều này đã cho tôi những gì tôi cần, cảm ơn rất nhiều
Chackle 10/07/2015

Điểm của (j <0) trong vòng lặp while là gì? Btw, +1, Điều này thực sự tuyệt vời! Cám ơn vì đã chia sẻ.
Hengameh

2
@Hengameh trong trường hợp j < 0, bđã cạn kiệt, vì vậy chúng tôi tiếp tục thêm các ayếu tố còn lại vào answer mảng
Natan Streppel

6
Quá "thông minh" và khó đọc trong tâm trí tôi. Tôi thích đọc mã dễ dàng hơn đặc biệt là vì bạn không thực sự đạt được nhiều cải thiện hiệu suất với mã này.
Kevin M

1
điểm cộng cho Thông báo và [i]> = b [j] thay vì [i]> b [j]. Điều này đảm bảo "sự ổn định"
Yan Khonski

16

Bất kỳ cải tiến nào có thể được thực hiện sẽ là tối ưu hóa vi mô, thuật toán tổng thể là chính xác.


Nếu a lớn và b nhỏ thì thuật toán này sai.
jack

7
Nó không sai nhưng không hiệu quả.
jack

@jack làm thế nào bạn có thể làm điều đó nhanh hơn O (n) khi bạn đang sản xuất một mảng gồm n mặt hàng?
Sẽ

@will System.arrayCopy()nhanh một cách ngu ngốc vì nó sử dụng các memcpycuộc gọi được tối ưu hóa cho CPU . Vì vậy, có phạm vi để cải thiện hiệu suất bằng cách sao chép khối. Ngoài ra còn có phạm vi để tìm kiếm nhị phân cho các ranh giới.
mỏng

Đặc biệt là nếu bạn có thể sử dụng tính chất được sắp xếp để bỏ qua hầu hết các mục và không bao giờ so sánh chúng. Bạn thực sự có thể đánh bại O (n).
Tatarize

10

Giải pháp này cũng rất giống với các bài đăng khác ngoại trừ việc nó sử dụng System.arrayCopy để sao chép các thành phần mảng còn lại.

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

Đây là chức năng cập nhật. Nó loại bỏ các bản sao, hy vọng ai đó sẽ tìm thấy cái này có thể sử dụng được:

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, Cảm ơn bạn đã chia sẻ. Một câu hỏi: tại sao bạn chọn loại mảng và loại biến 'temp', dài?
Hengameh

(Tôi có nghi ngờ về tên phương pháp.)
lọ

5

Nó có thể được thực hiện trong 4 tuyên bố như dưới đây

 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
Tôi không hiểu tại sao câu trả lời này lại có phiếu bầu tiêu cực. Đúng là nó không hiệu quả. Nhưng đôi khi tất cả những gì bạn cần là hoàn thành công việc càng sớm càng tốt. Nếu bạn đang xử lý các mảng rất nhỏ, giả sử có ít hơn 100 phần tử, tôi thích sử dụng mã ở trên hơn là viết một đoạn mã dài sẽ không thực hiện bất kỳ cải tiến hiệu suất quan trọng nào. Vì vậy, cảm ơn Sudhir vì đã cung cấp giải pháp dễ dàng này và SANN3 để chỉnh sửa nó.
Ahmedov

2
Tiền đề bất thành văn là một sorthàm không thể sử dụng chính nó như một phương pháp sắp xếp. Đó sẽ là hồi quy vô hạn thay vì đệ quy. Ngoài ra, tiền đề khác là merge_array là hàm thực hiện sắp xếp. Do đó, câu trả lời này là không thể sử dụng trong bối cảnh rất có thể.
Aki Suihkonen

Câu hỏi được hỏi không đề cập rằng mã yêu cầu chỉ dành cho mảng nhỏ. Vì vậy, câu trả lời này sẽ gây hiểu nhầm trừ khi nó nêu rõ giới hạn của nó. Cũng nhìn vào câu trả lời của tôi dưới đây. Phải mất cùng một số dòng để viết mã hiệu quả hoạt động cho mọi kích thước mảng :)
Shital Shah

Câu hỏi quy định rằng các mảng đã được sắp xếp theo thứ tự. Nếu các mảng có thể rất lớn, giải pháp này sẽ dừng lại và hoạt động kém. Vì vậy, chắc chắn rằng bạn sẽ nhận được kết quả cuối cùng cần thiết, nhưng ứng dụng sẽ không hoạt động và bạn sẽ không nhận được công việc nếu tôi đang phỏng vấn.
Kevin M

Hàm Array.sort () sử dụng TimSort, phần lớn sẽ tìm thấy các lần chạy được sắp xếp và áp dụng sắp xếp hợp nhất trên chúng. Thật kỳ lạ, mã này thậm chí không thể bị lỗi vì "không hiệu quả", nó thực sự sẽ rơi vào hoàn thiện trong thời gian O (n) vì các lần chạy được sắp xếp. Bạn có thể chạy một loạt các điểm chuẩn trên đó, tỷ lệ cược là nó sẽ đánh bại mã OP khá thường xuyên.
Tatarize

4

Tôi đã phải viết nó bằng javascript, đây là:

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

Các bộ sưu tập Apache hỗ trợ phương thức đối chiếu kể từ phiên bản 4; bạn có thể làm điều này bằng collatephương pháp trong:

org.apache.commons.collections4.CollectionUtils

Đây là trích dẫn từ javadoc:

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

Hợp nhất hai Bộ sưu tập được sắp xếp abvào một Danh sách được sắp xếp duy nhất sao cho thứ tự của các phần tử theo Bộ so sánh c được giữ lại.

Đừng phát minh lại bánh xe! Tài liệu tham khảo: http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

Hợp nhất GallopSearch: O (log (n) * log (i)) thay vì O (n)

Tôi đã đi trước và thực hiện đề nghị greybeard trong các ý kiến. Chủ yếu là vì tôi cần một phiên bản nhiệm vụ quan trọng của mã này.

  • Mã này sử dụng một gallopSearch là O (log (i)) trong đó i là khoảng cách từ chỉ mục hiện tại mà chỉ mục có liên quan tồn tại.
  • Mã sử ​​dụng nhị phân Tìm kiếm sau khi tìm kiếm phi nước đại đã xác định phạm vi, phạm vi thích hợp. Vì phi nước đại giới hạn ở phạm vi nhỏ hơn, kết quả nhị phân Tìm kiếm cũng là O (log (i))
  • Việc phi nước đại và hợp nhất được thực hiện ngược. Điều này dường như không phải là nhiệm vụ quan trọng nhưng nó cho phép hợp nhất các mảng. Nếu một trong các mảng của bạn có đủ chỗ để lưu trữ các giá trị kết quả, bạn chỉ cần sử dụng nó làm mảng hợp nhất mảng kết quả. Bạn phải chỉ định phạm vi hợp lệ trong mảng trong trường hợp đó.
  • Nó không yêu cầu phân bổ bộ nhớ trong trường hợp đó (tiết kiệm lớn trong các hoạt động quan trọng). Nó chỉ đơn giản là đảm bảo rằng nó không và không thể ghi đè lên bất kỳ giá trị chưa được xử lý nào (chỉ có thể được thực hiện ngược). Trong thực tế, bạn sử dụng cùng một mảng cho cả hai đầu vào và kết quả. Nó sẽ không bị ảnh hưởng xấu.
  • Tôi luôn sử dụng Integer.compare () để có thể tắt các mục đích này.
  • Có một số cơ hội tôi có thể đã bị lừa một chút và không sử dụng thông tin mà tôi đã chứng minh trước đây. Chẳng hạn như tìm kiếm nhị phân vào một phạm vi gồm hai giá trị, trong đó một giá trị đã được kiểm tra. Cũng có thể có một cách tốt hơn để nêu vòng lặp chính, giá trị lật c sẽ không cần thiết nếu chúng được kết hợp thành hai thao tác theo trình tự. Vì bạn biết bạn sẽ làm cái này rồi cái kia mọi lúc. Có chỗ để đánh bóng.

Đây phải là cách hiệu quả nhất để làm điều này, với độ phức tạp thời gian của O (log (n) * log (i)) thay vì O (n). Và độ phức tạp thời gian trường hợp xấu nhất của O (n). Nếu các mảng của bạn bị vón cục và có các chuỗi giá trị dài với nhau, điều này sẽ lùn đi bất kỳ cách nào khác để làm điều đó, nếu không nó sẽ tốt hơn chúng.

Nó có hai giá trị đọc ở cuối mảng hợp nhất và giá trị ghi trong mảng kết quả. Sau khi tìm ra giá trị cuối nào ít hơn, nó thực hiện tìm kiếm phi nước đại vào mảng đó. 1, 2, 4, 8, 16, 32, v.v. Khi nó tìm thấy phạm vi mà giá trị đọc của mảng khác lớn hơn. Nó tìm kiếm nhị phân vào phạm vi đó (cắt phạm vi một nửa, tìm kiếm một nửa chính xác, lặp lại cho đến khi một giá trị duy nhất). Sau đó, nó sao chép các giá trị đó vào vị trí ghi. Hãy nhớ rằng bản sao, do cần thiết, đã di chuyển sao cho nó không thể ghi đè lên cùng các giá trị từ mảng đọc (có nghĩa là mảng ghi và mảng đọc có thể giống nhau). Sau đó, nó thực hiện thao tác tương tự cho mảng khác mà hiện được biết là nhỏ hơn giá trị đọc mới của mảng khác.

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;
}

Đây phải là cách hiệu quả nhất để làm điều đó.


Một số câu trả lời có khả năng loại bỏ trùng lặp. Điều đó sẽ yêu cầu thuật toán O (n) vì bạn thực sự phải so sánh từng mục. Vì vậy, đây là một độc lập cho điều đó, sẽ được áp dụng sau thực tế. Bạn không thể phi nước đại qua nhiều mục trong suốt quá trình nếu bạn cần xem tất cả chúng, mặc dù bạn có thể phi nước đại qua các bản sao, nếu bạn có rất nhiều mục.

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;
}

Cập nhật: Câu trả lời trước, mã không kinh khủng nhưng rõ ràng kém hơn những điều trên.

Một tối ưu hóa không cần thiết khác. Nó không chỉ gọi bản sao cho các bit cuối, mà còn cho đầu. Xử lý bất kỳ giới thiệu không trùng lặp trong O (log (n)) bằng nhị phân Tìm kiếm vào dữ liệu. O (log (n) + n) là O (n) và trong một số trường hợp, hiệu ứng sẽ được phát âm khá rõ, đặc biệt là những thứ như không có sự chồng chéo giữa các mảng hợp nhất.

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
Được khuyến khích để bắt đầu làm một cái gì đó về sự đối xứng, nhưng tại sao dừng lại ở đó? Sử dụng tìm kiếm phi mã, để nó trả về chỉ mục sau các khóa bằng nhau. Sử dụng bản sao mảng nếu có nhiều hơn 3 phần tử. Lưu ý rằng sau bản sao đó, không có gì thay đổi ngoài a) chỉ mục bắt đầu trong một đầu vào và mảng đầu ra b) kiến ​​thức của bạn về phần tử "tiếp theo" nào nhỏ hơn.
greybeard

Đó hoàn toàn là những gì Arrays.sort đã thực hiện. Đáng chú ý nhất là loại hợp nhất. Tôi nghĩ rằng họ trao đổi 2 yếu tố khi cần thiết nhưng rơi vào mảng nội dung cho hơn 2 yếu tố. Tôi không chắc chắn liệu bạn có kiểm tra phần tử tiếp theo hoặc tìm kiếm nhị phân vào phần tử đó không. Sẽ có một lợi thế khá lớn để kiểm tra theo suy đoán nếu bạn có thể phi nước đại một khoảng cách lớn hơn nếu bạn có thể phi nước đại khoảng cách đó. Giống như kiểm tra 8 trước và nếu bạn có thể sao chép rằng bạn đã tự lưu 7 thao tác cho những thứ bạn không cần phải xem.
Tatarize

@greybeard ... và đã xong. Cũng đã đi ngược lại để tôi có thể sử dụng lại bộ nhớ tương tự.
Tatarize

Điều tốt là bạn có động lực đi đạn đạo. Sẽ có một cái nhìn gần hơn sau khi chìm vào ban ngày.
greybeard

That is totally what the implemented Arrays.sort does( Đó là : từ lần sửa đổi đầu tiên của câu trả lời của bạn - hoặc - từ nhận xét ngày 19 tháng 2 của tôi?) - không thể tìm thấy trong JDK 8 của Sunsoft: Arrays.sortbạn đang đề cập đến việc triển khai nào?
greybeard

2

Đây là một hình thức rút gọn được viết bằng 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);
    }
}

Bản sao này a[mid+1 .. hi]để auxlàm gì?
greybeard

1

Tôi nghĩ rằng việc giới thiệu danh sách bỏ qua cho mảng được sắp xếp lớn hơn có thể làm giảm số lượng so sánh và có thể tăng tốc quá trình sao chép vào mảng thứ ba. Điều này có thể tốt nếu mảng quá lớn.


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
Một số lời giải thích sẽ tốt đẹp. :)
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;
}

Ân sủng tiết kiệm của điều này là gì? Nó có thể được thu nhỏ lại 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++];. Nó khác với câu trả lời năm 2014 của Andrew như thế nào?
greybeard

1

Thuật toán có thể được tăng cường theo nhiều cách. Ví dụ, nó là hợp lý để kiểm tra, nếu a[m-1]<b[0]hoặc b[n-1]<a[0]. Trong bất kỳ trường hợp nào, không cần phải so sánh nhiều hơn. Thuật toán chỉ có thể sao chép các mảng nguồn theo kết quả theo thứ tự đúng.

Các cải tiến phức tạp hơn có thể bao gồm tìm kiếm các phần xen kẽ và chỉ chạy thuật toán hợp nhất cho chúng. Nó có thể tiết kiệm nhiều thời gian, khi kích thước của các mảng được hợp nhất khác nhau về điểm số lần.


Đối với cải tiến này, tốt hơn là kiểm tra xem phần tử đầu tiên sẽ nằm ở mảng thứ hai với nghiên cứu nhị phân, sau đó phân mảng dữ liệu đó để bắt đầu. Sau đó, trong trường hợp một trong những kiểm tra đó là đúng, nó sẽ chỉ có bản sao tất cả mọi thứ sau đó phân mảng theo thứ tự và bạn sẽ nhận được kết quả tương tự. Nhưng, trong trường hợp có một chút trùng lặp, bạn chỉ cần thực hiện thuật toán thích hợp trong quá trình chồng lấp và không có thời gian khác. Vì bạn bị mắc kẹt với O (n) bằng cách sử dụng một số lệnh O (logn) nhanh chóng trước đó sẽ không tốn bất kỳ chi phí nào.
Tatarize

1

Vấn đề này liên quan đến thuật toán sáp nhập, trong đó hai mảng con được sắp xếp được kết hợp thành một mảng con được sắp xếp duy nhất. các CLRS cuốn sách đưa ra một ví dụ về thuật toán và dọn dẹp sự cần thiết của việc kiểm tra nếu cuối cùng đã đạt được bằng cách thêm một giá trị trọng điểm (cái gì mà so sánh và "lớn hơn bất kỳ giá trị khác") vào cuối của mỗi mảng.

Tôi đã viết cái này bằng Python, nhưng nó cũng sẽ dịch sang Java một cách độc đáo:

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

sao chép số lượng lớn các phần tử một lần qua (một cách khéo léo) bằng cách sử dụng sentinel
greybeard

1

Bạn có thể sử dụng 2 luồng để điền vào mảng kết quả, một từ phía trước, một từ phía sau.

Điều này có thể hoạt động mà không có bất kỳ sự đồng bộ hóa nào trong trường hợp số, ví dụ nếu mỗi luồng chèn một nửa giá trị.


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
Câu trả lời này không áp dụng cho ngôn ngữ lập trình Java, mặc dù nó sẽ là một câu trả lời tốt cho javascript.
gknicker

Đây là một phần của một cuộc phỏng vấn việc làm. Trong những trường hợp này, bạn không thực sự mong đợi viết mã "bình thường" như trên. Họ đang tìm kiếm mã "hiệu quả" và một minh chứng rằng bạn hiểu các thuật toán liên quan.
d11wtq

0

Ngôn ngữ lập trình yêu thích của tôi là 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

Có thể sử dụng 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
Bạn chỉ đang hợp nhất chúng; Mảng kết quả của bạn không được sắp xếp mà là một yêu cầu.
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 + ", ");
    }
}

Đầu ra là:

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


Nhầm lẫn cho việc đặt tên chỉ mục vào arr2không ind2, nhưng temp.
greybeard

0

Bạn có thể sử dụng các toán tử ternary để làm cho mã nhỏ gọn hơn một chút

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;
}

Chỉ khác một chút so với giải pháp ban đầu


0

Để điều chỉnh hai mảng được sắp xếp theo độ phức tạp thời gian O (m + n), chỉ sử dụng cách tiếp cận bên dưới với một vòng lặp. m và n là chiều dài của mảng thứ nhất và mảng thứ hai.

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

Vì câu hỏi không giả sử bất kỳ ngôn ngữ cụ thể. Đây là giải pháp trong Python. Giả sử các mảng đã được sắp xếp.

Cách tiếp cận 1 - sử dụng mảng numpy: nhập 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()

Cách tiếp cận 2 - Sử dụng danh sách, giả sử danh sách được sắp xếp.

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

Since the question doesn't assume any specific languagetừ 2011/5/11/19: 43, nó được gắn thẻ java .
greybeard

giải pháp của bạn không tận dụng lợi thế của các danh sách thực tế đã được sắp xếp, và thời gian chạy của nó không phải là O (n), vì .sort()O(n log n)lúc tốt nhất
dark_ruby

-1

Đây là thực hiện java của tôi mà loại bỏ trùng lặp.

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);
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.