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

··설명 약 6분·작성: 해비·블로그 소개

읽기 전에 알아두면 좋아요

아래 개념이 익숙하면 본문을 바로 읽으세요. 낯선 개념만 링크에서 복습하면 됩니다.

개념과 예제는 실행하지 않아도 읽을 수 있습니다. 내 컴퓨터·터미널에서 직접 실행하는 과정은 선택 실습입니다.

과정 위치와 선택 복습 안내

과정 위치: 구간과 탐색 · 문제

과정 위치는 이 글이 속한 묶음입니다. 이 단계의 모든 개념을 먼저 읽을 필요는 없습니다. 위에 연결한 개념 중 낯선 내용만 복습하세요. 문제의 조건·상태·갱신 순서는 익숙한 언어로 설명하면 됩니다.

JavaScript로 문법·실행 복습하기 (선택)

개념 글의 코드는 JavaScript 예시입니다. 문제 화면에서는 JavaScript·Python·Java·C++·C# 중 익숙한 언어의 함수를 작성하세요. 본문의 Node.js 입출력 예시는 로컬 실행을 위한 별도 안내입니다.

이 글의 TypeScript 컴파일 예제는 선택 실습입니다. 이진 탐색을 이해하려고 TypeScript 도구를 새로 설치할 필요는 없습니다. 화면에서 고른 언어의 함수로 같은 탐색 구간을 추적하세요.

숫자 카드 문제는 카드 목록과 여러 질의를 받아 각 질의의 무엇을 1 또는 0으로 반환하나요?

카드 목록과 여러 질의를 입력받아 각 숫자의 존재 여부를 1 또는 0으로 반환하는 문제입니다. 배열·정렬·탐색 구간의 기본을 준비하고, 아래 입력·반환 계약을 확인해 직접 풀어보세요.

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

공식 문서 참고: 관련 개념: 정렬된 배열의 이진 탐색(3.3절)

직접 풀어볼 문제

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로 각 숫자를 확인하는 방법과, 정렬된 배열에서 범위를 절반씩 줄이는 방법의 비용을 비교해 보세요.

직접 풀어보기

먼저 선택한 언어의 입력·반환 계약에 맞춰 작성해 보세요. 힌트 받기와 정답과 비교하기는 코드를 실행하지 않는 AI 분석입니다. 해답·해설은 아래에서 필요할 때 펼칠 수 있습니다.

0 / 6,000바이트

입력 예시 전체를 문자열로 전달하고 출력 문자열을 반환합니다. solve(input)를 호출합니다. 표준 입력·출력 코드는 추가하지 않습니다. 입력 공백과 LF/CRLF 줄바꿈을 처리합니다. Tab은 다음 항목으로 이동합니다. 입력 영역 오른쪽 아래를 끌어 높이를 조절할 수 있습니다.

비밀키·개인정보는 입력하지 마세요. 코드는 이 페이지에만 유지됩니다.

해답·해설 보기
JavaScript 해답
function solve(input) {
    const t = input.trim().split(/\s+/).map(Number);
    const n = t[0], a = t.slice(1, n + 1);
    a.sort((x, y) => x - y);
    const m = t[n + 1], out = [];
    for (const x of t.slice(n + 2, n + 2 + m)) {
        let l = 0, r = n - 1, found = false;
        while (l <= r) {
            const mid = l + Math.floor((r - l) / 2);
            if (a[mid] === x) {
                found = true;
                break;
            }
            if (a[mid] < x) l = mid + 1;
            else r = mid - 1;
        }
        out.push(found ? 1 : 0);
    }
    return out.join(' ');
}

입력 예시 전체를 문자열로 전달하고 출력 문자열을 반환합니다. solve(input)를 호출합니다. 표준 입력·출력 코드는 추가하지 않습니다. 입력 공백과 LF/CRLF 줄바꿈을 처리합니다.

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

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

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

원자료와 확인 범위

