본문 바로가기

Python bisect 모듈로 정렬된 리스트에 빠르고 효율적인 삽입하기

@threeseasct2025. 11. 24. 21:49




bisect 모듈 기본 이해

파이썬에서 정렬된 리스트를 다룰 때, 새로운 요소를 삽입해야 하는 경우가 빈번합니다. 이때 리스트의 정렬 상태를 유지하면서 효율적으로 삽입하는 것은 성능에 큰 영향을 미칩니다. 바로 이럴 때 `bisect` 모듈이 빛을 발합니다. `bisect` 모듈은 정렬된 시퀀스에 항목을 삽입할 위치를 찾거나, 항목을 유지하면서 삽입하는 기능을 제공합니다. 이 모듈은 이진 탐색(binary search) 알고리즘을 기반으로 동작하기 때문에, 리스트의 크기가 커져도 삽입 위치를 찾는 데 걸리는 시간이 로그 시간 복잡도를 가지므로 매우 빠릅니다. 일반적인 리스트에서 `append` 후 정렬하는 방식은 리스트가 클수록 비효율적일 수 있지만, `bisect`를 사용하면 삽입과 동시에 정렬 상태를 유지할 수 있습니다. `bisect_left`와 `bisect_right` (또는 `bisect`) 함수는 삽입할 위치를 반환하며, `insort_left`와 `insort_right` (또는 `insort`) 함수는 해당 위치에 요소를 실제로 삽입해줍니다. 이러한 함수들은 정렬된 리스트를 유지해야 하는 다양한 상황에서 유용하게 사용될 수 있습니다.

`bisect` 모듈의 핵심은 바로 이진 탐색을 활용하여 삽입 위치를 효율적으로 찾는다는 점입니다.

 

함수 설명
`bisect_left(a, x)` 리스트 `a`에서 `x`를 삽입할 가장 왼쪽 인덱스를 반환합니다. `x`가 이미 리스트에 있다면, 기존 `x`의 앞에 삽입될 위치를 반환합니다.
`bisect_right(a, x)` 또는 `bisect(a, x)` 리스트 `a`에서 `x`를 삽입할 가장 오른쪽 인덱스를 반환합니다. `x`가 이미 리스트에 있다면, 기존 `x`의 뒤에 삽입될 위치를 반환합니다.
`insort_left(a, x)` `bisect_left`로 찾은 위치에 `x`를 삽입하여 정렬을 유지합니다.
`insort_right(a, x)` 또는 `insort(a, x)` `bisect_right`로 찾은 위치에 `x`를 삽입하여 정렬을 유지합니다.




bisect_left와 bisect_right의 차이점

`bisect_left`와 `bisect_right` (혹은 `bisect`) 함수는 모두 정렬된 리스트에서 특정 값을 삽입할 적절한 위치를 찾는 역할을 합니다. 둘의 가장 큰 차이점은 삽입될 위치가 기존에 같은 값을 가진 요소들과 겹칠 때 발생합니다. `bisect_left`는 중복되는 값이 있을 경우, 해당 값들 중 가장 왼쪽의 인덱스를 반환합니다. 이는 새 값이 기존 값들의 앞에 삽입되도록 합니다. 반면 `bisect_right`는 중복되는 값이 있을 경우, 해당 값들 중 가장 오른쪽의 인덱스를 반환하여 새 값이 기존 값들 뒤에 삽입되도록 합니다. 이 작은 차이는 리스트에 동일한 값을 여러 개 저장해야 하는 경우, 또는 데이터의 순서가 중요한 경우에 결과에 영향을 미칠 수 있습니다. 예를 들어, 동일한 시간대의 로그를 기록할 때 `bisect_right`를 사용하면 나중에 기록된 로그가 먼저 기록된 로그 뒤에 위치하게 되어 시간 순서대로 정렬하는 데 유리할 수 있습니다. 어떤 함수를 사용할지는 원하는 삽입 로직에 따라 결정하면 됩니다. 삽입 위치를 결정하는 데 있어서 이 두 함수의 동작 방식은 매우 중요합니다.

간단히 말해, `bisect_left`는 "앞에 꽂아줘", `bisect_right`는 "뒤에 꽂아줘"라고 생각하면 이해하기 쉽습니다.

 

