읽기 전에 알아두면 좋아요
아래 개념이 익숙하면 본문을 바로 읽으세요. 낯선 개념만 링크에서 복습하면 됩니다.
- 투 포인터의 이동 기준: 양끝 비교와 정렬 배열 병합투 포인터는 두 위치를 움직이며 비교할 범위를 좁히는 방법입니다. 두 정렬 배열에서 작은 쪽을 선택하고 남은 꼬리를 처리합니다.
- JavaScript 배열: 인덱스 조회와 삽입·삭제 비용배열은 여러 값을 순서대로 담습니다. 인덱스로 값을 읽고 반복문으로 하나씩 살펴봅니다. 인덱스 접근과 배열을 순회할 때의 연산 비용을 확인합니다.
개념과 예제는 실행하지 않아도 읽을 수 있습니다. 내 컴퓨터·터미널에서 직접 실행하는 과정은 선택 실습입니다.
과정 위치와 선택 복습 안내
과정 위치: 구간과 탐색 · 문제
과정 위치는 이 글이 속한 묶음입니다. 이 단계의 모든 개념을 먼저 읽을 필요는 없습니다. 위에 연결한 개념 중 낯선 내용만 복습하세요. 문제의 조건·상태·갱신 순서는 익숙한 언어로 설명하면 됩니다.
JavaScript로 문법·실행 복습하기 (선택)
개념 글의 코드는 JavaScript 예시입니다. 문제 화면에서는 JavaScript·Python·Java·C++·C# 중 익숙한 언어의 함수를 작성하세요. 본문의 Node.js 입출력 예시는 로컬 실행을 위한 별도 안내입니다.
정렬된 두 정수 배열 합치기에서는 어떤 입력을 받아 어떤 순서의 배열을 반환하나요?
정렬된 두 정수 배열을 입력받아 하나의 오름차순 배열을 반환하는 문제입니다. 배열·인덱스·반복문의 기본을 준비하고, 아래 입력·반환 계약을 확인해 직접 풀어보세요.
적용 조건·범위: 이 문제는 두 배열이 이미 정렬되어 있다는 조건을 줍니다. 즉, 다시 정렬하지 말고 “병합”만 하라는 의미입니다.
문제
오름차순으로 정렬이 된 두 배열이 주어지면 두 배열을 오름차순으로 합쳐 출력하는 프로그램을 작성하세요.
입력 설명
첫 번째 줄에 첫 번째 배열의 크기 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
직접 풀어보기
먼저 선택한 언어의 입력·반환 계약에 맞춰 작성해 보세요. 힌트 받기와 정답과 비교하기는 코드를 실행하지 않는 AI 분석입니다. 해답·해설은 아래에서 필요할 때 펼칠 수 있습니다.
AI 힌트
코드를 실행한 채점 결과가 아닌 AI의 코드 분석입니다.
해답·해설 보기
function solution(arr1, arr2) {
const result = [];
let i = 0, j = 0;
while (i < arr1.length && j < arr2.length) {
if (arr1[i] <= arr2[j]) result.push(arr1[i++]);
else result.push(arr2[j++]);
}
while (i < arr1.length) result.push(arr1[i++]);
while (j < arr2.length) result.push(arr2[j++]);
return result;
}
solution(arr1, arr2)에 arr1(정수 배열), arr2(정수 배열)를 전달하고 정수 배열를 반환합니다. 아래 문제의 유효 입력 조건을 따릅니다. 표준 입력과 출력 코드는 작성하지 않습니다.
def solution(arr1, arr2):
result = []
i = j = 0
while i < len(arr1) and j < len(arr2):
if arr1[i] <= arr2[j]:
result.append(arr1[i])
i += 1
else:
result.append(arr2[j])
j += 1
while i < len(arr1):
result.append(arr1[i])
i += 1
while j < len(arr2):
result.append(arr2[j])
j += 1
return result
solution(arr1, arr2)에 arr1(정수 배열), arr2(정수 배열)를 전달하고 정수 배열를 반환합니다. 아래 문제의 유효 입력 조건을 따릅니다. 표준 입력과 출력 코드는 작성하지 않습니다.
import java.util.*;
class Solution {
public int[] solution(int[] arr1, int[] arr2) {
int[] result = new int[arr1.length + arr2.length];
int i = 0, j = 0, k = 0;
while (i < arr1.length && j < arr2.length) {
if (arr1[i] <= arr2[j]) result[k++] = arr1[i++];
else result[k++] = arr2[j++];
} while (i < arr1.length) result[k++] = arr1[i++];
while (j < arr2.length) result[k++] = arr2[j++];
return result;
}
}
Solution.solution(int[] arr1, int[] arr2)에 arr1(정수 배열), arr2(정수 배열)를 전달하고 정수 배열를 반환합니다. 아래 문제의 유효 입력 조건을 따릅니다. 표준 입력과 출력 코드는 작성하지 않습니다.
#include <algorithm>
#include <string>
#include <vector>
#include <map>
#include <stack>
#include <stdexcept>
std::vector < int > solution(const std::vector < int > & arr1, const std::vector < int > & arr2) {
std::vector < int > result;
size_t i = 0, j = 0;
while (i < arr1.size() && j < arr2.size()) {
if (arr1[i] <= arr2[j]) result.push_back(arr1[i++]);
else result.push_back(arr2[j++]);
} while (i < arr1.size()) result.push_back(arr1[i++]);
while (j < arr2.size()) result.push_back(arr2[j++]);
return result;
}
solution(arr1, arr2)에 arr1(정수 배열), arr2(정수 배열)를 전달하고 정수 배열를 반환합니다. 아래 문제의 유효 입력 조건을 따릅니다. 표준 입력과 출력 코드는 작성하지 않습니다.
using System;
using System.Collections.Generic;
public class Solution {
public int[] Solve(int[] arr1, int[] arr2) {
int[] result = new int[arr1.Length + arr2.Length];
int i = 0, j = 0, k = 0;
while (i < arr1.Length && j < arr2.Length) {
if (arr1[i] <= arr2[j]) result[k++] = arr1[i++];
else result[k++] = arr2[j++];
} while (i < arr1.Length) result[k++] = arr1[i++];
while (j < arr2.Length) result[k++] = arr2[j++];
return result;
}
}
Solution.Solve(int[] arr1, int[] arr2)에 arr1(정수 배열), arr2(정수 배열)를 전달하고 정수 배열를 반환합니다. 아래 문제의 유효 입력 조건을 따릅니다. 표준 입력과 출력 코드는 작성하지 않습니다.
내가 푼 방식

