정렬 된 두 배열을 정렬 된 배열로 병합하는 방법은 무엇입니까? [닫은]


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
잘 했어! 이것은 본질적으로 병합 정렬의 일부입니다 (테이프 또는 디스크에서 정렬 된 두 개의 스트림을 다른 정렬 된 스트림으로 병합).
블라디미르 듀우 예프

9
직업이 있습니까?
Shai

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
루프 변수와 루프 제어를 선언하는 행을 병합하려면 for 루프를 사용하십시오. 이중 공백 줄은 드물게 사용하십시오-대칭 "꼬리 사본"사이에서 불필요하게 보입니다.
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) 알고리즘 과 동일하거나 적은 수의 연산을 수행하지만 문자 그대로 단일 while 루프의 단일 명령문으로 수행됩니다!
  2. 두 배열의 크기가 거의 같으면 O (n)의 상수는 동일합니다. 그러나 배열이 실제로 불균형하면 System.arraycopy내부적으로 단일 x86 어셈블리 명령어로이를 수행 할 수 있기 때문에 버전 이 승리합니다.
  3. a[i] >= b[j]대신에 유의하십시오 a[i] > b[j]. 이것은 a와 b의 요소가 같을 때 정의되는 "안정성"을 보장하며, b 이전의 요소를 원합니다.

이것은 정말 좋은 접근법입니다. Swift lang의 Merge 정렬 알고리즘에 대한 좋은 벤치 마크를 얻는 데 어려움이있었습니다. 이것을 변환하면 내가 필요한 것을 줬습니다. 감사합니다
Chackle

while 루프에서 (j <0)의 요점은 무엇입니까? Btw, +1, 정말 멋지다! 공유해 주셔서 감사합니다.
Hengameh

2
@Hengameh 경우에 j < 0, b우리는 나머지 계속 추가 할 수 있도록 이미 고갈 a받는 요소를 answer 배열
나탄 Streppel

6
너무 "영리한"내 마음에 읽기 어렵다. 이 코드로 성능을 크게 향상시키지 못하기 때문에 코드를 읽기가 더 쉽습니다.
Kevin M

1
주의점의 플러스 포인트, a [i]> b [j] 대신 a [i]> = b [j]. 이것은 "안정성"을 보장합니다
Yan Khonski

16

수행 할 수있는 모든 개선 사항은 미세 최적화이며 전체 알고리즘이 정확합니다.


a가 크고 b가 작 으면이 알고리즘이 잘못되었습니다.
jack

7
잘못은 아니지만 효율적이지 않습니다.
jack

@ jack n 개의 항목을 생성 할 때 어떻게 O (n)보다 빠르게 할 수 있습니까?
Will

@will System.arrayCopy()은 CPU 최적화 memcpy호출을 사용하기 때문에 어리석게 빠릅니다 . 따라서 청크를 복사하여 성능을 향상시킬 수있는 범위가 있습니다. 경계에 대한 이진 검색 범위도 있습니다.
날씬한

특히 정렬 된 특성을 사용하여 대부분의 항목을 건너 뛰고 비교하지 않는 경우. 실제로 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 Shah

이 질문은 배열이 이미 정렬 된 순서로되어 있다고 규정했다. 어레이가 매우 클 수있는 경우이 솔루션은 중단되고 성능이 저하됩니다. 따라서 필요한 최종 결과를 얻을 수는 있지만 앱이 수행되지 않으며 인터뷰 중일 때 일자리를 얻지 못할 것입니다.
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

아파치 컬렉션은 버전 4부터 collate 메소드를 지원합니다. 다음 collate방법을 사용하여이 작업을 수행 할 수 있습니다 .

org.apache.commons.collections4.CollectionUtils

다음은 javadoc에서 인용 한 것입니다.

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

비교기 c에 따라 요소의 순서가 유지되도록 두 개의 정렬 된 콜렉션 ab를 하나의 정렬 된 목록으로 병합 합니다.

바퀴를 다시 발명하지 마십시오! 문서 참조 : http://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html


4

GallopSearch 병합 : O (n) 대신 O (log (n) * log (i) )

나는 의견에 회색 수염 제안을 이행했습니다. 주로이 코드의 매우 효율적인 미션 크리티컬 버전이 필요했기 때문입니다.

  • 이 코드는 O (log (i)) 인 gallopSearch를 사용합니다. 여기서 i는 현재 색인에서 관련 색인이 존재하는 거리입니다.
  • 이 코드는 갤럽 검색에서 적절한 범위를 식별 한 후 이진 검색을 사용합니다. 갤럽이이를 더 작은 범위로 제한했기 때문에 결과 binarySearch도 O (log (i))입니다.
  • 갤럽 및 병합은 거꾸로 수행됩니다. 이것은 미션 크리티컬 한 것처럼 보이지 않지만 배열을 병합 할 수 있습니다. 배열 중 하나에 결과 값을 저장할 공간이 충분하면 병합 배열 결과 배열 로 간단히 사용할 수 있습니다 . 이 경우 배열 내에 유효한 범위를 지정해야합니다.
  • 이 경우 메모리 할당이 필요하지 않습니다 (중요한 작업에서 크게 절약). 단순히 처리되지 않은 값을 덮어 쓸 수 없으며 덮어 쓸 수 없습니다 (뒤로 만 가능). 실제로 입력과 결과 모두에 동일한 배열을 사용합니다. 아무런 영향을 미치지 않습니다.
  • 나는 Integer.compare ()를 일관되게 사용하여 다른 목적으로 전환 할 수있었습니다.
  • 내가 약간 증명했을 수도 있고 내가 이전에 입증 한 정보를 활용하지 못할 수도 있습니다. 하나의 값이 이미 확인 된 두 값의 범위로 이진 검색과 같은. 메인 루프를 명시하는 더 좋은 방법이있을 수도 있습니다. 뒤집기 c 값이 순서대로 두 개의 연산으로 결합되면 필요하지 않습니다. 당신은 당신이 매번 다른 하나를 할 것을 알고 있기 때문에. 광택을 내야 할 여지가 있습니다.