중요 포인트: `bisect_left`는 기존 값보다 같거나 큰 첫 번째 위치를, `bisect_right`는 기존 값보다 큰 첫 번째 위치를 반환합니다.

▶ 예시: `my_list = [1, 3, 5, 5, 7]`

bisect_left(my_list, 5) 결과: 2 (첫 번째 5 앞에 삽입)

bisect_right(my_list, 5) 결과: 4 (두 번째 5 뒤에 삽입)




insort 함수를 이용한 효율적인 삽입

`bisect` 모듈의 가장 실용적인 부분은 바로 `insort` 계열의 함수들입니다. `insort_left`와 `insort_right` (또는 `insort`) 함수는 단순히 삽입될 위치를 찾는 것을 넘어, 해당 위치에 새 요소를 **직접 삽입**하여 리스트의 정렬 상태를 유지해 줍니다. 이는 `bisect` 함수로 위치를 찾고, `list.insert()` 메서드를 별도로 호출하는 것보다 더 간결하고 효율적인 코드를 작성하게 해줍니다. 예를 들어, 실시간으로 발생하는 데이터를 정렬된 상태로 계속 유지해야 하는 경우, `insort` 함수를 사용하면 매번 전체 리스트를 다시 정렬하는 부담 없이 빠르고 정확하게 데이터를 추가할 수 있습니다. 이는 데이터 처리량과 응답 속도 측면에서 큰 이점을 제공합니다. 특히 대규모 데이터를 다루거나, 빈번한 삽입 작업이 필요한 애플리케이션에서 `bisect` 모듈과 `insort` 함수를 활용하는 것은 성능 최적화의 핵심 요소가 될 수 있습니다. `insort` 함수는 파이썬 리스트를 정렬 상태로 유지하면서 요소를 삽입하는 데 최적화되어 있습니다.

`insort` 함수를 사용하면 리스트를 계속 정렬된 상태로 유지하는 것이 매우 간편해집니다.

 

핵심 팁: `insort` 함수를 사용하면 삽입 위치를 찾는 과정과 실제 삽입 과정이 한 번에 이루어지므로 코드가 더 간결해지고 가독성이 향상됩니다.

▶ 단계별 사용 예시:

▶ 1단계: 초기 정렬된 리스트 생성: `numbers = [2, 5, 7, 8, 11, 12]`

▶ 2단계: `insort_left`를 사용하여 6 삽입: `bisect.insort_left(numbers, 6)` -> `numbers`는 `[2, 5, 6, 7, 8, 11, 12]`가 됩니다.

▶ 3단계: `insort_right`를 사용하여 8 삽입: `bisect.insort_right(numbers, 8)` -> `numbers`는 `[2, 5, 6, 7, 8, 8, 11, 12]`가 됩니다.




bisect_left 와 bisect_right 이해하기

Python의 bisect 모듈은 정렬된 리스트에서 특정 값을 효율적으로 찾거나 삽입할 위치를 결정하는 데 매우 유용합니다. 이 모듈에는 두 가지 주요 함수, bisect_leftbisect_right가 있습니다. 두 함수의 기본적인 역할은 같지만, 삽입 위치를 결정하는 방식에 미묘한 차이가 있어 주의 깊게 이해하는 것이 중요합니다. bisect_left는 삽입하려는 값이 이미 리스트에 존재할 경우, 해당 값의 왼쪽에 삽입될 위치를 반환합니다. 반면 bisect_right (또는 bisect)는 같은 경우 오른쪽에 삽입될 위치를 반환합니다. 이 차이는 중복된 값이 많은 리스트에서 특히 두드러지며, 어떤 함수를 사용하느냐에 따라 결과가 달라질 수 있습니다.

예를 들어, 리스트 [1, 2, 2, 3, 4]에 숫자 2를 삽입한다고 가정해 봅시다. bisect_left(list, 2)를 사용하면 인덱스 1을 반환합니다. 이는 기존 2의 왼쪽에 새로운 2가 삽입되어 [1, 2, 2, 2, 3, 4]가 되는 것을 의미합니다. 반면에 bisect_right(list, 2)를 사용하면 인덱스 3을 반환합니다. 이 경우 새로운 2는 기존 2들 뒤에 삽입되어 [1, 2, 2, 2, 3, 4]와 같이 결과적으로 같은 리스트가 되지만, 삽입 위치 자체가 다르다는 것을 알 수 있습니다. 이러한 미세한 차이를 제대로 이해하면, 데이터의 순서나 중복 처리에 있어 보다 정교한 로직을 구현할 수 있습니다.

 

