Code/Algorithm

[Algorithm] 정렬 알고리즘_6(퀵 정렬)

heedy 2023. 1. 8. 19:10
728x90

06-6 퀵 정렬

퀵 정렬 알아보기

퀵 정렬(quick sort): 가장 빠른 정렬 알고리즘

퀵 정렬의 예

위 그림은 퀵 정렬 알고리즘을 사용하여 학생 그룹을 키 순서로 정렬하는 과정을 나타냈습니다.

먼저 키가 168cm인 학생 A를 선택하여 이 학생을 기준으로 168cm 미만인 그룹과 168cm 이상인 그룹으로 나눕니다. 이때 그룹을 나누는 기준(학생 A의 키)을 피벗(pivot)이라고 합니다.
[피벗은 다른 말로 중심축이라고 합니다. 피벗은 임의로 선택할 수 있고, 선택된 피벗은 2개로 나눈 그룹 어디에 넣어도 상관없습니다.]
다시 각 그룹에서 피벗을 선택하여 나누기를 반복하며 모든 그룹이 1명씩 남으면 정렬이 완료됩니다.


배열을 두 그룹으로 나누기

먼저  피벗을  x, 왼쪽 끝 원소의 인덱스를 pl(왼쪽 커서), 오른쪽 끝 원소의 인덱스를 pr(오른쪽 커서)라고 하겠습니다.

그룹을 나누려면 피벗 이하인 원소를 배열 왼쪽(맨 앞쪽)으로, 피벗 이상인 원소를 배열 오른쪽(맨 뒤쪽)으로 이동시켜야 합니다.

  • a[pl] >= x가 성립하는 원소를 찾을 때까지 pl을 오른쪽 방향으로 스캔합니다.
  • a[pr] <= x가 성립하는 원소를 찾을 때까지 pr를 왼쪽 방향으로 스캔합니다.

pl과 pr가 교차하면 이로써 그룹을 나누는 과정이 끝나고, 배열은 다음과 같이 두 그룹으로 나뉩니다.

  • 피벗 이하인 그룹: a[0], ···, a[pl - 1]
  • 피벗 이상인 그룹: a[pr + 1], ···, a[n -1]

또 그룹을 나누는 작업이 긑난 다음 pl > pr + 1일 때에 한해서 다음과 같은 그룹이 만들어집니다.

  • 피벗과 일치하는 그룹: a[pr + 1], ···, a[pl - 1]

실습 6-10 배열을 두 그룹으로 나누기

# 배열을 두 그룹으로 나누기

from typing import MutableSequence

