Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions 0215.Kth-Largest-Element-in-an-Array/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# 215. Kth Largest Element in an Array

## step1

heapを使った解法を思いつく。3minほど。

heappushpopを使った場合も書いた。

heapreplaceというメソッドもあるが今回は使わない。

## step2

Mediumとなっていることから考えて、本来の出題意図はQuick Selectを書かせたいのではないだろうか。

愚直に書くとTLEしたので、pivotの最初と終わりを返すようにした。

かなり時間がかかってしまった(測り忘れた)。添字と長さで混乱し、手で具体例を書いて添字を合わせた。

以下の問題のアルゴリズム(Dutch National Flag)に似ている。

https://leetcode.com/problems/sort-colors/description/?envType=problem-list-v2&envId=rab78cw1

Arai60の問題:

https://leetcode.com/problems/kth-largest-element-in-a-stream/description/

解くだけなら今回の問題の方が簡単だがQuick Selectを書くのが大変。

書き直したら分かりやすくなった。

## step3

Quick selectを書く。

間違えた点:

- left == rightの場合の処理(これがないと無限ループ)
- dutch flagのwhileの条件を<とした
24 changes: 24 additions & 0 deletions 0215.Kth-Largest-Element-in-an-Array/step1_heap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import heapq


class Solution:
def findKthLargest(self, nums: list[int], k: int) -> int:
heap = []
for n in nums:
heapq.heappush(heap, n)
if len(heap) > k:
heapq.heappop(heap)

return heapq.heappop(heap)


class Solution:
def findKthLargest(self, nums: list[int], k: int) -> int:
heap = []
for n in nums:
if len(heap) == k:
heapq.heappushpop(heap, n)
else:
heapq.heappush(heap, n)

return heapq.heappop(heap)
43 changes: 43 additions & 0 deletions 0215.Kth-Largest-Element-in-an-Array/step2_quick_select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import random

random.seed(42)


class Solution:
def findKthLargest(self, nums: list[int], k: int) -> int:
def partition(pivot_index, left, right):
if left == right:
return nums[left]

pivot_value = nums[pivot_index]
end_of_smaller = left
end_of_pivot = right
i = left
while i <= end_of_pivot:
if nums[i] < pivot_value:
nums[i], nums[end_of_smaller] = nums[end_of_smaller], nums[i]
end_of_smaller += 1
i += 1
elif nums[i] > pivot_value:
nums[i], nums[end_of_pivot] = nums[end_of_pivot], nums[i]
end_of_pivot -= 1
else:
i += 1

return end_of_smaller, end_of_pivot

def quick_select(left, right, l):
if left == right:
return nums[left]
pivot_index = random.randint(left, right)
end_of_smaller, end_of_pivot = partition(pivot_index, left, right)
if right + 1 - end_of_pivot < l <= right + 1 - end_of_smaller:
return nums[end_of_smaller]
elif right + 1 - end_of_pivot < l:
return quick_select(
left, end_of_smaller - 1, l - (right + 1 - end_of_smaller)
)
else: # l <= right + 1 - end_of_pivot
return quick_select(end_of_pivot, right, l)

return quick_select(0, len(nums) - 1, k)
43 changes: 43 additions & 0 deletions 0215.Kth-Largest-Element-in-an-Array/step2_quick_select_revised.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import random

random.seed(42)


class Solution:
def findKthLargest(self, nums: list[int], k: int) -> int:
def partition(pivot_index, left, right):
if left == right:
return nums[left]

pivot_value = nums[pivot_index]
end_of_smaller = left
end_of_pivot = right
i = left
while i <= end_of_pivot:
if nums[i] < pivot_value:
nums[i], nums[end_of_smaller] = nums[end_of_smaller], nums[i]
end_of_smaller += 1
i += 1
elif nums[i] > pivot_value:
nums[i], nums[end_of_pivot] = nums[end_of_pivot], nums[i]
end_of_pivot -= 1
else:
i += 1

return end_of_smaller, end_of_pivot

target_index = len(nums) - k

def quick_select(left, right):
if left == right:
return nums[left]
pivot_index = random.randint(left, right)
end_of_smaller, end_of_pivot = partition(pivot_index, left, right)
if end_of_smaller <= target_index < end_of_pivot:
return nums[end_of_smaller]
elif target_index < end_of_smaller:
return quick_select(left, end_of_smaller - 1)
else: # end_of_pivot <= target_index
return quick_select(end_of_pivot, right)

return quick_select(0, len(nums) - 1)
36 changes: 36 additions & 0 deletions 0215.Kth-Largest-Element-in-an-Array/step2_quick_select_tle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import random

random.seed(42)


class Solution:
def findKthLargest(self, nums: list[int], k: int) -> int:
def partition(pivot_index, left, right):
if left == right:
return nums[left]

nums[pivot_index], nums[right] = nums[right], nums[pivot_index]
end_of_smaller = left
for i in range(left, right):
if nums[i] <= nums[right]:
nums[i], nums[end_of_smaller] = nums[end_of_smaller], nums[i]
end_of_smaller += 1

nums[end_of_smaller], nums[right] = nums[right], nums[end_of_smaller]
return end_of_smaller

def quick_select(left, right, l):
if left == right:
return nums[left]
pivot_index = random.randint(left, right)
partitioned_index = partition(pivot_index, left, right)
if right - partitioned_index == l - 1:
return nums[partitioned_index]
elif right - partitioned_index < l - 1:
return quick_select(
left, partitioned_index - 1, l - (right - partitioned_index + 1)
)
else:
return quick_select(partitioned_index + 1, right, l)

return quick_select(0, len(nums) - 1, k)
43 changes: 43 additions & 0 deletions 0215.Kth-Largest-Element-in-an-Array/step3_quick_select.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from math import e
import random

random.seed(42)


class Solution:
def findKthLargest(self, nums: list[int], k: int) -> int:
def partition(pivot_index, left, right):
pivot = nums[pivot_index]
end_of_smaller = left
end_of_pivot = right
index = left
while index <= end_of_pivot:
if nums[index] < pivot:
nums[index], nums[end_of_smaller] = (
nums[end_of_smaller],
nums[index],
)
index += 1
end_of_smaller += 1
elif nums[index] == pivot:
index += 1
else:
nums[index], nums[end_of_pivot] = nums[end_of_pivot], nums[index]
end_of_pivot -= 1
return end_of_smaller, end_of_pivot

target_index = len(nums) - k

def quick_select(left, right):
if left == right:
return nums[left]
pivot_index = random.randint(left, right)
end_of_smaller, end_of_pivot = partition(pivot_index, left, right)
if target_index < end_of_smaller:
return quick_select(left, end_of_smaller - 1)
elif target_index >= end_of_pivot:
return quick_select(end_of_pivot, right)
else:
return nums[end_of_smaller]

return quick_select(0, len(nums) - 1)