함수 동작 설명 (중복 값 존재 시) 반환되는 인덱스
bisect_left 삽입할 값이 이미 리스트에 존재하면, 해당 값들의 가장 왼쪽 인덱스를 반환합니다. 기존 값의 왼쪽
bisect_right (bisect) 삽입할 값이 이미 리스트에 존재하면, 해당 값들의 가장 오른쪽 인덱스를 반환합니다. 기존 값의 오른쪽




bisect_insort 함수로 삽입하기

bisect_leftbisect_right 함수가 삽입할 위치를 찾는 데 초점을 맞춘다면, bisect_insort 계열 함수들은 그 위치를 찾아 바로 삽입까지 수행해 줍니다. 이 함수 역시 bisect_insort_leftbisect_insort_right (bisect_insort) 두 가지로 나뉩니다. bisect_insort_leftbisect_left와 동일하게 삽입할 값이 존재할 경우 왼쪽을 기준으로 삽입하며, bisect_insort_rightbisect_right와 마찬가지로 오른쪽을 기준으로 삽입합니다. 이 함수들은 정렬된 상태를 유지하면서 새로운 요소를 효율적으로 추가해야 할 때 매우 유용합니다. 별도로 bisect_left 등으로 위치를 찾고, 다시 list.insert() 메서드를 호출하는 번거로움을 줄여줍니다.

bisect_insort 함수들을 사용할 때 가장 큰 장점은 시간 복잡도입니다. 일반적인 리스트의 insert() 메서드는 리스트의 중간에 삽입할 때 요소를 뒤로 밀어내야 하므로 O(n)의 시간 복잡도를 가지지만, bisect_insort는 삽입 위치를 찾는 데 O(log n)이 걸리고, 실제 삽입 과정에서도 (내부적으로 리스트 구현 방식에 따라) 효율적으로 처리되므로 전체적으로 매우 효율적인 삽입이 가능합니다. 따라서 대규모 데이터셋에서 지속적으로 정렬된 상태를 유지해야 하는 경우, bisect_insort 함수를 사용하는 것이 성능 향상에 큰 도움이 됩니다.

 

핵심 포인트: bisect_insort_leftbisect_insort_right는 삽입 위치를 찾고 바로 리스트에 요소를 추가하는 함수입니다. 이 함수들을 사용하면 정렬된 리스트의 상태를 유지하면서 효율적으로 데이터를 관리할 수 있습니다.




실제 활용 사례와 성능 비교

Python의 bisect 모듈은 단순한 삽입 기능을 넘어 다양한 실제 응용 분야에서 빛을 발합니다. 예를 들어, 시계열 데이터에서 특정 시간 범위에 속하는 데이터 포인트를 빠르게 찾거나, 실시간으로 들어오는 데이터 스트림을 정렬된 상태로 유지하며 처리하는 경우에 bisect 모듈이 유용하게 사용될 수 있습니다. 또한, 대규모 로그 파일을 분석하거나, 사용자 데이터를 관리하는 시스템에서 새로운 데이터를 추가할 때, 정렬 순서를 유지하는 것이 중요한데, 이때 bisect 모듈은 append()sort()를 반복적으로 호출하는 것보다 훨씬 뛰어난 성능을 보입니다. 이진 탐색 알고리즘을 기반으로 하기 때문에, 리스트의 크기가 커질수록 그 효율성은 더욱 극대화됩니다.

성능 비교를 위해 간단한 예시를 살펴보겠습니다. 100만 개의 정수를 가진 정렬된 리스트에 새로운 숫자를 삽입하는 경우를 가정해 봅시다. 일반적인 `list.insert()` 메서드를 사용할 경우, 평균적으로 리스트의 절반에 해당하는 요소들을 이동시켜야 하므로 O(n)의 시간 복잡도를 갖습니다. 즉, 약 50만 번의 이동 연산이 필요할 수 있습니다. 반면, `bisect.insort()` 함수는 삽입 위치를 찾는 데 O(log n)의 시간 복잡도를 가지며, 실제 삽입 과정 역시 효율적으로 이루어져 훨씬 빠른 속도를 보여줍니다. 100만 개라는 규모에서는 이 차이가 매우 뚜렷하게 나타나, bisect 모듈을 사용한 코드가 훨씬 빠르게 실행될 것입니다. 이러한 성능상의 이점 때문에 bisect 모듈은 대규모 데이터를 다루는 Python 프로그래밍에서 빼놓을 수 없는 중요한 도구로 자리 잡고 있습니다.

 