이것은 O (n)이 아니라 O (log (n) * log (i))의 시간 복잡성과 함께 가장 효율적인 방법이어야합니다 . 그리고 최악의 경우 시간 복잡도 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를 호출합니다. binarySearch를 사용하여 O (log (n))의 소개 비 중첩을 데이터로 처리합니다. 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
대칭에 대해 무언가를 시작한 것에 대해 찬성했지만 왜 거기서 멈추는가? 급속도 검색을 사용하고 동일한 키 다음에 색인 반환하도록 합니다. 요소가 3 개 이상인 경우에만 배열 복사를 사용하십시오. 그 복사 후에는 a) 한 입력의 시작 색인과 출력 배열 b) 어떤 "다음"요소가 더 작은 지에 대한 지식 만 변경되었습니다.
greybeard

이것이 구현 된 Arrays.sort의 기능입니다. 특히 병합 정렬이 최악입니다. 필요한 경우 2 개의 요소를 교체하지만 2 개 이상의 요소에 대해 배열 복사에 속합니다. 다음 요소를 선형 또는 이진 검색으로 확인할지 확실하지 않습니다. 그 거리를 갤럽 할 수 있다면 더 큰 거리를 갤럽 할 수 있는지 추측 적으로 확인하는 것이 상당히 큰 이점이 있습니다. 앞서 확인 8처럼, 당신이 복사 할 수 있다면 당신이 볼 필요가없는 것들의 7 가지 작업을 저장했습니다.
Tataize

@ greybeard ... 그리고 완료. 또한 같은 메모리를 재사용 할 수 있도록 뒤로 이동했습니다.
Tatarize

탄도가 되겠다고 동기를 부여한 것이 좋습니다. 주간 타임 싱크 후 자세히 살펴볼 것입니다.
greybeard

That is totally what the implemented Arrays.sort does( 그것은 : 당신의 대답의 첫 번째 개정에서-또는-내 2 월 19 일 코멘트에서?)-Sunsoft의 JDK 8에서 찾을 수 없습니다 Arrays.sort.
greybeard

2

다음은 자바 스크립트로 작성된 단축 양식입니다.

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 2016 년

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]or인지 확인하는 것이 합리적 b[n-1]<a[0]입니다. 이러한 경우에는 더 이상 비교할 필요가 없습니다. 알고리즘은 소스 배열을 결과 순서대로 올바른 순서로 복사 할 수 있습니다.

더 복잡한 개선 사항에는 인터리빙 부품 검색 및 병합 알고리즘 실행 만 포함될 수 있습니다. 병합 된 어레이의 크기가 점수에 따라 다를 때 많은 시간을 절약 할 수 있습니다.


이 향상을 위해 첫 번째 요소가 이진 검색으로 두 번째 배열의 어디에 있는지 확인한 다음 해당 데이터를 배열 복사하여 시작합니다. 그런 다음 해당 검사 중 하나가 참이면 모든 것을 arraycopy하고 삼항을 arraycopy하면 동일한 결과를 얻습니다. 그러나 약간의 오버랩이 발생하는 경우 오버랩 동안 적절한 알고리즘 만 수행하면되고 다른 시간은 필요하지 않습니다. 빠른 O (logn) 명령을 사용하여 O (n)에 붙어 있기 때문에 사전에 비용이 들지 않습니다.
Tatarize

1

이 문제는 두 개의 정렬 된 하위 배열이 단일 정렬 된 하위 배열로 결합되는 mergesort 알고리즘과 관련이 있습니다. CLRS의 책은 알고리즘의 일례를 제공하고 결국 각 어레이의 단부 (비교 무언가 "이외의 값보다 큰"참조) 센티널 값을 추가하여 도달하는 경우 검사에 대한 필요성을 정리.

나는 이것을 파이썬으로 작성했지만 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

요소를 한 번에 (skilfully) 한 번에 복사하여 센티넬을 사용하는 경우…
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 프로그래밍 언어에는 적용되지 않지만 javascript에는 적합합니다.
gknicker

이것은 면접이었습니다. 이 경우 실제로 위와 같은 "정상적인"코드를 작성하지 않아도됩니다. 그들은 "효율적인"코드와 관련 알고리즘을 이해하고 있다는 데모를 찾고 있습니다.
d11wtq 2018 년

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은 제 1 어레이 및 제 2 어레이의 길이이다.

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 language2011/5/11/19 : 43부터 java 태그가 지정되었습니다 .
greybeard 2012

때문에, 사실 목록을 활용하지 않는 솔루션은 이미 분류되어, 그 실행은하지 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.