TypeScript 이진 탐색 연습: 숫자 카드 존재 여부 확인

2026.09.11·수정 2026.09.13·약 7분·작성: 해비·블로그 소개
먼저 확인할 내용

숫자 카드 목록을 한 번 정렬한 뒤, 여러 숫자의 존재 여부를 이진 탐색으로 확인합니다. 이 글의 입력과 제약은 학습용으로 정의했으며 특정 사이트 문제의 원문을 옮긴 것이 아닙니다.

직접 풀어볼 문제

TypeScript 이진 탐색으로 숫자 카드 존재 여부를 판단하는 콘셉트 이미지

서로 다른 정수가 적힌 카드 N개와 확인할 숫자 M개가 주어집니다. 확인할 숫자가 카드에 있으면 1, 없으면 0을 순서대로 출력하세요. 입력은 N, 카드 N개, M, 확인할 숫자 M개 순서이며 공백과 줄바꿈으로 구분합니다.

  • 1 ≤ N, M ≤ 100,000
  • 각 정수는 -10,000,000 이상 10,000,000 이하
  • 카드는 서로 다른 정수이며, 확인할 숫자는 반복될 수 있음
5
-7 2 9 15 21
6
9 0 -7 21 22 9

예상 출력

1 0 1 1 0 1

먼저 includes로 각 숫자를 확인하는 방법과, 정렬된 배열에서 범위를 절반씩 줄이는 방법의 비용을 비교해 보세요.

한 번 정렬하고 여러 번 찾는 이유

질의마다 카드 전체를 확인하면 최악의 경우 N×M번 수준의 비교가 필요합니다. 카드를 정렬해 두면 중간값과 목표값의 크기를 비교해 남은 구간 절반을 제외할 수 있습니다. 정렬 자체에는 비용이 들지만 질의가 많을수록 반복 탐색 비용을 줄일 수 있습니다.

Set도 존재 여부 확인에 적합합니다. 이 글에서는 정렬된 데이터의 탐색 범위를 관리하는 연습을 위해 이진 탐색을 사용합니다. 이진 탐색이 모든 상황에서 Set보다 빠르다는 뜻은 아닙니다.

9를 찾는 과정과 0이 없음을 확인하는 과정

목표 left right mid 중간값 다음 동작
9 0 4 2 9 같으므로 true
0 0 4 2 9 right = 1
0 0 1 0 -7 left = 1
0 1 1 1 2 right = 0
0 1 0 left > right이므로 false

left와 right는 현재 후보 구간의 양끝 인덱스입니다. 비교한 mid는 다음 후보에서 제외하므로 mid+1 또는 mid-1로 갱신합니다. mid를 다시 포함시키면 구간이 줄지 않아 반복문이 끝나지 않을 수 있습니다.

TypeScript 풀이

export function binarySearch(sorted: readonly number[], target: number): boolean {
  let left = 0;
  let right = sorted.length - 1;
  while (left <= right) {
    const mid = left + Math.floor((right - left) / 2);
    if (sorted[mid] === target) return true;
    if (sorted[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
  return false;
}
export function solve(input: string): string {
  const tokens = input.trim().split(/s+/).map(Number);
  let index = 0;
  const n = tokens[index++];
  const cards = tokens.slice(index, index + n);
  index += n;
  const m = tokens[index++];
  const queries = tokens.slice(index, index + m);
  cards.sort((a, b) => a - b);
  return queries.map((target) => binarySearch(cards, target) ? "1" : "0").join(" ");
}

위 코드를 solution.ts로 저장합니다. solve는 입력 문자열을 받아 출력 문자열을 반환하므로 파일 입출력과 분리해 검증할 수 있습니다. 아래의 main.ts는 표준 입력을 읽고 결과를 출력하는 실행 진입점입니다.

import { readFileSync } from "node:fs";
import { solve } from "./solution.js";
console.log(solve(readFileSync(0, "utf8")));

TypeScript와 Node 타입이 설치된 환경에서 아래처럼 컴파일합니다. input.txt에는 앞의 예제 입력을 넣습니다.

npx tsc solution.ts main.ts --target ES2022 --module NodeNext --moduleResolution NodeNext --outDir dist
node dist/main.js < input.txt

정수 정렬에는 숫자 비교 함수가 필요합니다. 또한 입력을 공백 전체로 나누는 정규식은 /s+/입니다. 역슬래시가 빠진 /s+/와 다르므로 복사 후 확인하세요.

시간·공간 비용

통상적인 비교 정렬 비용을 O(N log N)으로 보고, 질의마다 O(log N)이 걸리므로 전체는 O(N log N + M log N)입니다. 이 표기는 정렬 구현에 대한 일반적인 분석 가정이며 JavaScript 표준이 특정 정렬 알고리즘을 지정한다는 뜻은 아닙니다.

반복형 binarySearch 자체는 추가 공간 O(1)입니다. 그러나 전체 프로그램은 입력 토큰, 카드·질의 배열과 출력 배열을 보관하므로 정렬 내부 작업 공간을 제외해도 O(N+M)이 필요합니다. 탐색 함수 공간과 프로그램 전체 공간을 구분해야 합니다.

경계값과 반례 확인

카드 질의 예상 결과
[5] [5, 4] 1 0
[-7, 2, 9] [-7, 9, 10] 1 1 0
[21, 2, -7] [2, 0] 1 0
[-1, 0, 1] [0, 0, 2] 1 1 0

입력 카드 순서가 뒤섞인 경우, 첫 값과 마지막 값, 없는 값, 같은 숫자를 반복해서 찾는 경우를 확인합니다. 이진 탐색 함수에 직접 전달하는 배열은 반드시 정렬되어 있어야 합니다.

학습용 입력은 조건을 만족한다고 가정합니다. 일반 사용자 입력을 받는 서비스에 재사용한다면 토큰 수, 숫자 형식, 범위 검사도 추가해야 합니다.

풀이 전에 확인할 순서

  1. 입력값과 출력값을 한 문장으로 다시 적습니다.
  2. 반복할 대상과 비교·저장할 값을 정합니다.
  3. 필요한 자료구조와 시간복잡도를 예상합니다.
  4. 코드를 보기 전에 손으로 작은 예제를 계산합니다.
힌트: TypeScript 이진 탐색 연습: 숫자 카드 존재 여부 확인

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

테스트 확인

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

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

이 글이 도움이 되었나요?

조회 중

코딩테스트 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 피드 구독하기

댓글 남기기