본문으로 바로가기
제품데스크톱 도구TASTECAD도면 확인TASTEFILES파일 관리TASTEFTP파일 전송TASTESSH원격 작업공간TASTEZIP압축과 해제개발 도구TASTECODERust 통합 개발 환경TASTESTUDIO통합 개발·테스트 환경TASTEETL데이터 통합(ETL)서버TASTEREMOTE원격 데스크톱 서버TASTEGEO공간정보 서버TASTEMAIL메일 작업공간시작하기다운로드문서구매모든 제품
마켓플레이스
소식
개발소식오늘의 개발 소식개발 문서프로그래밍 언어RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron알고리즘알고리즘 기초알고리즘 핵심알고리즘 심화실무 알고리즘
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스커뮤니티 안내
협업기술 전수홈페이지 구축데스크톱 앱 구축릴리스·결제 파이프라인판매 파트너문의
서비스원격 데스크톱설치 패키지 커스텀
TASTEDEV
제품

데스크톱 도구

TASTECAD복잡한 도면을 가볍게 열다TASTEFILES필요한 파일만, 가장 빠르게TASTEFTP파일이 오가는 가장 단정한 길TASTESSH서버와 나 사이, 더 단단한 연결TASTEZIP더 빠르게 묶고, 더 안전하게 풀다

개발 도구

TASTECODERust 앱을 편집 · 빌드 · 디버깅 · 테스트까지 한 창에서TASTESTUDIO편집부터 원격 테스트와 승인 기반 AI 수정까지TASTEETL값을 보며 데이터를 옮깁니다

서버

TASTEREMOTE우리 회사 서버로 운영하는 원격 데스크톱TASTEGEOGeoServer 호환 공간정보 서버를 Rust로 다시 세웁니다TASTEMAIL메일의 주도권을 다시 내 손에

시작하기

다운로드모든 제품 최신 판문서제품 사용법구매구독 · 영구 라이선스모든 제품모든 제품 한눈에
모든 제품
마켓플레이스소식
개발

소식

오늘의 개발 소식언어 · 프레임워크 · GitHub · AI 공식 발표개발 문서언어 · 프레임워크 · 알고리즘 안내서

프로그래밍 언어

RustTypeScriptJavaScriptPythonGoC#JavaCC++KotlinDart

프레임워크

프런트엔드 프레임워크Next.jsReactVue.js · NuxtSvelteKitAngular
서버 프레임워크Spring BootDjangoFastAPIASP.NET CoreNestJS · Express
앱 · 데스크톱 프레임워크TauriFlutterReact NativeElectron

알고리즘

알고리즘 기초복잡도 분석 · 정렬 · 탐색 · 기본 자료구조 · 트리와 힙알고리즘 핵심그래프 탐색 · 최단 경로와 최소 신장 트리 · 동적 계획법 · 탐욕 알고리즘 · 분할 정복 · 백트래킹알고리즘 심화문자열 알고리즘 · 수학과 정수론 · 비트 연산 · 구간 트리 · 유니온 파인드실무 알고리즘코딩 면접 풀이 전략 · LRU 캐시 · 레이트 리미터 · 일관된 해시
개발 문서 전체 보기
커뮤니티
프로젝트빌더와 작업 발견개발 로그결정과 시행착오프롬프트재사용 가능한 지시AI 도구 이슈Claude · Codex · Gemini 문제와 우회법쇼케이스완성된 결과커뮤니티 안내공개하고 함께 만드는 방식
협업
기술 전수Rust·egui 네이티브 앱 만들기홈페이지 구축10개 언어, 계정, 문서·다운로드데스크톱 앱 구축Rust·egui, WebView 없이릴리스·결제 파이프라인서명 · MSI·deb·rpm·AppImage · 해시판매 파트너소개하고 결제액의 30% 받기문의구축 · 제휴 · 투자 문의
서비스
원격 데스크톱브라우저로 내 컴퓨터에 접속설치 패키지 커스텀필요한 기능만, 설정은 미리
구매
KO
한국어EnglishDeutschEspañolFrançaisItalianoPortuguês (Brasil)日本語简体中文繁體中文
로그인
로그인
TASTEDEV

작은 도구부터, 더 좋은 개발까지.

support@tastedev.netTASTEDEV 후원하기
제품제품군구매문서다운로드마켓플레이스고객 지원
커뮤니티프로젝트개발 로그프롬프트AI 도구 이슈쇼케이스
정책개인정보처리방침이용약관구독 약관환불 정책커뮤니티 운영 정책
계정로그인가입하기
방문자
오늘
012345678901234567890123456789012345678901234567890123456789
어제
012345678901234567890123456789012345678901234567890123456789
누적 조회
0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
상호
지엑스소프트
대표자
최영구
사업자등록번호
853-44-00368
주소
인천광역시 미추홀구 경인로 351 9층 V24
전화
+82-10-4404-1523
이메일
whitecalvin@tastedev.net
개인정보 보호책임자
최영구

