코딩테스트 JS 투 포인터 풀이: 두 정렬 배열 합치기

2026.05.07·수정 2026.09.13·약 13분·작성: 해비·블로그 소개

주요 포인트 한눈에 보기

이미 오름차순인 두 배열을 다시 정렬하지 않고 O(N+M)에 병합하는 방법을 익히는 JavaScript 투 포인터 풀이입니다. 작은 입력의 출력형 채점에서는 sort() 풀이도 통과할 수 있지만, 정렬 조건을 활용한 선형 병합의 의도와 잔여 원소 처리, 숫자 정렬 비교 함수를 정확히 검증합니다.

문제

오름차순으로 정렬이 된 두 배열이 주어지면 두 배열을 오름차순으로 합쳐 출력하는 프로그램을 작성하세요.

입력 설명
첫 번째 줄에 첫 번째 배열의 크기 N(1 ≤ N ≤ 100)이 주어집니다.
두 번째 줄에 N개의 배열 원소가 오름차순으로 주어집니다.
세 번째 줄에 두 번째 배열의 크기 M(1 ≤ M ≤ 100)이 주어집니다.
네 번째 줄에 M개의 배열 원소가 오름차순으로 주어집니다.
각 리스트의 원소는 int형 변수의 크기를 넘지 않습니다.

출력 설명
오름차순으로 정렬된 배열을 출력합니다.

입력 예제
3
1 3 5
5
2 3 6 7 9

출력 예제
1 2 3 3 5 6 7 9

내가 푼 방식

두 정렬 배열 합치기 원인 진단: 배열 A 포인터 배열 B 포인터 작은 값 선택 흐름

두 배열을 합친 뒤 sort()로 다시 정렬하는 방식입니다.
구현은 쉽지만, 이 문제의 의도(정렬된 상태 활용)와는 다릅니다.

function solution(arr1, arr2) {
    return [...arr1, ...arr2].sort((a, b) => a - b);
}

console.log(solution([1, 3, 5], [2, 3, 6, 7, 9]));

동작: 합치고 정렬합니다. 결과는 맞게 나옵니다.

왜 이 방식이 아닌가

이 문제는 두 배열이 이미 정렬되어 있다는 조건을 줍니다.
즉, 다시 정렬하지 말고 “병합”만 하라는 의미입니다.

sort()는 (N+M)log(N+M)만큼 비교가 생깁니다.
반면 투 포인터 병합은 각 원소를 한 번씩만 보고 끝나서 O(N + M)입니다.
출력만 채점하는 문제에서는 제한 시간 안에 끝나면 정답이 될 수 있지만, 정렬 조건을 활용하는 출제 의도와 선형 복잡도를 보여주지는 못합니다.

정답

두 정렬 배열 합치기 해결 체크리스트: i 이동 j 이동 while 조건 흐름

투 포인터로 두 배열을 동시에 보면서 작은 값부터 결과에 넣습니다.
투 포인터로 두 배열을 동시에 보면서 작은 값부터 결과에 넣습니다.

같은 투 포인터의 양쪽 포인터 비교 방식은 중복 원소 비교 문제에서도 자주 쓰입니다. JS 투 포인터 공통 원소 추출로 확장해보면 비교 기준이 더 분명해집니다.

function solution(arr1, arr2) {
    let answer = [];
    let p1 = 0;
    let p2 = 0;

    while (p1 < arr1.length && p2 < arr2.length) {
        if (arr1[p1] <= arr2[p2]) {
            answer.push(arr1[p1]);
            p1++;
        } else {
            answer.push(arr2[p2]);
            p2++;
        }
    }

    while (p1 < arr1.length) {
        answer.push(arr1[p1]);
        p1++;
    }

    while (p2 < arr2.length) {
        answer.push(arr2[p2]);
        p2++;
    }

    return answer;
}

console.log(solution([1, 3, 5], [2, 3, 6, 7, 9]));

정답 해설

1) arr1[p1]과 arr2[p2]를 비교합니다.
2) 더 작은 값을 결과 배열에 넣습니다.
3) 넣은 쪽 포인터만 1칸 이동합니다.

한쪽 배열이 끝나면, 다른 배열의 남은 값은 이미 정렬되어 있으므로 그대로 이어 붙이면 됩니다.

시간 복잡도는 O(N + M)입니다.
포인터가 뒤로 가지 않아서 각 원소를 최대 한 번만 처리합니다.
JS 연속부분수열 투 포인터는 배열값이 양수인 경우 구간 합을 확장/축소하는 방식으로 같은 패턴을 다른 형태로 연습할 수 있습니다.

