본문으로 바로가기
제품데스크톱 도구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

구현

이 장에서는 병합 정렬과 퀵 정렬을 Python으로 구현하고 한 줄씩 설명합니다. 이어서 같은 병합 정렬을 C++, Java, TypeScript로 옮겨 언어마다 무엇이 달라지는지 봅니다.

Python 병합 정렬

def merge_sort(items):
    if len(items) <= 1:
        return items[:]
    mid = len(items) // 2
    left = merge_sort(items[:mid])
    right = merge_sort(items[mid:])
    return merge(left, right)


def merge(left, right):
    merged = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            merged.append(left[i])
            i += 1
        else:
            merged.append(right[j])
            j += 1
    merged.extend(left[i:])
    merged.extend(right[j:])
    return merged


print(merge_sort([38, 27, 43, 3, 9, 82, 10]))  # [3, 9, 10, 27, 38, 43, 82]
  • len(items) <= 1: 원소가 0개나 1개면 이미 정렬된 상태입니다. 재귀의 바닥입니다. items[:]로 복사본을 돌려주어 입력을 바꾸지 않는다는 약속을 지킵니다.
  • mid = len(items) // 2: 가운데에서 나눕니다. 길이가 홀수면 오른쪽이 하나 더 깁니다.
  • merge_sort(items[:mid]), merge_sort(items[mid:]): 두 절반을 각각 재귀로 정렬합니다.
  • merge의 while: 두 목록의 맨 앞(left[i], right[j])을 비교해 작은 쪽을 붙입니다.
  • left[i] <= right[j]: <=가 핵심입니다. 같을 때 왼쪽을 먼저 꺼내므로 원래 순서가 유지되어 안정 정렬이 됩니다. <로 바꾸면 안정성이 깨집니다.
  • extend: 한쪽이 먼저 바닥나면 남은 쪽을 그대로 붙입니다. 남은 쪽은 이미 정렬되어 있습니다.

이 구현은 읽기 쉽지만 슬라이스마다 새 리스트를 만들기 때문에 메모리 할당이 많습니다. 성능이 중요하면 버퍼 하나를 미리 만들어 인덱스로 구간을 다루는 방식(아래 C++ · Java 판)을 씁니다.

Python 퀵 정렬

import random


def quicksort(a, lo=0, hi=None):
    if hi is None:
        hi = len(a) - 1
    while lo < hi:
        lt, gt = partition3(a, lo, hi)
        if lt - lo < hi - gt:          # 작은 쪽만 재귀로
            quicksort(a, lo, lt - 1)
            lo = gt + 1
        else:
            quicksort(a, gt + 1, hi)
            hi = lt - 1


def partition3(a, lo, hi):
    pivot = a[random.randint(lo, hi)]
    lt, i, gt = lo, lo, hi
    while i <= gt:
        if a[i] < pivot:
            a[lt], a[i] = a[i], a[lt]
            lt += 1
            i += 1
        elif a[i] > pivot:
            a[i], a[gt] = a[gt], a[i]
            gt -= 1
        else:
            i += 1
    return lt, gt


data = [5, 2, 8, 2, 9, 1, 5, 5]
quicksort(data)
print(data)  # [1, 2, 2, 5, 5, 5, 8, 9]
  • random.randint(lo, hi): 기준값을 무작위로 골라, 이미 정렬된 입력 같은 특정 패턴에서 O(n²)이 되는 일을 확률적으로 피합니다.
  • partition3: 배열을 "기준값보다 작음 / 같음 / 큼" 세 구역으로 나누는 3분할(네덜란드 국기 문제)입니다. 끝나면 a[lo:lt]는 작고, a[lt:gt+1]은 기준값과 같고, a[gt+1:hi+1]은 큽니다. 같은 값이 많은 입력에서도 같은 값 구역을 다시 정렬하지 않으므로 느려지지 않습니다.
  • elif a[i] > pivot에서 i를 늘리지 않는 이유: 뒤에서 가져온 a[gt]는 아직 검사하지 않은 값이기 때문입니다.
  • while lo < hi와 "작은 쪽만 재귀": 작은 구간은 재귀로, 큰 구간은 반복문으로 처리하면 재귀 깊이가 최악에도 O(log n)으로 묶입니다. Python의 재귀 한도(기본 1000)에 걸리지 않게 하는 실용적인 장치입니다.

C++ 병합 정렬

