1 분 소요

선택 문제: k번째로 작은 수 찾기

분할정복을 이용한 k번째 작은 수 찾기

def quick_select(A, left, right, k): 
    pos = partition(A, left, right) 	# A에서 피벗의 인덱스
    print(A)

    if (pos+1 == left+k):				# case 1: 찾음. 완료.
        return A[pos] 
    elif (pos+1 > left+k):				# case 2: 답은 왼쪽 부분리스트에. 
        return quick_select(A, left, pos-1, k) 
    else : 							# case 3: 답은 오른쪽 부분리스트에.
        return quick_select(A, pos+1, right, k-(pos+1-left))

퀵 정렬의 partition 함수

def partition(A, left, right) :
	low = left + 1				    # 왼쪽 부분 리스트의 인덱스 (증가방향)
	high = right					# 오른쪽 부분 리스트의 인덱스 (감소방향)
	pivot = A[left] 				# 피벗 설정 
	while (low <= high) :			# low와 high가 역전되지 않는 한 반복 
	    while low <= right and A[low] <= pivot : low += 1
	    while high>= left  and A[high] > pivot : high -= 1

	    if low < high :			    # 선택된 두 레코드 교환 
	        A[low], A[high] = A[high], A[low]

	A[left], A[high] = A[high], A[left]	# 마지막으로 high와 피벗 항목 교환 
	return high					    # 피벗의 위치 반환

테스트 프로그램

org = [27, 4, 26, 23, 34, 13, 42, 22, 48]
n = len(org) 
array = list(org)
print("입력 리스트 =", array) 
print("[축소정복] 최솟값: ", quick_select(array, 0, n-1, 1))
print()

array = list(org)
print("[축소정복] 최댓값: ", quick_select(array, 0, n-1, n)) 
print()

array = list(org)
print("[축소정복] 중앙값: ", quick_select(array, 0, n-1, 1+(n-1)//2)) 
print()

array.sort()
print("정렬 리스트 =", array) 

array = [6, 5, 7, 9, 18, 3, 8]
n = 7
print("[축소정복] 중앙값: ", quick_select(array, 0, n-1, 1+(n-1)//2)) 
print()

복잡도 분석

  • 최선의 입력은 첫 분할 결과가 case1이 되어 바로 선택이 완료되는 경우입니다 partition() 함수가 $O(n)$이므로 최선의 경우는 $O(n)$
  • 이미 정렬된 리스트에서 가장 큰 항목을 찾는경우(k=n)이 최악 n번의 분할이 필요하므로 시간 복잡도가 $O(n^2)$
  • quick_select()의 평균적인 복잡도가 $O(n)$이다. 따라서 정렬을 이용하는 방법보다 훨씬 우월

댓글남기기