© 2026 TASTEDEV제품 정보는 현재 검증된 개발 소스를 기준으로 합니다.출시·고도화 중

복잡도 분석 문서
복잡도 분석
복잡도 분석의 개념동작 원리: 연산 세기에서 마스터 정리까지구현: 세 가지 차수를 직접 재 보기복잡도: 대표 차수, 상환 분석, 공간연습 문제실무 활용
토론

복잡도 분석 안내서 · 3/6

구현: 세 가지 차수를 직접 재 보기

이 장에서는 O(n) 선형 탐색, O(log n) 이진 탐색, O(n²) 쌍 세기를 Python으로 구현하고 timeit으로 시간을 잰 뒤, 같은 문제를 O(n²)과 O(n)으로 푸는 두 합(two-sum) 함수를 C++, Java, TypeScript로 옮겨 봅니다.

Python 구현

import timeit

def linear_search(items, target):          # O(n)
    for i, x in enumerate(items):
        if x == target:
            return i
    return -1

def binary_search(items, target):          # O(log n), 정렬된 입력
    lo, hi = 0, len(items)
    while lo < hi:
        mid = (lo + hi) // 2
        if items[mid] < target:
            lo = mid + 1
        else:
            hi = mid
    if lo < len(items) and items[lo] == target:
        return lo
    return -1

def count_pairs_with_sum(items, target):   # O(n²)
    count = 0
    n = len(items)
    for i in range(n):
        for j in range(i + 1, n):
            if items[i] + items[j] == target:
                count += 1
    return count

한 줄씩 보면 다음과 같습니다.

  • linear_search는 앞에서부터 비교하므로 최악 Θ(n)입니다.
  • binary_search는 [lo, hi) 범위를 유지합니다. 가운데 값이 목표보다 작으면 왼쪽 절반을 버리고, 아니면 오른쪽 절반을 버립니다. 반복마다 범위가 절반이 되므로 약 log₂ n + 1번 돕니다. 끝나면 lo는 목표 이상인 첫 위치입니다(bisect.bisect_left와 같음).
  • count_pairs_with_sum은 i < j인 모든 쌍을 봅니다. 앞 장에서 센 것처럼 비교가 n(n-1)/2번이라 Θ(n²)입니다.

timeit으로 재기

timeit은 같은 코드를 여러 번 실행하고 그동안 가비지 컬렉션을 꺼서 잡음을 줄입니다. repeat로 여러 차례 재고 가장 작은 값을 씁니다. 큰 값은 다른 프로세스 등이 만든 잡음일 때가 많습니다.

def bench(func, *args, number=100, repeat=7):
    timer = timeit.Timer(lambda: func(*args))
    return min(timer.repeat(repeat=repeat, number=number)) / number

for n in (1_000, 2_000, 4_000):
    data = list(range(n))
    lin = bench(linear_search, data, -1)        # 없는 값: 최악의 경우
    bi = bench(binary_search, data, -1)
    pairs = bench(count_pairs_with_sum, data, -1, number=1, repeat=5)
    print(f"{n:>6} {lin * 1e6:8.1f}us {bi * 1e6:8.2f}us {pairs * 1e3:8.1f}ms")

한 컴퓨터에서 실행한 예입니다(환경마다 다릅니다).

n선형 탐색이진 탐색쌍 세기
1,00035.6 µs0.78 µs26.7 ms
2,00075.7 µs0.99 µs115.2 ms
4,000164.2 µs0.91 µs528.1 ms

n을 두 배로 늘릴 때 선형 탐색은 약 2배, 쌍 세기는 약 4배 늘고, 이진 탐색은 거의 변하지 않습니다. 이처럼 n을 두 배씩 늘려 시간 비율을 보는 방법(doubling experiment)은 차수를 확인하는 가장 간단한 실험입니다. 비율 2는 n, 4는 n², 8은 n³을 뜻합니다. 1 µs도 안 되는 측정은 잡음이 커서 비율이 들쭉날쭉합니다.

같은 문제, 두 차수: 두 합

합이 target인 두 원소의 위치를 찾습니다. 모든 쌍을 보면 O(n²)이고, 이미 본 값을 해시 맵에 기억하면 한 번 훑는 O(n)(평균)으로 줄어듭니다.