#include <cstddef>
#include <iostream>
#include <vector>

template <typename T>
void merge_sort(std::vector<T>& a, std::vector<T>& buf, std::size_t lo, std::size_t hi) {
    if (hi - lo <= 1) return;                 // [lo, hi) 반열린 구간
    std::size_t mid = lo + (hi - lo) / 2;
    merge_sort(a, buf, lo, mid);
    merge_sort(a, buf, mid, hi);
    std::size_t i = lo, j = mid, k = lo;
    while (i < mid && j < hi) {
        if (a[j] < a[i]) buf[k++] = a[j++];   // 오른쪽이 엄격히 작을 때만 → 안정
        else buf[k++] = a[i++];
    }
    while (i < mid) buf[k++] = a[i++];
    while (j < hi) buf[k++] = a[j++];
    for (std::size_t t = lo; t < hi; ++t) a[t] = buf[t];
}

template <typename T>
void merge_sort(std::vector<T>& a) {
    std::vector<T> buf(a.size());
    merge_sort(a, buf, 0, a.size());
}

int main() {
    std::vector<int> v{38, 27, 43, 3, 9, 82, 10};
    merge_sort(v);
    for (int x : v) std::cout << x << ' ';
    std::cout << '\n';
}

C++ 판은 버퍼를 한 번만 만들고 [lo, hi) 구간을 인덱스로 다룹니다. 비교에는 operator<만 쓰므로 <가 정의된 어떤 타입에도 동작합니다. 실무에서는 std::stable_sort가 같은 역할을 합니다.

Java 병합 정렬

import java.util.Arrays;

public class MergeSort {
    public static void sort(int[] a) {
        int[] buf = new int[a.length];
        sort(a, buf, 0, a.length);
    }

    private static void sort(int[] a, int[] buf, int lo, int hi) {
        if (hi - lo <= 1) return;
        int mid = lo + (hi - lo) / 2;          // (lo + hi) / 2 의 오버플로를 피한다
        sort(a, buf, lo, mid);
        sort(a, buf, mid, hi);
        int i = lo, j = mid, k = lo;
        while (i < mid && j < hi) {
            buf[k++] = (a[j] < a[i]) ? a[j++] : a[i++];
        }
        while (i < mid) buf[k++] = a[i++];
        while (j < hi) buf[k++] = a[j++];
        System.arraycopy(buf, lo, a, lo, hi - lo);
    }

    public static void main(String[] args) {
        int[] v = {38, 27, 43, 3, 9, 82, 10};
        sort(v);
        System.out.println(Arrays.toString(v));
    }
}

System.arraycopy로 버퍼의 구간을 한 번에 되돌려 씁니다. 객체 배열이라면 Comparator<? super T>를 받아 c.compare(a[j], a[i]) < 0으로 비교하면 됩니다.

TypeScript 병합 정렬

function mergeSort<T>(items: readonly T[], compare: (a: T, b: T) => number): T[] {
  if (items.length <= 1) return items.slice();
  const mid = Math.floor(items.length / 2);
  const left = mergeSort(items.slice(0, mid), compare);
  const right = mergeSort(items.slice(mid), compare);
  const merged: T[] = [];
  let i = 0;
  let j = 0;
  while (i < left.length && j < right.length) {
    if (compare(right[j], left[i]) < 0) merged.push(right[j++]);
    else merged.push(left[i++]);
  }
  while (i < left.length) merged.push(left[i++]);
  while (j < right.length) merged.push(right[j++]);
  return merged;
}

console.log(mergeSort([38, 27, 43, 3, 9, 82, 10], (a, b) => a - b));

TypeScript 판은 비교 함수를 인자로 받아 Array.prototype.sort와 같은 약속(음수면 a가 앞)을 따릅니다. 제네릭 T 덕분에 숫자, 문자열, 객체 모두에 쓸 수 있습니다.

정리

  • 병합 정렬은 같은 값일 때 왼쪽을 먼저 꺼내는 한 줄로 안정성을 지킵니다.
  • 퀵 정렬은 무작위 기준값, 3분할, 작은 쪽 재귀의 세 가지로 실전에서 안전해집니다.
  • 언어가 달라도 핵심은 같으며, 버퍼 재사용과 반열린 구간이 실용적인 구현의 공통점입니다.
이 장에 대해 질문이 있나요?
이전 장동작 원리다음 장복잡도

댓글 0개

인기 댓글순최신순

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