주요 포인트 한눈에 보기
이미 오름차순인 두 배열을 다시 정렬하지 않고 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
내가 푼 방식

두 배열을 합친 뒤 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)입니다.
출력만 채점하는 문제에서는 제한 시간 안에 끝나면 정답이 될 수 있지만, 정렬 조건을 활용하는 출제 의도와 선형 복잡도를 보여주지는 못합니다.
정답

투 포인터로 두 배열을 동시에 보면서 작은 값부터 결과에 넣습니다.
투 포인터로 두 배열을 동시에 보면서 작은 값부터 결과에 넣습니다.
같은 투 포인터의 양쪽 포인터 비교 방식은 중복 원소 비교 문제에서도 자주 쓰입니다. 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. 입력이 정렬되어 있지 않다면 어떻게 해야 하나요?
먼저 정렬한 뒤 병합해야 합니다.
다만 이 문제는 정렬이 보장되므로, 정렬 단계 없이 병합만 하는 것이 정답입니다.
풀이 전에 확인할 순서
- 입력값과 출력값을 한 문장으로 다시 적습니다.
- 반복할 대상과 비교·저장할 값을 정합니다.
- 필요한 자료구조와 시간복잡도를 예상합니다.
- 코드를 보기 전에 손으로 작은 예제를 계산합니다.
힌트: 코딩테스트 JS 투 포인터 풀이: 두 정렬 배열 합치기
정답 코드를 바로 따라 쓰기보다, 본문에서 값이 갱신되는 조건과 반복 범위를 먼저 찾으세요. 반복 한 번마다 반드시 유지되어야 하는 값이 무엇인지 적으면 풀이의 중심 변수를 고르기 쉽습니다.
테스트 확인
- 가능한 가장 작은 입력
- 같은 값이나 문자가 반복되는 입력
- 정답이 처음 또는 마지막 위치에서 결정되는 입력
- 입력 제한에 가까운 경우의 실행 시간
확인 결과: 본문의 예제뿐 아니라 위 경계 사례에서도 예상값과 실제 출력이 같아야 풀이가 완료됩니다.
이 글이 도움이 되었나요?
코딩테스트 JavaScript 학습 순서
필수 49개 · 전체 49개
읽음 기록 관리
전체 과정 목차 (49개)
- 필수 길잡이 · 코딩테스트 JS 자료구조 로드맵: 배열, 해시, 스택, 투 포인터 순서
- 필수 학습 · 세 수 중 최솟값 JavaScript 조건문 풀이 정리
- 필수 학습 · 삼각형 판별하기 JavaScript 풀이
- 필수 학습 · 연필 개수 JavaScript 풀이
- 필수 학습 · 1부터 N까지 합 출력하기 JavaScript 풀이
- 필수 학습 · 최솟값 구하기 JavaScript 풀이|배열 순회와 비교 갱신 원리
- 필수 학습 · 홀수 JavaScript 풀이: 조건 판별과 결과 처리 정리
- 필수 학습 · 10부제 JavaScript 풀이: 끝자리 비교로 위반 차량 수 세기
- 필수 학습 · A를 #으로 JavaScript 풀이: 문자열 순회와 치환
- 필수 학습 · 문자 찾기 JavaScript 풀이: 문자열 순회로 개수 세기
- 필수 학습 · 대문자 찾기 JavaScript 풀이
- 필수 학습 · 대문자로 통일 JavaScript 풀이
- 필수 학습 · 대소문자 변환 JavaScript 풀이
- 필수 학습 · 일곱 난쟁이 JavaScript 풀이: 두 명을 제외하는 완전탐색
- 필수 학습 · 코딩테스트 JS Map 풀이: 학급 회장 득표수 세기
- 필수 학습 · 코딩테스트 JS 스택 풀이: 올바른 괄호 검증하기
- 필수 학습 · 코딩테스트 JS 스택 풀이: 괄호문자 제거하기
- 필수 학습 · 코딩테스트 JS 스택 풀이: 크레인 인형뽑기 처리법
- 필수 학습 · 코딩테스트 JS 스택 풀이: 후위식 연산 계산하기
- 필수 학습 · 코딩테스트 JS 스택 풀이: 쇠막대기 레이저 절단 개수 세기
- 필수 학습 · 코딩테스트 JS 투 포인터 풀이: 두 정렬 배열 합치기 현재 글
- 필수 학습 · 코딩테스트 JS 투 포인터 풀이: 공통 원소 추출하기
- 필수 학습 · 코딩테스트 JS 슬라이딩 윈도우 풀이: 최대 매출 구간 합 계산하기
- 필수 학습 · JavaScript 투 포인터: 합이 M인 연속 부분수열 개수
- 필수 학습 · 코딩테스트 JS 해시 풀이: 모든 아나그램 찾기
- 필수 학습 · 가장 긴 문자열 JavaScript 풀이
- 필수 학습 · 가운데 문자 출력 JavaScript 풀이
- 필수 학습 · 중복문자제거 JavaScript 풀이
- 필수 학습 · 코딩테스트 JS 고급: 최소 힙으로 다익스트라 최단 경로 구하기
- 필수 학습 · 코딩테스트 JS Union-Find: 연결 성분 수와 크기 구하기
- 필수 학습 · 코딩테스트 JS Trie: 접두사에 맞는 단어 수 세기
- 필수 학습 · 코딩테스트 JS Fenwick Tree: 값 갱신과 구간 합 처리
- 필수 학습 · 코딩테스트 JS 세그먼트 트리: 단일 대입과 구간 합
- 필수 학습 · 코딩테스트 JS LRU 캐시: 지도 타일 재사용 기록
- 필수 학습 · 코딩테스트 JS AVL 트리: 기준 이상 최솟값 찾기
- 필수 학습 · 코딩테스트 JS 큐: 상담 창구 대기열 명령 처리
- 필수 학습 · 코딩테스트 JS 연결 리스트: 재생 대기 목록 관리
- 필수 학습 · JavaScript 원형 덱 연습: 최근 기록 창과 되돌리기
- 필수 학습 · JavaScript 해시 테이블 연습: 정규화 문자열 빈도와 등장 순서
- 필수 학습 · JavaScript 트리 순회 연습: 깊이별 노드 묶기
- 필수 학습 · JavaScript BST 연습: 닫힌 구간의 중복 키 보고서
- 필수 학습 · JavaScript 최소 힙 연습: 동률 순서를 지키는 작업 스케줄러
- 필수 학습 · JavaScript 그래프 연습: 연결 구역 크기를 작은 순서로 출력하기
- 필수 학습 · 중복단어제거 JavaScript 풀이
- 필수 학습 · TypeScript 이진 탐색 연습: 숫자 카드 존재 여부 확인
- 필수 학습 · 큰 수 출력하기 JavaScript 풀이
- 필수 학습 · 보이는 학생 JavaScript 풀이
- 필수 학습 · 가위바위보 JavaScript 풀이
- 필수 학습 · 점수계산 JavaScript 풀이
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.