def two_sum_quadratic(nums, target):
    for i in range(len(nums)):
        for j in range(i + 1, len(nums)):
            if nums[i] + nums[j] == target:
                return i, j
    return None

def two_sum_linear(nums, target):
    seen = {}                        # 값 -> 처음 나온 위치
    for j, x in enumerate(nums):
        i = seen.get(target - x)     # 짝을 본 적 있나? 평균 O(1)
        if i is not None:
            return i, j
        seen.setdefault(x, j)
    return None

nums = [8, 3, 11, 5, 2]
print(two_sum_quadratic(nums, 13), two_sum_linear(nums, 13))  # (0, 3) (0, 3)

답이 여러 개면 두 함수가 찾는 쌍은 다를 수 있습니다. 아래 세 언어도 같은 두 함수를 구현합니다.

C++

#include <iostream>
#include <optional>
#include <unordered_map>
#include <utility>
#include <vector>

using Pair = std::optional<std::pair<int, int>>;

// O(n^2): 모든 쌍을 확인
Pair twoSumQuadratic(const std::vector<int>& nums, int target) {
    for (std::size_t i = 0; i < nums.size(); ++i)
        for (std::size_t j = i + 1; j < nums.size(); ++j)
            if (nums[i] + nums[j] == target) return std::make_pair(int(i), int(j));
    return std::nullopt;
}

// O(n) 평균: 본 값을 해시 맵에 기억
Pair twoSumLinear(const std::vector<int>& nums, int target) {
    std::unordered_map<int, int> seen;  // 값 -> 처음 나온 위치
    for (std::size_t j = 0; j < nums.size(); ++j) {
        auto it = seen.find(target - nums[j]);
        if (it != seen.end()) return std::make_pair(it->second, int(j));
        seen.emplace(nums[j], int(j));
    }
    return std::nullopt;
}

int main() {
    std::vector<int> nums{8, 3, 11, 5, 2};
    if (auto r = twoSumLinear(nums, 13)) std::cout << r->first << ' ' << r->second << '\n';  // 0 3
}

Java

import java.util.HashMap;
import java.util.Map;

public class TwoSum {
    // O(n^2): 모든 쌍을 확인
    static int[] twoSumQuadratic(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++)
            for (int j = i + 1; j < nums.length; j++)
                if (nums[i] + nums[j] == target) return new int[] {i, j};
        return null;
    }

    // O(n) 평균: HashMap으로 한 번 훑기
    static int[] twoSumLinear(int[] nums, int target) {
        Map<Integer, Integer> seen = new HashMap<>();  // 값 -> 처음 나온 위치
        for (int j = 0; j < nums.length; j++) {
            Integer i = seen.get(target - nums[j]);
            if (i != null) return new int[] {i, j};
            seen.putIfAbsent(nums[j], j);
        }
        return null;
    }

    public static void main(String[] args) {
        int[] r = twoSumLinear(new int[] {8, 3, 11, 5, 2}, 13);
        System.out.println(r == null ? "none" : r[0] + " " + r[1]);  // 0 3
    }
}

TypeScript

// O(n^2): 모든 쌍을 확인
function twoSumQuadratic(nums: number[], target: number): [number, number] | null {
  for (let i = 0; i < nums.length; i++)
    for (let j = i + 1; j < nums.length; j++)
      if (nums[i] + nums[j] === target) return [i, j];
  return null;
}

// O(n) 평균: Map으로 한 번 훑기
function twoSumLinear(nums: number[], target: number): [number, number] | null {
  const seen = new Map<number, number>(); // 값 -> 처음 나온 위치
  for (let j = 0; j < nums.length; j++) {
    const i = seen.get(target - nums[j]);
    if (i !== undefined) return [i, j];
    if (!seen.has(nums[j])) seen.set(nums[j], j);
  }
  return null;
}

console.log(twoSumQuadratic([8, 3, 11, 5, 2], 13), twoSumLinear([8, 3, 11, 5, 2], 13)); // [ 0, 3 ] [ 0, 3 ]

정리

  • 선형 탐색 O(n), 이진 탐색 O(log n), 쌍 세기 O(n²)는 두 배 실험에서 약 2배, 거의 그대로, 약 4배로 드러납니다.
  • timeit은 여러 번 재서 최솟값을 씁니다.
  • 두 합은 해시 맵으로 O(n) 메모리를 더 써서 O(n²)을 O(n)으로 줄이는 전형적인 시간-공간 교환입니다.
이 장에 대해 질문이 있나요?
이전 장동작 원리: 연산 세기에서 마스터 정리까지다음 장복잡도: 대표 차수, 상환 분석, 공간

댓글 0개

인기 댓글순최신순

로그인 · 로그인하면 댓글을 남길 수 있습니다.