자료구조

배열과 리스트

왜 맨 앞에 끼우면 느릴까

번호표를 든 7명이 줄을 서 있습니다. 새로 온 1번 사람을 맨 앞에 세우려고 합니다.

먼저 예상해 보세요. 맨 앞에 끼우기와 맨 뒤에 붙이기 중 줄 선 사람을 덜 미는 쪽은 어디일까요?

새로 온 사람
1
빈자리
밀어낸 횟수 0

맨 앞에 끼우려고 7번 밀었습니다. 줄 선 7명이 전부 한 칸씩 움직였습니다. 맨 뒤에 붙였다면 0번이었습니다.

맨 앞 = 7번 · 맨 뒤 = 0번맨 앞에 끼우면 줄 선 사람이 전부 한 칸씩 움직입니다.

맨 앞에 끼우기 · 줄 100만 명1,000,000번 밉니다
맨 뒤에 붙이기 · 줄 100만 명0번 밉니다
줄이 길수록 맨 앞에 끼울 때 밀 사람이 그만큼 늘어납니다. 맨 뒤는 길이와 상관없습니다.
  • 칸이 번호 순서로 딱 붙은 줄 = 배열 — 파이썬에서는 list
  • arr.insert(0, x) — 맨 앞에 끼우기. 줄 길이 N 만큼 민다. O(N)
  • arr.append(x) — 맨 뒤에 붙이기. 밀기 0번. O(1)

왜 밀어야 할까

  • 칸이 딱 붙어 있다 — 중간에 빈칸이 없다. 끼우려면 뒤 사람을 전부 밀어야 한다
  • 맨 뒤는 비어 있다 — 밀 사람이 없으니 그냥 붙이면 된다
  • 그 대신 번호로 바로 간다 — 3번 칸은 번호만 알면 줄을 세지 않고 곧장 찾아간다

가끔은 append 도 오래 걸립니다. 칸이 꽉 차면 줄 전체를 더 큰 자리로 옮기기 때문입니다. 파이썬이 그때 칸을 넉넉하게 잡아 두어서 평균하면 append 한 번은 칸 하나 옮기는 일 정도입니다.

코드는 뒤에서부터 한 칸씩 민다

다음을 눌러 한 줄씩 넘기며 빈자리가 맨 앞으로 오는 걸 보세요.

다음을 눌러 한 줄씩 넘겨 보세요

insert_front.py
  1. 1def insert_front(arr, x):
  2. 2 arr.append(None)
  3. 3 moved = 0
  4. 4 for i in range(len(arr) - 1, 0, -1):
  5. 5 arr[i] = arr[i - 1]
  6. 6 moved += 1
  7. 7 arr[0] = x
  8. 8 return moved
0 / 26
  • arr.append(None) — 줄 맨 뒤에 빈자리를 하나 만든다
  • range(len(arr) - 1, 0, -1) — 뒤에서 앞으로 센다. 앞에서부터 밀면 뒤 사람을 덮어쓴다
  • moved — 한 칸 밀 때마다 1씩 는다. 끝나면 줄 길이와 같다

줄이 1,000,000명인 리스트에 arr.insert(0, x) 를 한 번 하면 몇 명이 움직일까요?

한 줄이어도 한 번이 아니다

아래 시간은 Python 3.14 에서 직접 잰 값입니다. 컴퓨터마다 달라서 비율만 보세요.

  • arr.pop(0) — 맨 앞을 빼면 나머지를 한 칸씩 당긴다. 10만 칸에서 5만 번 하면 0.58초. arr.pop() 은 0.0015초
  • arr.insert(0, x) — 맨 앞 끼우기를 10만 번 하면 1.6초. append 10만 번은 0.002초
  • x in arr — 앞에서부터 하나씩 본다. 100만 칸에서 맨 끝 값을 찾는 데 약 0.005초. 반복문 안에서 1,000번 쓰면 약 5초
  • arr[1:] — 줄을 통째로 복사한 새 리스트다. 반복문 안에서 arr = arr[1:] 을 하면 칸 수만큼 복사가 되풀이된다

줄 서기(맨 앞에서 빼고 맨 뒤에 넣기)를 리스트로 흉내 내면 pop(0) 이 계속 나옵니다. 뒤에서 볼 큐와 덱의 deque 는 같은 일을 당기기 없이 한 번에 합니다.

문제에서 이 말이 보이면

맨 앞에서 넣고 뺀다줄(큐)을 리스트로 짠다반복문 안에서 in 을 쓴다

다음 장에서는 한쪽 끝에서만 넣고 빼는 스택을 봅니다.