Computational Thinking으로 “어떻게 생각할 것인가”를 배웠고, 자료구조로 “어떻게 담을 것인가”를 배웠다면, 오늘은 “어떻게 효율적으로 처리할 것인가”를 배우는 날이었다. 알고리즘은 단순한 ‘문제 푸는 방법’이 아니라 **“최적의 절차를 설계하는 능력”**이라는 걸 깨달았다.

오늘 다룬 범위

  • 알고리즘의 정의와 중요성
  • 정렬 알고리즘: 선택 정렬, 삽입 정렬, 버블 정렬, 퀵 정렬, 병합 정렬
  • 시간 복잡도(Time Complexity): Big O 표기법
  • 공간 복잡도(Space Complexity)
  • Divide and Conquer 기법

핵심 개념 정리

알고리즘이란?

강사님은 이렇게 정의했다:

“문제를 풀기 위한 단계별 절차. 정확성도 중요하지만, 효율성이 더 중요하다.”

100개 숫자를 정렬하는 데 버블 정렬을 쓰나 퀵 정렬을 쓰나 결과는 같다. 하지만 1백만 개를 정렬한다면? 버블 정렬은 몇 시간이 걸리지만 퀵 정렬은 몇 초에 끝난다.

“정확한 답을 구하는 것”과 “정확하면서도 빠르게 구하는 것”은 완전히 다르다.

정렬 알고리즘 비교

1. 선택 정렬(Selection Sort)

  • 가장 작은 값을 찾아서 앞으로 옮기는 방식
  • 시간복잡도: O(n²)
  • 가장 이해하기 쉽지만 가장 느리다

2. 삽입 정렬(Insertion Sort)

  • 카드를 정렬하듯이 한 개씩 끼워 넣는 방식
  • 시간복잡도: O(n²)
  • 이미 정렬된 데이터에는 O(n)까지 빨라진다

3. 버블 정렬(Bubble Sort)

  • 옆의 값과 비교해서 크면 자리를 바꾸기를 반복
  • 시간복잡도: O(n²)
  • 실제로는 잘 안 쓰인다

4. 퀵 정렬(Quick Sort)

  • Pivot(기준값)을 정하고 작은 값들과 큰 값들로 분할
  • 시간복잡도: O(n log n) (평균), O(n²) (최악)
  • 가장 많이 쓰이는 정렬

5. 병합 정렬(Merge Sort)

  • 반으로 계속 나눈 후 다시 병합하는 방식
  • 시간복잡도: O(n log n) (항상)
  • 안정적이지만 O(n) 추가 공간 필요

시간 복잡도 - Big O 표기법

이건 정말 중요한 개념이다. 코드를 짤 때마다 “이 코드는 몇 번 반복될까?”를 생각해야 한다.

O(1)       → 1번
O(log n)   → 로그 번
O(n)       → n번
O(n log n) → n * 로그 n번
O(n²)      → n * n번
O(2ⁿ)      → 지수적 (피해야 함)
O(n!)      → 팩토리얼 (절대 피해야 함)

n = 1,000,000일 때:

  • O(n): 1백만 번
  • O(n log n): 약 2천만 번
  • O(n²): 1조 번 (컴퓨터가 수십 초 걸림)

작은 수에서는 차이가 안 보이지만, 데이터가 많아질수록 극적으로 차이가 난다.

헷갈렸던 점 / 실수 포인트

”왜 정렬 알고리즘을 이렇게 많이 배우나?”

처음엔 이 생각을 했다. “정렬은 라이브러리에서 제공하는데 왜 일일이 구현을 배우지?”

그런데 강사님이 하신 말이 깨달음을 줬다:

“정렬 알고리즘을 배우는 건 정렬이 목표가 아니라, ‘같은 문제를 푸는데 여러 방법이 있고, 각 방법의 장단점을 이해하는 연습’이기 때문이다.”

정렬뿐만 아니라 모든 알고리즘이 그렇다. 같은 문제를 푸는 여러 방법 중에 상황에 맞는 최선의 방법을 고를 수 있어야 한다.

”Divide and Conquer가 뭔 대단한 거야?”

퀵 정렬을 배우면서 처음 본 개념이 **“분할 정복(Divide and Conquer)“**이었다.

  • 분할: 큰 문제를 작은 부분 문제로 나눈다
  • 정복: 작은 문제를 푼다
  • 결합: 작은 답들을 합쳐서 큰 답을 만든다

처음엔 단순해 보였지만, 강사님의 설명 후 깨달았다. 이건 “불가능해 보이는 큰 문제를 어떻게 풀 것인가”의 일반적인 전략이라는 뜻이다.

병합 정렬도, 퀵 정렬도, 이진 탐색도, 심지어 이진 트리 탐색도 모두 이 원리를 따른다. 패턴이 있다.

복습 Q&A

Q. 퀵 정렬의 시간복잡도가 O(n²)일 수도 있다는데, 그럼 버블 정렬이랑 같은 건가?

A. 아니다. 큰 차이가 있다.

퀵 정렬은 pivot을 잘 선택하면 대부분 O(n log n)이고, pivot을 잘못 선택했을 때만 드물게 O(n²)이 된다.

버블 정렬은 항상 O(n²)이다.

즉, 퀵 정렬의 O(n²)는 “최악의 경우”이고, 버블 정렬의 O(n²)는 “항상”이라는 뜻이다. 이게 실제로는 엄청난 차이다.

Q. 시간복잡도를 어떻게 계산하나?

A. 가장 중요한 부분만 본다.

# O(n²)
for i in range(n):
    for j in range(n):
        print(i, j)  # n * n번 실행

# O(n log n)
# 퀵 정렬: 반으로 나누기(log n) × 각 단계마다 모든 원소 처리(n)
  • 상수는 무시한다: O(3n) = O(n)
  • 낮은 차수 항은 무시한다: O(n² + n) = O(n²)
  • 가장 높은 차수만 본다
Q. 그럼 병합 정렬이 항상 O(n log n)이면 퀵 정렬보다 낫지 않나?

A. 이론상으로는 그렇지만, 실제로는 퀵 정렬이 더 자주 쓰인다.

이유:

  1. 상수 인수: 퀵 정렬의 상수가 병합 정렬보다 작다 (실제로는 더 빠름)
  2. 캐시 효율성: 퀵 정렬이 메모리를 더 효율적으로 사용한다
  3. 추가 공간: 병합 정렬은 O(n)의 추가 메모리가 필요하다

Big O 표기법은 “대략적인 흐름”을 보는 것이지, 절대적인 속도를 비교하는 게 아니다.

한 줄 정리

좋은 알고리즘은 결과가 같아도 훨씬 적은 단계로 도달한다. 데이터가 많을수록 이 차이는 초와 분의 차이, 나아가 가능과 불가능의 차이가 된다.

내일은 개발 도구를 배운다. 지금까지는 순수 논리(CT, 자료구조, 알고리즘)를 배웠다면, 내일부터는 이걸 코드로 구현하는 실전 도구들을 배우게 될 것 같다.

Community

Comments

0 comments

Comments appear immediately. Use report if something needs review.

No comments yet.