예제 디테일

기본 예제

console.log(solution([1, 3, 5], [2, 3, 6, 7, 9]));
// [1, 2, 3, 3, 5, 6, 7, 9]

동작: 두 포인터가 왼쪽부터 비교하며 작은 값을 순서대로 넣습니다.

메커니즘: 이미 정렬되어 있으니, 현재 작은 값을 선택해도 전체 순서가 깨지지 않습니다.

실수 포인트: 한쪽 배열이 끝난 뒤 남은 값을 붙이는 코드를 빼먹기 쉽습니다.

응용 예제

console.log(solution([-5, -1, 4, 10], [-3, 0, 0, 7]));
// [-5, -3, -1, 0, 0, 4, 7, 10]

동작: 음수와 중복이 있어도 비교 규칙은 동일합니다.

메커니즘: 값의 크기 범위는 상관없고, 정렬되어 있다는 조건만 중요합니다.

실수 포인트: 중복 처리에서 조건을 <로만 두면 결과 순서가 달라 보일 수 있습니다.
보통 <=로 한쪽 기준을 고정해 혼란을 줄입니다.

오류 예제

console.log(solution([3, 1, 5], [2, 6]));
// 입력이 정렬되어 있지 않으면 전제가 깨집니다.

동작: 투 포인터는 “뒤에 더 작은 값이 없다”고 가정합니다.

메커니즘: [3, 1, 5]처럼 정렬이 깨져 있으면 비교 선택이 틀어질 수 있습니다.

실수 포인트: 문제에 정렬이 보장되지 않으면 먼저 정렬해야 합니다.
이 문제는 정렬이 보장되므로 그 전제를 그대로 사용합니다.

개선 예제

function solutionFast(arr1, arr2) {
    const n = arr1.length;
    const m = arr2.length;
    const answer = new Array(n + m);

    let p1 = 0;
    let p2 = 0;
    let i = 0;

    while (p1 < n && p2 < m) {
        if (arr1[p1] <= arr2[p2]) {
            answer[i] = arr1[p1];
            p1++;
        } else {
            answer[i] = arr2[p2];
            p2++;
        }
        i++;
    }

    while (p1 < n) {
        answer[i] = arr1[p1];
        p1++;
        i++;
    }

    while (p2 < m) {
        answer[i] = arr2[p2];
        p2++;
        i++;
    }

    return answer;
}

동작: 정답 풀이와 같지만, 배열을 미리 만들고 인덱스로 채웁니다.

메커니즘: 데이터가 매우 큰 경우 push()보다 예측 가능한 동작을 기대할 수 있습니다.

실수 포인트: 인덱스 i 증가를 빼먹으면 값이 덮어써지거나 빈칸이 남습니다.

방식 비교 표

개념 요약 장점 단점 실무 사례
투 포인터로 병합 O(N+M), 정렬 조건 활용 잔여 처리 누락 가능 정렬된 로그/타임라인 병합
합친 뒤 sort() 구현이 매우 간단 O((N+M)log(N+M)) 작은 데이터 임시 처리

선택 기준: 코딩 테스트(정렬 보장)라면 투 포인터가 우선입니다.
입력이 작고 빠르게 끝내야 하는 작업이라면 sort()도 실무에서 선택될 수 있습니다.

복잡도·정렬 전제·검증 체크리스트

투 포인터 병합은 각 원소를 한 번씩만 결과에 넣어 O(N+M) 시간과 결과 배열 O(N+M) 공간을 사용합니다. 합친 뒤 다시 정렬하면 일반적으로 O((N+M) log(N+M))으로 설명하지만, JavaScript sort()의 구체적인 시간·공간 복잡도는 엔진 구현에 따라 달라질 수 있습니다.

sort()는 원본을 제자리에서 바꾸고, 비교 함수를 생략하면 숫자도 문자열로 바꿔 UTF-16 순서로 비교합니다. 따라서 숫자 배열에는 (a, b) => a - b가 필요합니다. 정확한 계약은 MDN Array.prototype.sort() 문서에서 확인하세요.

console.assert(JSON.stringify(solution([], [1, 2])) === '[1,2]');
console.assert(JSON.stringify(solution([1, 2], [])) === '[1,2]');
console.assert(JSON.stringify(solution([1, 1], [1, 2])) === '[1,1,1,2]');

