Python 내장 자료구조
✅ Python 내장 자료구조가 다양한 이유
1. 데이터의 특성과 용도
• 데이터가 순서가 필요한지, 중복을 허용해야 하는지, 변경이 가능한지에 따라 적합한 자료구조가 다릅니다.
2. 성능(시간 및 공간 효율성)
• 자료구조마다 삽입, 삭제, 탐색의 성능이 다릅니다.
• 예를 들어, 리스트는 인덱스로 접근이 빠르지만 중간 삽입·삭제는 느리고, 집합은 중복 제거와 빠른 검색에 유리합니다.
3. 특정 기능 제공
• 특정 자료구조는 특정 기능을 쉽게 제공합니다.
• 예: 딕셔너리는 키-값 쌍 저장, 큐는 FIFO, 스택은 LIFO
4. 메모리 사용 최적화
• 튜플은 리스트보다 메모리 사용량이 적고, 불변(immutable) 특성으로 안전합니다.
✅ 자료구조 선택 기준
자료구조를 선택할 때는 데이터의 특성과 주요 작업을 고려해야 합니다. 다음 기준을 참고하여 선택한다.
1. 데이터의 순서가 중요한가?
• 순서 유지 O → List, Tuple, Deque
• 순서 유지 X → Set, Dictionary (Python 3.7+에서는 딕셔너리도 순서를 유지하지만 기본 목적은 아님)
2. 데이터 수정이 필요한가?
• 변경 가능(Mutable) → List, Set, Dictionary, Deque
• 변경 불가(Immutable) → Tuple, frozenset
3. 중복을 허용해야 하나?
• 중복 허용 O → List, Tuple
• 중복 허용 X → Set, Dictionary의 키
4. 빠른 탐색이 중요한가?
• 빠른 탐색(검색) → Set, Dictionary (해시 기반, O(1))
• 순차 탐색 → List, Tuple (O(n))
5. 삽입·삭제가 자주 발생하는가?
• 끝에서 빠른 삽입·삭제 → List, Deque
• 앞뒤 양방향 삽입·삭제 → Deque
• 중간 삽입·삭제 빈번 → LinkedList (Python 표준에는 없지만 직접 구현 가능)
6. 데이터가 어떻게 처리되어야 하나?
• 스택(LIFO) → List (append(), pop()), Deque
• 큐(FIFO) → Deque, Queue
• 우선순위 큐 → heapq (힙)
✅ 자료구조 선택 요약
| 기준 | 추천 자료구조 |
| 순서 유지 + 변경 가능 | List, Deque |
| 순서유지 + 변경 불가 | Tuple |
| 중복 허용 X | Set, Dictionary(Key) |
| 빠른 검색 | Set, Dictionary |
| 끝 삽입/삭제 | List, Deque |
| 양방향 삽입/삭제 | Deque |
| 우선순위 처리 | Heap(heapq) |
| 불변 데이터 | Tuple, frozenset |
| 집합 연산(합집합, 교집합) | Set |
🔹 List
순서가 있는 변경 가능한(mutable) 자료형. 다양한 자료형을 혼합해서 저장할 수 있음
• 용도: 순서가 중요하고, 중복을 허용하는 데이터
• 예시: 학생들의 점수 목록, 쇼핑 카트
• 선택 이유: 인덱싱과 슬라이싱이 빠르며, 동적으로 크기 변경 가능
예시코드
fruits = ["apple", "banana", "cherry"]
fruits.append("orange") # ['apple', 'banana', 'cherry', 'orange']
메서드
# 리스트 생성 및 추가
fruits = ["apple", "banana", "cherry"]
fruits.append("orange") # 맨 뒤에 추가 ['apple', 'banana', 'cherry', 'orange']
fruits.insert(1, "grape") # 특정 위치에 추가 ['apple', 'grape', 'banana', 'cherry', 'orange']
# 삭제
fruits.remove("banana") # 특정 값 삭제 ['apple', 'grape', 'cherry', 'orange']
fruits.pop() # 마지막 요소 삭제 ['apple', 'grape', 'cherry']
del fruits[1] # 인덱스로 삭제 ['apple', 'cherry']
# 정렬 및 뒤집기
fruits.sort() # 알파벳 순 정렬 ['apple', 'cherry']
fruits.reverse() # 역순으로 뒤집기 ['cherry', 'apple']
생성방법
# 리스트 리터럴: 가장 일반적인 방법
numbers = [1, 2, 3, 4]
# 리스트 컴프리헨션: 조건이나 변형을 적용해 생성
squares = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]
# list() 함수: 반복 가능한 객체를 리스트로 변환
nums = list(range(5)) # [0, 1, 2, 3, 4]
mapped_list = list(map(lambda x: x + 1, [1, 2, 3])) # [2, 3, 4]
Tuple
순서가 있는 변경 불가능한(immutable) 자료형. 리스트보다 메모리 사용이 효율적
• 용도: 변경되지 않는 고정된 데이터
• 예시: 좌표, RGB 색상값
• 선택 이유: 불변성(Immutable)으로 데이터 보호, 메모리 효율적
예시코드
coordinates = (10, 20)
메서드
# 튜플 생성
coordinates = (10, 20, 30)
# 인덱싱 및 슬라이싱
print(coordinates[1]) # 20
print(coordinates[:2]) # (10, 20)
# 개수 세기와 인덱스 찾기
numbers = (1, 2, 2, 3)
print(numbers.count(2)) # 2 (2가 몇 번 등장하는지)
print(numbers.index(3)) # 3의 인덱스: 3
생성방법
# 튜플 리터럴: 괄호 사용
point = (1, 2)
single_element_tuple = (5,) # 쉼표 필수
# tuple() 함수: 리스트나 다른 반복 가능한 객체를 튜플로 변환
nums = tuple(range(3)) # (0, 1, 2)
mapped_tuple = tuple(map(lambda x: x * 2, [1, 2, 3])) # (2, 4, 6)
Dictionary
Key와 Value 쌍으로 데이터를 저장. 순서가 있으며, 키는 고유해야한다.
• 용도: 키-값 쌍으로 빠르게 데이터를 조회해야 할 때
• 예시: 회원 정보(ID → 이름, 나이)
• 선택 이유: 해시 기반으로 빠른 검색 속도(O(1)) 제공
예시코드
student = {"name": "John", "age": 25}
student["grade"] = "A" # {'name': 'John', 'age': 25, 'grade': 'A'}
메서드
# 딕셔너리 생성 및 추가
student = {"name": "John", "age": 25}
student["grade"] = "A" # {'name': 'John', 'age': 25, 'grade': 'A'}
# 키와 값 접근
print(student.get("name")) # 'John'
print(student.keys()) # dict_keys(['name', 'age', 'grade'])
print(student.values()) # dict_values(['John', 25, 'A'])
print(student.items()) # dict_items([('name', 'John'), ('age', 25), ('grade', 'A')])
# 값 삭제
student.pop("age") # {'name': 'John', 'grade': 'A'}
student.clear() # 딕셔너리 초기화 {}
생성방법
# 딕셔너리 리터럴
student = {"name": "Alice", "age": 20}
# 딕셔너리 컴프리헨션
squares = {x: x**2 for x in range(5)} # {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
# dict() 함수
pairs = dict([("a", 1), ("b", 2)]) # {'a': 1, 'b': 2}
mapped_dict = dict(map(lambda x: (x, x**2), range(3))) # {0: 0, 1: 1, 2: 4}
Set
중복을 허용하지 않는 변경 가능한 자료형. 순서가 없음
• 용도: 중복을 제거하고, 교집합·합집합 연산이 필요할 때
• 예시: 중복 없는 태그 목록
• 선택 이유: 중복 자동 제거, 빠른 검색 및 집합 연산
예시코드
unique_numbers = {1, 2, 3, 3, 4} # {1, 2, 3, 4}
unique_numbers.add(5) # {1, 2, 3, 4, 5}
메서드
# 집합 생성 및 추가
unique_numbers = {1, 2, 3}
unique_numbers.add(4) # {1, 2, 3, 4}
# 집합 연산
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
print(a | b) # 합집합 {1, 2, 3, 4, 5, 6}
print(a & b) # 교집합 {3, 4}
print(a - b) # 차집합 {1, 2}
print(a ^ b) # 대칭 차집합 {1, 2, 5, 6}
# 요소 삭제
a.remove(1) # {2, 3, 4}
a.discard(10) # 에러 없이 무시
생성방법
# 집합 리터럴
unique_numbers = {1, 2, 3, 3} # {1, 2, 3}
# set() 함수
unique_chars = set("hello") # {'h', 'e', 'l', 'o'}
# 집합 컴프리헨션
squares_set = {x**2 for x in range(5)} # {0, 1, 4, 9, 16}
Array
리스트와 비슷하지만, 같은 자료형만 저장 가능. 대규모 수치 데이터 처리에 유리함.
예시코드
from array import array
numbers = array('i', [1, 2, 3, 4])
메서드
from array import array
# 배열 생성 (정수형 배열)
numbers = array('i', [1, 2, 3, 4])
# 요소 추가 및 삭제
numbers.append(5) # array('i', [1, 2, 3, 4, 5])
numbers.pop() # array('i', [1, 2, 3, 4])
# 인덱스 접근
print(numbers[2]) # 3
Queue
FIFO(First In, First Out) 구조
from collections import deque
queue = deque([1, 2, 3])
queue.append(4) # [1, 2, 3, 4]
queue.popleft() # 1 -> [2, 3, 4]
Stack
LIFO(Last in, First Out) 구조
stack = []
stack.append(1) # [1]
stack.append(2) # [1, 2]
stack.pop() # 2 -> [1]
Deque(Double-Ended Queue)
양쪽 끝에서 빠르게 추가와 삭제가 가능한 자료구조
from collections import deque
d = deque([1, 2, 3])
d.appendleft(0) # [0, 1, 2, 3]
d.pop() # 3 -> [0, 1, 2]
Heap
우선순위 큐 (priority queue)로 활용. 항상 최소값을 빠르게 찾을 수 있음.
import heapq
heap = [3, 1, 4]
heapq.heapify(heap) # [1, 3, 4]
heapq.heappush(heap, 2) # [1, 2, 4, 3]
heapq.heappop(heap) # 1 -> [2, 3, 4]
✅ 결론
자료구조는 문제의 요구사항에 따라 적절히 선택해야 성능과 효율성이 극대화됩니다.
• 데이터의 성격 (순서, 중복, 변경 가능성)
• 작업의 빈도 (삽입, 삭제, 탐색)
• 성능 요구사항 (속도, 메모리)
