Quick Selection
2020-1ํ๊ธฐ, ๋ํ์์ โ์๊ณ ๋ฆฌ์ฆโ ์์ ์ ๋ฃ๊ณ ๊ณต๋ถํ ๋ฐ๋ฅผ ์ ๋ฆฌํ ๊ธ์ ๋๋ค. ์ง์ ์ ์ธ์ ๋ ํ์์ ๋๋ค :) ์ ์ฒด ํฌ์คํธ๋ Algorithm ํฌ์คํธ์์ ํ์ธํ์ค ์ ์์ต๋๋ค.
๋ค์ด๊ฐ๋ฉฐ
์์์ ์ซ์ ๋ฐฐ์ด์ด ์์ ๋ $k$๋ฒ์งธ๋ก ์์ ๊ฐ์ ์ฐพ๋ ๊ฒ์ โSelectionโ์ด๋ผ๊ณ ํ๋ค. ์ฝ๊ฒ ์๊ฐํ๋ฉด ๋ฐฐ์ด์ ์ ๋ ฌํ ํ์ $k$๋ฒ์งธ ๊ฐ์ ์ ํํ๋ฉด ๋์ง๋ง, ํต ์ ๋ ฌ์ ์ด๋ค๋ฉด ํ๊ท $O(n \log n)$ ์ต์ ์ ๊ฒฝ์ฐ $O(n^2)$์ ๋น์ฉ์ด ๋ ๋ค. ์ด๋ฒ์ ๋ค๋ฃฐ ์๊ณ ๋ฆฌ์ฆ์ โ์ ๋ ฌ๋์ง ์์ ๋ฐฐ์ดโ์์ $k$๋ฒ์งธ๋ก ์์/ํฐ ๊ฐ์ ๋น ๋ฅด๊ฒ ์ฐพ๋ โQuick Selectionโ์ด๋ค. โQuick Selectionโ์ $O(n)$์ ๋น์ฉ์ผ๋ก $k$๋ฒ์ฌ ๊ฐ์ ์ฐพ์ ์ ์๋ค!
Idea
โQuick Selectionโ์ ์์ด๋์ด๋ ํต ์ ๋ ฌ๊ณผ ์ ๋ง ๋น์ทํ๋ค! โQuick Selectionโ๋ pivot์ ๊ณ ๋ฅด๊ณ left, center, right๋ก ๋ฐฐ์ด์ ๋ถํ ํ๋ค.
- input: a list $S$ of numbers and an integer $k$
- output: The $k$-th smallest elt in $S$
Choose a number $v$ from $S$, split $S$ into 3 sublists w.r.t. $v$.

์ด์ $S_L$, $S_v$, $S_R$์ ํฌ๊ธฐ๋ฅผ ๊ธฐ์ค์ผ๋ก ์ฌ๊ท ํจ์๋ฅผ ํธ์ถํ๋ค.
\[\text{selection}(S, k) = \begin{cases} \text{selection}(S_L, k) & \text{if} \quad k \le \left|S_L\right| \\ v & \text{if} \quad \left| S_L \right| < k \le \left|S_L\right| + \left|S_v\right| \\ \text{selection}(S_R, k - \left|S_L\right| -\left|S_v\right| ) & \text{if} \quad k > \left|S_L\right| + \left|S_v\right| \\ \end{cases}\]โQuick Selectionโ ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ๋จํ์ง๋ง ๊ฐ๋ ฅํ๋ค ๐
How to set pivot
โQuick Selectionโ์ ์ฃผ์ํ ์ด์๋ ๊ธฐ์ค์ด ๋๋ $v$ ๊ฐ์ ์ด๋ป๊ฒ ์ก๋๋ ์ด๋ค. ๋ง์ฝ ๋งค ๊ณผ์ ๋ง๋ค $v$๋ฅผ the smallest elt๋ก ์ก์ผ๋ฉด ์ต์ ์ ์ผ์ด์ค๋ก $O(n^2)$์ ๋น์ฉ์ด ๋ค๊ฒ ๋๋ค.
\[T(n) = T(n-1) + O(n) = O(n^2)\]์ฌ์ค ์ข์ pivot์ ์ก๊ธฐ ์ํ ๋ ๋ง์ ๊ณ ์ฐฐ๋ค์ด ์์ง๋ง ์ด๋ฒ ํฌ์คํธ์์๋ โQuick Selectionโ์ ์์ด๋์ด๋ง ๊ฐ๋จํ ์ค๋ช ํ๊ณ ๋์ด๊ฐ๋๋ก ํ๊ฒ ๋ค. pivot์ ์ ์ก๊ฒ ๋๋ฉด $O(n)$์ ๋น์ฉ์ด ๋จ์ ์ฆ๋ช ํ ์ ์๋ค. ์๋์ ์ํฐํด์์ ์ด ๋ด์ฉ์ ์ ๋ค๋ฃจ๊ณ ์์ผ๋ ๋ ๊ถ๊ธํ ์ฌ๋๋ค์๊ฒ ์ถ์ฒํ๋ค.
๐ Quick Selection ์๊ณ ๋ฆฌ์ฆ
๐ Median of Medians ์๊ณ ๋ฆฌ์ฆ
Implementation
๋ฆฌํธ์ฝ๋์ 215. Kth Largest Element in an Array ๋ฌธ์ ๋ฅผ ํตํด ํด๋น ๋ฌธ์ ๋ฅผ ํ์ด๋ณผ ์ ์์ต๋๋ค.
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
# print(sorted(nums))
mid = len(nums) // 2
pivot = nums[mid]
less_list = []
same_list = []
more_list = []
for num in nums:
if pivot == num:
same_list.append(num)
elif pivot < num:
more_list.append(num)
else:
less_list.append(num)
if len(more_list) >= k:
return self.findKthLargest(more_list, k)
elif k <= (len(more_list) + len(same_list)):
return pivot
else:
return self.findKthLargest(less_list, k - (len(more_list) + len(same_list)))
์ฐธ๊ณ ๋ก C++์์๋ โQuick Selectionโ์ ๊ตฌํํ ํ์์์ด nth_element()
ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค ๐