2026-10-12에 제공 학습 자료의 이분검색 문제와 비교했습니다. 그 문제는 한 검색 값의 정렬 후 위치를 구하고, 이 글은 여러 숫자 카드의 존재 여부를 출력하므로 동일 원자료로 연결하지 않았습니다. 이 글의 입력 조건은 본문에 적힌 숫자 카드 학습 문제를 기준으로 하며 최초 출제 원문·기출 여부는 미확인입니다.

이번에는 전체 JavaScript·TypeScript 코드 실행과 채점 제출을 하지 않았습니다. 본문의 기대 출력과 직접 실행하는 확인 방법을 구분하세요.

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

사이트의 5언어 함수 실습은 선택한 언어의 입력·반환 계약을 따릅니다. 아래 TypeScript 파일·설치·터미널 명령은 자신의 컴퓨터에서 별도로 실행해 보고 싶은 분을 위한 선택 안내입니다. 실습창에 이 파일 전체나 설치 명령을 넣지 않습니다.

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")));

JavaScript 배열·함수·조건문과 TypeScript 타입 표기를 알고 시작합니다. Node.js와 npm을 확인한 뒤 tsconfig.json이 없는 별도 연습 폴더에서 npm init -y, npm install --save-dev typescript @types/node로 컴파일러와 Node 타입을 준비할 수 있습니다. 이미 준비된 실습 환경이라면 그 의존성을 사용하고 npx tsc --version으로 버전을 기록합니다. solution.ts와 main.ts는 같은 폴더에 저장하고, input.txt에는 위의 N·카드·M·질의 예제를 그대로 넣어 아래 명령으로 컴파일합니다. 컴파일과 실행은 이번 편집에서 수행하지 않았습니다.

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

아래 실행 줄의 < 입력 리디렉션은 Bash·명령 프롬프트용입니다. PowerShell 7에서는 컴파일을 마친 뒤 Get-Content -Raw -Encoding UTF8 ./input.txt | node ./dist/main.js로 전달합니다. 기대 출력 1 0 1 1 0 1과 비교하세요.

정수 정렬에는 숫자 비교 함수가 필요합니다. 또한 입력을 공백 전체로 나누는 정규식은 /\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

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

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

이 문제를 푼 뒤 확인하기

첫·마지막 카드와 없는 카드, 중복 질의를 검색하며 left·right 갱신을 추적해 보세요.

본문의 예제와 이 반례를 직접 실행해 예상 출력과 비교하고, 결과가 다르면 입력 조건과 반복 범위를 다시 확인하세요.

글 소개·학습 준비·전체 목차
먼저 확인할 내용

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

이 글이 도움이 되었나요?

조회 중

이어서 공부하기

체크포인트: 해설을 가리고 다시 풀고, 경계 입력을 하나 바꾸어 예상 결과와 실제 결과를 비교하세요. 포인터를 움직여도 답을 버리지 않는 이유와 양수·0·음수의 차이를 설명하세요.

다음 추천 · 알고리즘 개념: DP 메모이제이션: 피보나치의 중복 호출 줄이기

다음 주제는 동적 계획법의 두 계산 순서입니다. 중복 부분 문제의 결과를 저장하고 메모이제이션과 타뷸레이션의 계산 순서를 비교합니다.

권장 순서는 학습 가이드와 같습니다. 다른 순서는 접힌 참고 목록에서 확인할 수 있습니다. 읽음 표시는 숙달이나 채점 결과가 아닙니다.

원래 과정 목록 순서 (선택)

읽음 기록

읽음 — / 전체 58개 · 필수 58개

이 글을 쓴 해비

웹사이트를 제작·운영하고, 개인 서비스와 자동화·AI 도구를 만듭니다. 직접 만들며 배운 내용과 문제 해결 과정을 BlogFlow에 기록합니다.

새 글 받아보기

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

RSS 피드 구독하기

댓글 남기기