두 배열을 합친 뒤 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. 입력이 정렬되어 있지 않다면 어떻게 해야 하나요?
먼저 정렬한 뒤 병합해야 합니다.
다만 이 문제는 정렬이 보장되므로, 정렬 단계 없이 병합만 하는 것이 정답입니다.
이 문제를 푼 뒤 확인하기
한쪽 배열의 값이 먼저 모두 소진되는 경우와 같은 값이 양쪽에 있는 경우를 병합해 보세요.
본문의 예제와 이 반례를 직접 실행해 예상 출력과 비교하고, 결과가 다르면 입력 조건과 반복 범위를 다시 확인하세요.
풀이 함수를 독립 실행해 확인합니다
선수 지식은 배열·조건문·반복문과 위에서 설명한 자료구조입니다. 정답의 완성 함수 블록 하나를 solution.cjs로 저장하고, 그 아래에 다음 호출을 붙입니다. 앞의 비교용 풀이와 같은 이름의 함수를 함께 붙이지 않습니다. 터미널에서 node solution.cjs로 실행합니다.
console.log(JSON.stringify(solution(...[[1, 1], [1, 2]])));
이 변형 호출의 JSON 출력은 [1,1,1,2]입니다. 기존 함수 블록의 console.log가 있으면 그 출력 다음 줄에서 확인합니다.
이 글이 도움이 되었나요?
이어서 공부하기
체크포인트: 해설을 가리고 다시 풀고, 경계 입력을 하나 바꾸어 예상 결과와 실제 결과를 비교하세요. 포인터를 움직여도 답을 버리지 않는 이유와 양수·0·음수의 차이를 설명하세요.
다음 추천 · 문제: 코딩테스트 투 포인터 풀이: 공통 원소 추출하기
권장 순서는 학습 가이드와 같습니다. 다른 순서는 접힌 참고 목록에서 확인할 수 있습니다. 읽음 표시는 숙달이나 채점 결과가 아닙니다.
원래 과정 목록 순서 (선택)
읽음 기록
읽음 — / 전체 58개 · 필수 58개
새 글 받아보기
RSS 리더에서 BlogFlow의 새 글을 확인할 수 있습니다.