▶ 1단계: bisect 모듈 임포트하기

▶ 2단계: 정렬된 리스트 준비하기

▶ 3단계: bisect.insort_left() 또는 bisect.insort_right()를 사용하여 새로운 요소를 삽입하기

Python bisect 모듈로 정렬된 리스트에 빠르고 효율적인 삽입하기




bisect_left와 bisect_right 이해하기

Python의 `bisect` 모듈은 정렬된 리스트에 요소를 효율적으로 삽입하는 데 사용됩니다. 이 모듈은 크게 두 가지 핵심 함수, `bisect_left`와 `bisect_right`를 제공합니다. 두 함수의 역할은 비슷하지만, 삽입 지점을 결정하는 방식에서 미묘한 차이가 있습니다.

`bisect_left(a, x)` 함수는 정렬된 리스트 `a`에서 값 `x`를 삽입할 위치를 찾습니다. 이때, 만약 `x`와 같은 값을 가진 요소가 이미 리스트에 존재한다면, `bisect_left`는 가장 왼쪽에 있는 해당 요소의 인덱스를 반환합니다. 즉, `x`를 삽입했을 때 기존 `x` 값들의 왼쪽에 삽입됩니다.

반면에 `bisect_right(a, x)` 함수는 `bisect_left`와 유사하게 `x`의 삽입 위치를 찾지만, `x`와 같은 값이 이미 존재할 경우 가장 오른쪽에 있는 해당 요소의 인덱스 바로 다음 위치를 반환합니다. 따라서 `x`를 삽입했을 때 기존 `x` 값들의 오른쪽에 삽입됩니다. 이 차이는 리스트에 중복된 값이 많을 때 삽입 결과에 영향을 줄 수 있습니다.

어떤 함수를 사용할지는 데이터의 특성과 원하는 동작 방식에 따라 결정해야 합니다. 대부분의 경우, 두 함수 모두 정렬된 상태를 유지하는 데 문제가 없지만, 특정 상황에서는 `bisect_left`가, 다른 상황에서는 `bisect_right`가 더 적합할 수 있습니다.

 

함수 설명 예시 (a=[1, 2, 4, 4, 5], x=4)
bisect_left x를 삽입할 가장 왼쪽 인덱스를 찾습니다. 2 (4의 첫 번째 등장 위치)
bisect_right x를 삽입할 가장 오른쪽 인덱스 바로 다음 위치를 찾습니다. 4 (4의 마지막 등장 위치 바로 다음)




bisect.insort 함수 활용법

`bisect` 모듈은 단순히 삽입 위치를 찾는 것 외에도, 해당 위치에 요소를 직접 삽입해주는 편리한 함수인 `insort`를 제공합니다. `insort` 함수는 `bisect_left` 또는 `bisect_right`와 동일한 로직을 사용하여 삽입 위치를 찾은 후, 리스트에 해당 요소를 삽입합니다. `insort` 함수를 사용하면 삽입 위치를 직접 계산하고 별도로 `insert` 메소드를 호출하는 번거로움을 줄일 수 있어 코드가 간결해집니다.

`insort(a, x)`는 `a.insert(bisect_left(a, x), x)`와 동일하게 작동합니다. 마찬가지로 `insort_left(a, x)`는 `bisect_left`를 사용하고, `insort_right(a, x)`는 `bisect_right`를 사용하여 요소를 삽입합니다. `insort`는 기본적으로 `insort_left`와 동일한 동작을 수행합니다.

이 함수들을 사용하면 정렬된 상태를 유지하면서 리스트에 새로운 요소를 추가하는 과정을 매우 효율적으로 처리할 수 있습니다. 이는 대규모 데이터를 다루거나, 삽입 연산이 빈번하게 발생하는 애플리케이션에서 성능 향상에 크게 기여합니다. 이진 탐색 기반으로 작동하기 때문에 삽입 속도가 O(log n)에 가깝고, 실제 삽입은 O(n)이 걸리지만, 위치 탐색 과정에서의 효율성이 높습니다.

 