def partition(a: MutableSequence) -> None:
  """배열을 나누어 출력"""
  n = len(a)
  pl = 0        # 왼쪽 커서
  pr = n - 1    # 오른쪽 커서
  x = a[n // 2] # 피벗(가운데 원소)


  while pl <= pr:
    while a[pl] < x: pl += 1
    while a[pr] > x: pr -= 1
    if pl <= pr:
      a[pl], a[pr] = a[pr], a[pl] 
      pl += 1
      pr -= 1

  print(f'피벗은 {x}입니다.')

  print('피벗 이하인 그룹입니다.')
  print(*a[0 : pl])       # a[0] ! a[pl - 1]

  if pl > pr + 1:
    print('피벗과 일치하는 그룹입니다.')
    print(*a[pr + 1 : pl])  # a[pr + 1] ~ a[pl - 1]
  
  print('피벗 이상인 그룹입니다.')
  print(*a[pr + 1 : n])     # a[pr + 1] ~ a[n - 1]

if __name__ == '__main__':
  print('배열을 나눕니다.')
  num = int(input('원소 수를 입력하세요.: '))
  x = [None] * num                # 원소 수가 num인 배열을 생성

  for i in range(num):
    x[i] = int(input(f'x[{i}]: '))

  partition(x)
배열을 나눕니다.
원소 수를 입력하세요.: 9
x[0]: 1
x[1]: 8
x[2]: 7
x[3]: 4
x[4]: 5
x[5]: 2
x[6]: 6
x[7]: 3
x[8]: 9
피벗은 5입니다.
피벗 이하인 그룹입니다.
1 3 2 4 5
피벗과 일치하는 그룹입니다.
5
피벗 이상인 그룹입니다.
5 7 6 8 9

이 프로그램에서는 배열 가운데에 있는 원소를 피벗으로 선택했습니다.
피벗은 어떤 값으로 선택하느냐에 따라 배열을 나누는 것과 정렬하는 성능(performance)에 영향을 미칩니다.


퀵 정렬 만들기

지금까지 알아본 배열을 두 그룹으로 나누는 것을 조금 더 발전시키면 퀵 정렬 알고리즘이 됩니다.

퀵 정렬의 배열 나누기

원소 수가 2개 이상인 그룹만 다음과 같이 반복해서 나눕니다.

  • pr가 a[0]보다 오른쪽에 위치하면(left < pr) 왼쪽 그룹을 나눕니다.
  • pl이 a[8]보다 왼쪽에 위치하면(pl < right) 오른쪽 그룹을 나눕니다.

실습 6-11 퀵 정렬 알고리즘 구현하기

# 퀵 정렬 알고리즘 구현하기

from typing import MutableSequence

def qsort(a: MutableSequence, left: int, right: int) -> None:
  """a[left] ~ a[right]를 퀵 정렬"""
  pl = left                       # 왼쪽 커서
  pr = right                      # 오른쪽 커서
  x = a[(left + right) // 2]      # 피벗(가운데 원소)

  while pl <= pr:
    while a[pl] < x: pl += 1
    while a[pr] > x: pr -= 1
    if pl <= pr:
      a[pl], a[pr] =  a[pr], a[pl]
      pl += 1
      pr -= 1

  if left < pr: qsort(a, left, pr)
  if pl < right: qsort(a, pl, right)

def quick_sort(a: MutableSequence) -> None:
  """퀵 정렬"""
  qsort(a, 0, len(a) - 1)



if __name__ == '__main__':
  print('퀵 정렬을 수행합니다.')
  num = int(input('원소 수를 입력하세요.: '))
  x = [None] * num                # 원소 수가 num인 배열을 생성

  for i in range(num):
    x[i] = int(input(f'x[{i}]: '))

  quick_sort(x)                   # 배열 x를 퀵 정렬

  print('오름차순으로 정렬했습니다.')
  for i in range(num):
    print(f'x[{i}] = {x[i]}')
퀵 정렬을 수행합니다.
원소 수를 입력하세요.: 9
x[0]: 5
x[1]: 8
x[2]: 4
x[3]: 2
x[4]: 6
x[5]: 1
x[6]: 3
x[7]: 9
x[8]: 7
오름차순으로 정렬했습니다.
x[0] = 1
x[1] = 2
x[2] = 3
x[3] = 4
x[4] = 5
x[5] = 6
x[6] = 7
x[7] = 8
x[8] = 9

11 ~ 17행은 실습 6-10의 12 ~ 18행과 똑같이 작성되었으며 나누는 과정을 수행합니다. 그리고 좌우 각 그룹을 다시 나누기 위해 qsort() 함수 끝부분인 19 ~ 20행에서 재귀 호출을 추가했습니다.

하지만 퀵 정렬은 서로 이웃하지 않는 원소를 교환하므로 안정적이지 않은 알고리즘입니다.


퀵 정렬에서 나누는 과정 출력하기

실습 6C-3 퀵 정렬 알고리즘 구현하기(배열을 나누는 과정 출력)

# 퀵 정렬 알고리즘 구현하기(배열을 나누는 과정 출력)

from typing import MutableSequence

def qsort(a: MutableSequence, left: int, right: int) -> None:
  """a[left] ~ a[right]를 퀵 정렬(배열을 나누는 과정 출력)"""
  pl = left                       # 왼쪽 커서
  pr = right                      # 오른쪽 커서
  x = a[(left + right) // 2]      # 피벗(가운데 원소)

  print(f'a[{left}] ~ a[{right}]:', *a[left: right + 1])    # 새로 추가된 부분

  while pl <= pr:
    while a[pl] < x: pl += 1
    while a[pr] > x: pr -= 1
    if pl <= pr:
      a[pl], a[pr] =  a[pr], a[pl]
      pl += 1
      pr -= 1

  if left < pr: qsort(a, left, pr)
  if pl < right: qsort(a, pl, right)

def quick_sort(a: MutableSequence) -> None:
  """퀵 정렬"""
  qsort(a, 0, len(a) - 1)



if __name__ == '__main__':
  print('퀵 정렬을 수행합니다.')
  num = int(input('원소 수를 입력하세요.: '))
  x = [None] * num                # 원소 수가 num인 배열을 생성

  for i in range(num):
    x[i] = int(input(f'x[{i}]: '))

  quick_sort(x)                   # 배열 x를 퀵 정렬

  print('오름차순으로 정렬했습니다.')
  for i in range(num):
    print(f'x[{i}] = {x[i]}')
퀵 정렬을 수행합니다.
원소 수를 입력하세요.: 9
x[0]: 5
x[1]: 8
x[2]: 4
x[3]: 2
x[4]: 6
x[5]: 1
x[6]: 3
x[7]: 9
x[8]: 7
a[0] ~ a[8]: 5 8 4 2 6 1 3 9 7
a[0] ~ a[4]: 5 3 4 2 1
a[0] ~ a[2]: 1 3 2
a[0] ~ a[1]: 1 2
a[3] ~ a[4]: 4 5
a[5] ~ a[8]: 6 8 9 7
a[5] ~ a[6]: 6 7
a[7] ~ a[8]: 9 8
오름차순으로 정렬했습니다.
x[0] = 1
x[1] = 2
x[2] = 3
x[3] = 4
x[4] = 5
x[5] = 6
x[6] = 7
x[7] = 8
x[8] = 9

퀵 정렬의 배열 나누기(전체 과정)


비 재귀적인 퀵 정렬 만들기

비 재귀적인 퀵 정렬은 05-2절에서 배운 재귀 함수 recur()를 비재귀적으로 구현하는 방법과 같이 만들 수 있습니다.

재귀 함수 비재귀적 구현

2022.12.09 - [Code/Algorithm] - [Algorithm] 재귀 알고리즘_2

 

[Algorithm] 재귀 알고리즘_2

05-2 재귀 알고리즘 분석 재귀 알고리즘의 2가지 분석 방법 실습 5-3 순수한 재귀 함수 구현하기 # 순수한 재귀 함수 구현하기 # 순수한(genuinely) 재쉬 -> 재귀 호출을 여러 번 실행하는 함수 def recur(n

heejins.tistory.com

실습 6-12 비재귀적인 퀵 정렬 구현하기

# 비 재귀적인 퀵 정렬 구현하기

from stack import Stack
from typing import MutableSequence

def qsort(a: MutableSequence, left: int, right: int) -> None:
  """a[left] ~ a[right]를 퀵 정렬(비재귀적인 퀵 정렬)"""
  range = Stack(right - left + 1)       # 스택 생성

  range.push((left, right))

  while not range.is_empty():
    pl, pr = left, right = range.pop()  # 왼쪽, 오른쪽 커서를 꺼냄
    x = a[(left + right) // 2]          # 피벗(가운데 원소)

    while pl <= pr:
      while a[pl] < x: pl += 1
      while a[pr] > x: pr -= 1
      if pl <= pr:
        a[pl], a[pr] = a[pr], a[pl]
        pl += 1
        pr -= 1

    if left < pr: range.push((left, pr))    # 왼쪽 그룹의 커서를 저장
    if pl < right: range.push((pl, right))  # 오른쪽 그룹의 커서를 저장

def quick_sort(a: MutableSequence) -> None:
  """퀵 정렬"""
  qsort(a, 0, len(a) - 1)

if __name__ == '__main__':
  print('퀵 정렬을 수행합니다.')
  num = int(input('원소 수를 입력하세요.: '))
  x = [None] * num                # 원소 수가 num인 배열을 생성

  for i in range(num):
    x[i] = int(input(f'x[{i}]: '))

  quick_sort(x)                   # 배열 x를 퀵 정렬

  print('오름차순으로 정렬했습니다.')
  for i in range(num):
    print(f'x[{i}] = {x[i]}')
퀵 정렬을 수행합니다.
원소 수를 입력하세요.: 9
x[0]: 5
x[1]: 8
x[2]: 4
x[3]: 2
x[4]: 6
x[5]: 1
x[6]: 3
x[7]: 9
x[8]: 7
오름차순으로 정렬했습니다.
x[0] = 1
x[1] = 2
x[2] = 3
x[3] = 4
x[4] = 5
x[5] = 6
x[6] = 7
x[7] = 8
x[8] = 9

05-2절에서 다룬 비재귀적으로 구현한 함수 recur()와 마찬가지로 qsort() 함수에서도 데이터를 임시 저장하기 위해 다음 스택을 사용합니다.

  • range: 나눌 범위에서 맨 앞 원소의 인덱스와 맨 끝 원소의 인덱스를 조합한 튜플 선택
  • 스택의 크기는 right - left + 1이며 나누는 배열의 원소 수와 같습니다.

비재귀적으로 구현한 퀵 정렬의 배열 나누기와 스택의 변화

  • 10행: 튜플(left, right)를 스택 range에 푸시합니다. left와 right는 나눠야 할 배열의 범위인 맨 앞 원소 인덱스와 맨 끝 원소 인덱스입니다.
    12행에 나오는 while 문은 스택이 비어 있지 않은 동안 작업을 반복합니다.
    참고로 스택은 나눠야 할 배열의 범위이므로, 스택이 비어 있다면 나눌 배열이 없다는 것입니다. 반대로 스택이 비어 있지 않다면 나눠야 할 배열이 있다는 것입니다.
  • 13행: 스택에서 팝한 튜플(pl, pr)를 (left, right)에 대입합니다(위 그림 b). 그 결과 pl과 left는 0이 되고, pr와 right는 8이 됩니다. 이 값은 나눠야 할 배열의 범위를 의미합니다.
    따라서 a[0] ~ a[8]을 나눕니다. 그러면 a[0] ~ a[4]의 왼쪽 그룹과 a[5] ~ a[8]의 오른쪽 그룹으로 나누어집니다(pl은 5가 되고, pr는 4가 됩니다).
  • 24행: if 문에서 스택에 튜플 (0, 4)를 푸시합니다.
  • 25행: if 문에서 튜플 (5,8)을 푸시합니다. 그 결과 스택은 c 상태가 됩니다.
  • 12행: while 문의 동작으로 루프 본문을 반복합니다.
  • 13행: 스택에서 팝한 튜플을 (pl, pr)와 (left, right)에 대입합니다(위 그림 d). 그 결과 pl과 left는 5가 되고, pr와 right는 8이 됩니다. 따라서 배열 a[5] ~ a[8]을 나눕니다. 그러면 a[5] ~ a[6]의 왼쪽 그룹과 a[7] ~ a[8]의 오른쪽 그룹으로 나누어집니다(pl은 7이 되고, pr는 6이 됩니다).
  • 24행: if 문에서 스택에 튜플 (5, 6)을 푸시합니다.
  • 25행: if 문에서 튜플 (7, 8)을 푸시합니다. 그 결과 스택은 e 상태가 됩니다.

스택에 푸시한 값은 나눠야 할 배열의 맨 앞 원소와 맨 끝 원소의 인덱스입니다. 배열을 나누는 작업을 완료하면 왼쪽 그룹의 인덱스와 오른쪽 그룹의 인덱스 범위를 푸시합니다. 그리고 다시 스택에서 팝한 범위를 나누는 작업을 반복하여 정렬을 수행합니다. 위 그림 n과 같이 스택이 비면 정렬이 끝납니다.


스택의 크기

배열을 스택에 푸시하는 순서를 정할 때는 다음 2가지 규칙을 고려할 수 있습니다.

  • 규칙1: 원소 수가 많은 쪽의 그룹을 먼저 푸시합니다.
  • 규칙2: 원소 수가 적은 쪽의 그룹을 먼저 푸시합니다.

일반적으로 원소 수가 적은 배열일수록 나누는 과정을 빠르게 마칠 수 있습니다.
규칙 1과 같이 원소 수가 많은 그룹의 나누기를 나중에 하고, 원소 수가 적은 그룹의 나누기를 먼저 하면 스택에 동시에 쌓는 데이터 개수는 적어집니다.
규칙 1, 2의 경우 스택에 넣고 꺼내는 횟수(푸시, 팝)는 같지만, 동시에 쌓이는 데이터의 최대 개수는 다릅니다.


피벗 선택하기

피벗 선택 방법은 퀵 정렬의 실행 효율에 큰 영향을 미칩니다. 
다음 배열을 예로 들어 살펴봅시다.

8 7 6 5 4 3 2 1 0

빠른 정렬을 원한다면 배열을 정렬한 뒤 가운데에 위치하는 값, 즉 전체에서 중앙값을 피벗으로 하는 것이 이상적입니다.
그러면 배열이 한쪽으로 치우치지 않고 절반 크기로 나누어집니다. 하지만 정렬된 배열의 중앙값을 구하려면 그에 대한 처리가 필요하고, 이 처리를 위해 많은 계산 시간이 걸립니다. 결국 피벗을 선택하는 의미가 없어집니다.

이 문제를 해결하기 위해 다음 방법을 사용하면 최악의 경우를 피할 수 있습니다.

  • 방법 1: 나누어야 할 배열의 원소 수가 3 이상이면, 배열에서 임의의 원소 3개를 꺼내 중앙값인 원소를 피벗으로 선택합니다.
  • 방법 2: 나누어야 할 배열의 맨 앞 원소, 가운데 원소, 맨 끝 원소를 정렬한 뒤 가운데 원소와 맨 끝에서 두 번째 원소를 교환합니다. 맨 끝에서 두 번째 원솟값 a[right - 1]이 피벗으로 선택되었고, 그 동시에 나눌 대상을 a[left + 1] ~ a[right -2]로 좁힙니다.

방법 2 적용 피벗 선택과 분할 범위 축소

  • a: 정렬하기 전의 상태입니다. 맨 앞 원소(8), 가운데 원소(4), 맨 끝 원소(0)를 정렬합니다.
  • b: 정렬한 후 맨 앞 원소는 0, 가운데 원소는 4, 맨 끝 원소는 8이 됩니다. 여기서 가운데 원소(4)와 맨 끝에서 두 번째 원소(1)를 교환합니다.
  • c: 이때 맨 끝에서 두 번째 원소에 위치한 값(4)을 피벗으로 선택합니다. a[left]의 0은 피벗이하인 값이고, a[right - 1]과 a[right]의 4와 8은 피벗 이상인 값입니다.

이 과정을 거치고 나면 스캔할 커서의 시작 위치(pl, pr)를 다음과 같이 변경하여 나눌 대상 범위를 좁힐 수 있습니다.

  • 왼쪽 커서 pl의 시작 위치: left → left + 1    # 오른쪽으로 1칸 옮김
  • 오른쪽 커서 pr의 시작 위치: right → right - 2    # 왼쪽으로 2칸 옮김

퀵 정렬의 시간 복잡도

퀵 정렬은 배열을 조금씩 나누어 보다 작은 문제를 푸는 과정을 반복하므로 시간 복잡도는 O(n log n)입니다. 그런데 정렬하는 배열의 초깃값이나 피벗을 선택하는 방법에 따라 실행 시간 복잡도가 증가하는 경우가 있습니다. 예를 들어 매번 1개의 원소와 나머지 원소로 나누어진다면 n번의 분할이 필요합니다. 이렇나 최악의 경우 시간 복잡도는 O(n²)이 됩니다.

퀵 정렬은 원소 수가 적은 경우에는 그다지 빠른 알고리즘이 아닌 것으로 알려져 있습니다. 그래서 다음 2가지 방법을 적용하여 실습 6-13 프로그램을 작성했습니다.

  • 원소 수가 9개 미만인 경우 단순 삽입 정렬로 전환합니다.
  • 피벗 선택은 방법 2를 채택합니다.

실습 6-13 퀵 정렬 알고리즘 구현하기(원소 수가 9 미만이면 단순 삽입 정렬)

# 퀵 정렬 알고리즘 구현하기(원소 수가 9 미만이면 단순 삽입 정렬)

from typing import MutableSequence

def sort3(a: MutableSequence, idx1: int, idx2: int, idx3: int):
  """a[idx1], a[idx2], a[idx3]을 오름차순으로 정렬하고 중앙값의 인덱스를 반환"""
  if a[idx2] < a[idx1]:a[idx2], a[idx1] = a[idx1], a[idx2]
  if a[idx3] < a[idx2]:a[idx3], a[idx2] = a[idx2], a[idx3]
  if a[idx2] < a[idx1]:a[idx2], a[idx1] = a[idx1], a[idx2]
  return idx2

def insertion_sort(a: MutableSequence, left:int, right: int) -> None:
  """a[left] ~ a[right]를 퀵 정렬"""
  if right - left < 9:                # 원소 수가 9 미만이면 단순 삽입 정렬로 전환
    insertion_sort(a, left, right)
  else:
    pl = left                         # 왼쪽 커서
    pr = right                        # 오른쪽 커서
    m = sort3(a, pl, (pl + pr) // 2, pr)
    x = a[m]

    a[m], a[pr - 1]= a[pr - 1], a[m]
    pl += 1
    pr -= 2
    while pl <= pr:
      while a[pl] < x: pl += 1
      while a[pr] > x: pr -= 1
      if pl <= pr:
        a[pl], a[pr] = a[pr], a[pl]
        pl += 1
        pr -= 1

    if left < pr: qsort(a, left, pr)
    if pl < right: qsort(a, pl, right)


def quick_sort(a: MutableSequence) -> None:
  """퀵 정렬"""
  qsort(a, 0, len(a) - 1)

if __name__ == '__main__':
  print('퀵 정렬을 합니다.(원소 수가 9 미만이면 단순 삽입 정렬을 합니다).')
  num = int(input('원소 수를 입력하세요.: '))
  x = [None] * num                # 원소 수가 num인 배열을 생성

  for i in range(num):
    x[i] = int(input(f'x[{i}]: '))

  quick_sort(x)                   # 배열 x를 퀵 정렬

  print('오름차순으로 정렬했습니다.')
  for i in range(num):
    print(f'x[{i}] = {x[i]}')
퀵 정렬을 합니다.(원소 수가 9 미만이면 단순 삽입 정렬을 합니다).
원소 수를 입력하세요.: 12
x[0]: 5
x[1]: 8
x[2]: 4
x[3]: 2
x[4]: 6
x[5]: 1
x[6]: 3
x[7]: 9
x[8]: 7
x[9]: 0
x[10]: 3
x[11]: 5
오름차순으로 정렬했습니다.
x[0] = 0
x[1] = 1
x[2] = 2
x[3] = 3
x[4] = 3
x[5] = 4
x[6] = 5
x[7] = 5
x[8] = 6
x[9] = 7
x[10] = 8
x[11] = 9

sorted() 함수로 정렬하기

파이썬에서는 정렬을 수행하는 sorted() 함수를 내장 함수로 제공합니다. 이 함수는 전달받은(임의의 자료형) 이터러블 객체의 원소를 정렬하여 list형으로 반환합니다. sorted() 함수는 '정렬을 직접(inplace) 수행' 하지 않고 '정렬을 수행한 뒤 늘어선 원소를 새로운 리스트로 생성하여 반환'합니다. 

a, b = sorted([a, b])				# a, b를 오름차순으로 정렬
a, b, c = sorted([a, b, c])			# a, b, c를 오름차순으로 정렬
a, b, c, d = sorted([a, b, c, d])	# a, b, c, d를 오름차순으로 정렬

sorted() 함수는 오름차순을 기본으로 하지만, reverse에 True값을 넘겨주면 내림차순 정렬을 수행합니다.

실습 6C-4 sorted() 함수를 사용하여 정렬하기

# sorted() 함수를 사용하여 정렬하기

print('sorted() 함수를 사용하여 정렬합니다.')
num = int(input('원소 수를 입력하세요.: '))
x = [None] * num    # 원소 수가 num인 배열을 생성

for i in range(num):
  x[i] = int(input(f'x[{i}]:'))

# 배열 x를 오름차순으로 정렬
x = sorted(x)
print('오름차순으로 정렬했습니다.')
for i in range(num):
  print(f'x[{i}] = {x[i]}')

# 배열 x를 내림차순으로 정렬
x = sorted(x, reverse = True)
print('내림차순으로 정렬했습니다.')
for i in range(num):
  print(f'x[{i}] = {x[i]}')
sorted() 함수를 사용하여 정렬합니다.
원소 수를 입력하세요.: 5
x[0]:6
x[1]:4
x[2]:3
x[3]:7
x[4]:1
오름차순으로 정렬했습니다.
x[0] = 1
x[1] = 3
x[2] = 4
x[3] = 6
x[4] = 7
내림차순으로 정렬했습니다.
x[0] = 7
x[1] = 6
x[2] = 4
x[3] = 3
x[4] = 1

튜플은 이뮤터블의 속성을 가지므로 튜플 자체를 정렬할 수는 없습니다. 튜플을 정렬해야 한다면 다음과 같은 2단계 방법을 사용합니다.

  • 1단계: sorted() 함수로 정렬한 원소의 나열에서 새로운 리스트를 생성합니다.
  • 2단계: 생성한 리스트를 튜플로 변환합니다.

 

- 출처: Do it! 자료구조와 함께 배우는 알고리즘 입문 - 파이썬 편
- notebook ipynb file:
https://github.com/heejvely/Algorithm/blob/main/Do_it_algorithm/Do_it_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98_%EC%A0%95%EB%A0%AC%20%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98.ipynb

 

GitHub - heejvely/Algorithm: 파이썬 알고리즘 인터뷰 책을 참고한 파이썬 알고리즘 연습

파이썬 알고리즘 인터뷰 책을 참고한 파이썬 알고리즘 연습. Contribute to heejvely/Algorithm development by creating an account on GitHub.

github.com

 

728x90