배열과 참조
사물함에 번호가 있으면 12번 칸으로 바로 갈 수 있습니다. 배열도 비슷합니다. 각 항목에는 위치 번호인 인덱스가 있어서 원하는 위치를 바로 읽을 수 있습니다.
배열이 잘하는 일
- 일반적인 배열은 같은 크기의 칸을 연속된 메모리에 둡니다. 시작 위치와 칸의 크기로 원하는 위치를 계산합니다.
- Python의
list는 크기가 늘어나는 배열 방식으로 구현됩니다. 각 칸에는 Python 객체를 가리키는 참조가 들어갑니다. - 인덱스는
0부터 시작합니다. 항목이 세 개라면0,1,2번입니다.
scores = [72, 88, 91]
print(scores[1])
scores.insert(1, 80)
print(scores)
출력은 88, 이어서 [72, 80, 88, 91]입니다. 1번 위치에 80을 넣으려면 원래 그 뒤에 있던 항목들을 한 칸씩 밀어야 합니다.
**인덱스로 읽기는 O(1), 중간 삽입과 삭제는 최악에 O(n)**입니다. 끝에 추가하는 append는 여러 번의 작업을 평균 내면 O(1)입니다. 이를 분할 상환 비용이라고 합니다. 공간을 늘려야 하는 한 번의 추가는 O(n)이 될 수 있습니다.
이름을 하나 더 붙이면 복사될까요?
original = [10, 20]
shared = original
copied = original.copy()
shared[0] = 99
print(original)
print(copied)
결과는 [99, 20]과 [10, 20]입니다. shared와 original은 같은 리스트를 가리킵니다. copy()는 바깥 리스트를 새로 만듭니다. 리스트 안에 다른 리스트가 있으면 그 안쪽 객체는 여전히 공유하는 얕은 복사입니다.
C의 포인터는 메모리 주소를 다룹니다. Python에서는 주소를 직접 계산하지 않고 객체의 참조로 관계를 표현합니다. 이후 연결 리스트의 ‘다음 항목’도 참조로 연결하겠습니다.
확인 문제
배열의 첫 번째 위치에 항목을 자주 넣습니다. 인덱스로 읽기가 빠르다는 이유만으로 좋은 선택일까요?
해설 보기
삽입할 때마다 기존 항목을 뒤로 밀어야 하므로 비용이 큽니다. 먼저 필요한 연산을 따져야 합니다. 양 끝에서 넣고 빼는 일이 많다면 4강의 덱(deque)이 알맞을 수 있습니다.