▶ 1단계: `bisect` 모듈을 import 합니다. (`import bisect`)

▶ 2단계: 삽입할 정렬된 리스트와 값을 준비합니다.

▶ 3단계: `bisect.insort(list_name, value)` 또는 `bisect.insort_left(list_name, value)`를 호출하여 리스트에 값을 삽입합니다. 중복 값 처리 방식에 따라 `insort_right`를 선택할 수도 있습니다.




Python bisect 모듈의 성능 및 활용 사례

Python의 `bisect` 모듈은 내부적으로 이진 탐색 알고리즘을 사용하여 정렬된 리스트 내에서 특정 값을 삽입할 위치를 O(log n)의 시간 복잡도로 빠르게 찾습니다. 리스트에 요소를 삽입하는 실제 `insert` 연산은 O(n)의 시간 복잡도를 가지므로, `bisect` 모듈을 사용한다고 해서 전체 삽입 연산이 O(log n)이 되는 것은 아닙니다. 하지만, 수백만 개 이상의 요소가 있는 리스트에서 삽입 위치를 찾는 데 걸리는 시간을 크게 단축시킬 수 있다는 점에서 매우 효율적입니다.

이러한 성능상의 이점 덕분에 `bisect` 모듈은 다양한 분야에서 활용될 수 있습니다. 예를 들어, 실시간으로 데이터를 받아와 정렬된 상태를 유지해야 하는 시계열 데이터 처리, 게임 개발에서 플레이어의 점수 목록을 정렬하거나 랭킹을 업데이트하는 경우, 또는 데이터베이스에서 인덱스를 관리하는 시스템 등에서 유용하게 사용될 수 있습니다. 특히, 데이터가 이미 정렬되어 있거나 정렬된 상태로 유지되어야 하는 상황에서 `bisect`는 반복적인 정렬 과정의 필요성을 줄여주어 코드의 효율성을 높여줍니다.

`bisect` 모듈은 단순히 리스트의 정렬된 상태를 유지하는 것을 넘어, 정렬된 데이터를 기반으로 효율적인 탐색 및 삽입 작업을 가능하게 함으로써 파이썬 프로그래밍의 성능을 한 단계 끌어올릴 수 있는 강력한 도구입니다. 파이썬 bisect를 제대로 이해하고 활용하면 복잡한 데이터 구조를 다루는 데 있어 개발 시간과 실행 시간을 모두 절약할 수 있습니다.

 

핵심 포인트: `bisect` 모듈은 삽입 위치 탐색에 O(log n)의 시간 복잡도를 가지므로, 많은 양의 데이터에서 빈번한 삽입이 발생할 때 특히 유용합니다.

핵심 요약

• `bisect_left`는 중복 시 왼쪽에, `bisect_right`는 중복 시 오른쪽에 삽입될 위치를 찾습니다.
• `insort` 함수는 삽입 위치 탐색과 실제 삽입을 한 번에 처리하여 편리합니다.
• `bisect` 모듈은 O(log n)의 탐색 시간 복잡도로 효율적인 정렬된 리스트 관리를 가능하게 합니다.




주요 질문 FAQ




Q. Python bisect 모듈은 언제 사용하면 가장 효과적인가요?

Python bisect 모듈은 이미 정렬되어 있는 리스트에 새로운 요소를 삽입하거나, 특정 값을 삽입할 위치를 빠르게 찾고 싶을 때 사용하면 매우 효과적입니다. 리스트가 크면 클수록 선형 탐색(O(n))보다 이진 탐색(O(log n))의 속도 향상 효과를 크게 체감할 수 있습니다. 대규모 데이터셋을 다루거나 실시간으로 정렬 상태를 유지해야 하는 경우에 특히 유용합니다.




Q. bisect_left와 bisect_right의 차이점은 무엇인가요?

bisect_left는 삽입할 위치를 찾을 때, 만약 동일한 값을 가진 요소가 이미 리스트에 존재한다면 그 요소의 '가장 왼쪽' 위치를 반환합니다. 반면, bisect_right (또는 단순히 bisect)는 동일한 값이 존재할 경우 그 요소의 '가장 오른쪽' 위치를 반환합니다. 예를 들어, [1, 2, 2, 3] 리스트에 2를 bisect_left로 삽입하면 인덱스 1에, bisect_right로 삽입하면 인덱스 3에 삽입됩니다.