한쪽 배열이 비었을 때, 같은 값이 양쪽에 있을 때, 음수와 0이 섞였을 때를 따로 검증하세요. 입력이 정렬되지 않았다면 이 알고리즘의 전제가 깨지므로 호출 전에 정렬하거나 별도의 오류 정책을 둬야 합니다.

정리: 출력형 채점은 결과와 제한 시간으로 판단하므로 작은 입력에서는 재정렬 풀이도 통과할 수 있습니다. 그래도 문제에서 제공한 정렬 조건을 활용해 선형 병합을 구현하고, 남은 원소 처리까지 증명하는 것이 이 유형의 핵심입니다.

FAQ

Q. sort()로 풀어도 정답이 될 수 있나요?
출력형 채점에서는 입력 크기와 제한 시간에 따라 통과할 수 있습니다.
다만 합친 뒤 다시 정렬하면 주어진 정렬 조건을 활용하지 못하고, 투 포인터의 O(N+M)보다 더 많은 비교가 필요합니다.

Q. 투 포인터는 언제 떠올리면 되나요?
정렬된 배열/리스트를 두 개 이상 동시에 다루거나, 양쪽 끝(경계)을 움직이는 문제가 나오면 먼저 떠올리면 됩니다.

Q. 한쪽이 끝나면 왜 남은 값을 그대로 붙여도 되나요?
남은 배열은 이미 오름차순입니다.
지금까지 결과에 들어간 값들은 그보다 작거나 같은 값들이므로, 그대로 이어 붙여도 정렬이 유지됩니다.

Q. 시간 복잡도 O(N+M)을 어떻게 설명하면 좋나요?
포인터는 뒤로 가지 않고 끝까지 한 번씩만 이동합니다.
그래서 각 원소를 최대 한 번만 처리하고, 전체 작업량은 두 길이의 합이 됩니다.

Q. 중복 값이 있으면 <=가 꼭 필요한가요?
정렬 결과만 요구한다면 <도 가능하지만, 한쪽 기준을 고정하려면 <=가 더 깔끔합니다.
중복이 많은 입력에서 흐름이 안정적으로 보입니다.

Q. 입력이 정렬되어 있지 않다면 어떻게 해야 하나요?
먼저 정렬한 뒤 병합해야 합니다.
다만 이 문제는 정렬이 보장되므로, 정렬 단계 없이 병합만 하는 것이 정답입니다.

풀이 전에 확인할 순서

  1. 입력값과 출력값을 한 문장으로 다시 적습니다.
  2. 반복할 대상과 비교·저장할 값을 정합니다.
  3. 필요한 자료구조와 시간복잡도를 예상합니다.
  4. 코드를 보기 전에 손으로 작은 예제를 계산합니다.
힌트: 코딩테스트 JS 투 포인터 풀이: 두 정렬 배열 합치기

정답 코드를 바로 따라 쓰기보다, 본문에서 값이 갱신되는 조건과 반복 범위를 먼저 찾으세요. 반복 한 번마다 반드시 유지되어야 하는 값이 무엇인지 적으면 풀이의 중심 변수를 고르기 쉽습니다.

테스트 확인

  • 가능한 가장 작은 입력
  • 같은 값이나 문자가 반복되는 입력
  • 정답이 처음 또는 마지막 위치에서 결정되는 입력
  • 입력 제한에 가까운 경우의 실행 시간

확인 결과: 본문의 예제뿐 아니라 위 경계 사례에서도 예상값과 실제 출력이 같아야 풀이가 완료됩니다.

이 글이 도움이 되었나요?

조회 중

코딩테스트 JavaScript 학습 순서

필수 49개 · 전체 49개

읽음 기록 관리