Q. bisect 모듈을 사용하여 정렬된 리스트에 값을 삽입하는 과정은 어떻게 되나요?

bisect 모듈은 실제 삽입 기능을 직접 제공하지 않고, 삽입할 '인덱스'만 반환합니다. 따라서, bisect_left나 bisect_right 함수를 호출하여 삽입할 정확한 위치를 얻은 후, Python 리스트의 insert() 메서드를 사용하여 해당 위치에 값을 삽입해야 합니다. 예를 들어 `my_list.insert(bisect.bisect_left(my_list, new_value), new_value)`와 같은 형태로 사용합니다.




Q. bisect 모듈을 사용하면 리스트가 자동으로 정렬되나요?

아닙니다. bisect 모듈은 '이미 정렬된' 리스트에 효율적으로 요소를 삽입할 수 있도록 돕는 도구일 뿐, 리스트를 자동으로 정렬해주는 기능은 없습니다. bisect 모듈을 사용하기 전에 리스트가 반드시 오름차순으로 정렬되어 있어야 합니다. 만약 정렬되지 않은 리스트에 사용하면 예상치 못한 결과를 얻을 수 있습니다.




Q. bisect_insort 함수는 어떤 역할을 하나요?

bisect_insort 함수는 bisect_left 또는 bisect_right를 사용하여 삽입 위치를 찾은 후, 리스트의 insert() 메서드를 자동으로 호출하여 값을 삽입하는 기능을 합친 것입니다. 따라서, bisect_insort(my_list, new_value)를 사용하면 별도의 insert() 호출 없이 바로 정렬된 리스트에 값이 삽입됩니다. bisect_insort_left와 bisect_insort_right가 존재하며, 이는 각각 bisect_left와 bisect_right와 동일한 삽입 위치 결정 로직을 따릅니다.




Q. bisect 모듈 사용 시 시간 복잡도는 어떻게 되나요?

bisect 모듈의 핵심 함수들(bisect_left, bisect_right)은 이진 탐색 알고리즘을 기반으로 작동하므로, 탐색 시간 복잡도는 O(log n)입니다. 하지만 bisect_insort와 같이 실제 삽입까지 수행하는 경우에는 삽입 위치를 찾는 O(log n) 시간과, 새로운 요소를 삽입하기 위해 기존 요소들을 이동시키는 O(n) 시간이 더해져 총 시간 복잡도는 O(n)이 됩니다. 그럼에도 불구하고, 삽입 위치를 찾는 과정이 매우 빠르기 때문에 리스트가 매우 크더라도 효율적인 삽입이 가능합니다.




Q. bisect 모듈을 사용하면서 주의해야 할 점이 있을까요?

가장 중요한 주의사항은 '반드시 정렬된 리스트에 사용해야 한다'는 점입니다. 또한, bisect_left와 bisect_right의 차이를 명확히 이해하고 상황에 맞게 선택해야 합니다. 동일한 값이 여러 개 있을 때 삽입 순서나 검색 결과에 영향을 줄 수 있기 때문입니다. 삽입 후에도 리스트의 정렬 상태가 유지되는 것을 보장하지만, 만약 리스트가 정렬되지 않은 상태로 bisect 함수를 사용하면 데이터 무결성이 깨질 수 있으므로 주의해야 합니다.




Q. bisect 모듈은 정렬된 문자열 리스트나 튜플에서도 사용할 수 있나요?

bisect 모듈은 기본적으로 리스트에 사용하도록 설계되었습니다. 튜플은 불변(immutable) 객체이기 때문에 직접 요소를 삽입할 수 없어 bisect 모듈을 사용하더라도 원하는 결과를 얻기 어렵습니다. 문자열의 경우에도 마찬가지로 불변이므로, 문자열 리스트에 대해서는 사용할 수 있지만, 개별 문자열 자체에 삽입하는 것은 불가능합니다. 리스트를 사용하여 bisect 모듈의 기능을 활용하는 것이 가장 일반적이고 권장되는 방식입니다.

threeseasct
@threeseasct

공감하셨다면 ❤️ 구독도 환영합니다! 🤗

목차