전체 과정 목차 (49개)
  1. 필수 길잡이 · 코딩테스트 JS 자료구조 로드맵: 배열, 해시, 스택, 투 포인터 순서
  2. 필수 학습 · 세 수 중 최솟값 JavaScript 조건문 풀이 정리
  3. 필수 학습 · 삼각형 판별하기 JavaScript 풀이
  4. 필수 학습 · 연필 개수 JavaScript 풀이
  5. 필수 학습 · 1부터 N까지 합 출력하기 JavaScript 풀이
  6. 필수 학습 · 최솟값 구하기 JavaScript 풀이|배열 순회와 비교 갱신 원리
  7. 필수 학습 · 홀수 JavaScript 풀이: 조건 판별과 결과 처리 정리
  8. 필수 학습 · 10부제 JavaScript 풀이: 끝자리 비교로 위반 차량 수 세기
  9. 필수 학습 · A를 #으로 JavaScript 풀이: 문자열 순회와 치환
  10. 필수 학습 · 문자 찾기 JavaScript 풀이: 문자열 순회로 개수 세기
  11. 필수 학습 · 대문자 찾기 JavaScript 풀이
  12. 필수 학습 · 대문자로 통일 JavaScript 풀이
  13. 필수 학습 · 대소문자 변환 JavaScript 풀이
  14. 필수 학습 · 일곱 난쟁이 JavaScript 풀이: 두 명을 제외하는 완전탐색
  15. 필수 학습 · 코딩테스트 JS Map 풀이: 학급 회장 득표수 세기
  16. 필수 학습 · 코딩테스트 JS 스택 풀이: 올바른 괄호 검증하기
  17. 필수 학습 · 코딩테스트 JS 스택 풀이: 괄호문자 제거하기
  18. 필수 학습 · 코딩테스트 JS 스택 풀이: 크레인 인형뽑기 처리법
  19. 필수 학습 · 코딩테스트 JS 스택 풀이: 후위식 연산 계산하기
  20. 필수 학습 · 코딩테스트 JS 스택 풀이: 쇠막대기 레이저 절단 개수 세기
  21. 필수 학습 · 코딩테스트 JS 투 포인터 풀이: 두 정렬 배열 합치기 현재 글
  22. 필수 학습 · 코딩테스트 JS 투 포인터 풀이: 공통 원소 추출하기
  23. 필수 학습 · 코딩테스트 JS 슬라이딩 윈도우 풀이: 최대 매출 구간 합 계산하기
  24. 필수 학습 · JavaScript 투 포인터: 합이 M인 연속 부분수열 개수
  25. 필수 학습 · 코딩테스트 JS 해시 풀이: 모든 아나그램 찾기
  26. 필수 학습 · 가장 긴 문자열 JavaScript 풀이
  27. 필수 학습 · 가운데 문자 출력 JavaScript 풀이
  28. 필수 학습 · 중복문자제거 JavaScript 풀이
  29. 필수 학습 · 코딩테스트 JS 고급: 최소 힙으로 다익스트라 최단 경로 구하기
  30. 필수 학습 · 코딩테스트 JS Union-Find: 연결 성분 수와 크기 구하기
  31. 필수 학습 · 코딩테스트 JS Trie: 접두사에 맞는 단어 수 세기
  32. 필수 학습 · 코딩테스트 JS Fenwick Tree: 값 갱신과 구간 합 처리
  33. 필수 학습 · 코딩테스트 JS 세그먼트 트리: 단일 대입과 구간 합
  34. 필수 학습 · 코딩테스트 JS LRU 캐시: 지도 타일 재사용 기록
  35. 필수 학습 · 코딩테스트 JS AVL 트리: 기준 이상 최솟값 찾기
  36. 필수 학습 · 코딩테스트 JS 큐: 상담 창구 대기열 명령 처리
  37. 필수 학습 · 코딩테스트 JS 연결 리스트: 재생 대기 목록 관리
  38. 필수 학습 · JavaScript 원형 덱 연습: 최근 기록 창과 되돌리기
  39. 필수 학습 · JavaScript 해시 테이블 연습: 정규화 문자열 빈도와 등장 순서
  40. 필수 학습 · JavaScript 트리 순회 연습: 깊이별 노드 묶기
  41. 필수 학습 · JavaScript BST 연습: 닫힌 구간의 중복 키 보고서
  42. 필수 학습 · JavaScript 최소 힙 연습: 동률 순서를 지키는 작업 스케줄러
  43. 필수 학습 · JavaScript 그래프 연습: 연결 구역 크기를 작은 순서로 출력하기
  44. 필수 학습 · 중복단어제거 JavaScript 풀이
  45. 필수 학습 · TypeScript 이진 탐색 연습: 숫자 카드 존재 여부 확인
  46. 필수 학습 · 큰 수 출력하기 JavaScript 풀이
  47. 필수 학습 · 보이는 학생 JavaScript 풀이
  48. 필수 학습 · 가위바위보 JavaScript 풀이
  49. 필수 학습 · 점수계산 JavaScript 풀이

새 글 받아보기

RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.

RSS 피드 구독하기

댓글 남기기