diff --git a/0215.Kth-Largest-Element-in-an-Array/memo.md b/0215.Kth-Largest-Element-in-an-Array/memo.md new file mode 100644 index 0000000..3473c80 --- /dev/null +++ b/0215.Kth-Largest-Element-in-an-Array/memo.md @@ -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の条件を<とした diff --git a/0215.Kth-Largest-Element-in-an-Array/step1_heap.py b/0215.Kth-Largest-Element-in-an-Array/step1_heap.py new file mode 100644 index 0000000..35c9806 --- /dev/null +++ b/0215.Kth-Largest-Element-in-an-Array/step1_heap.py @@ -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) diff --git a/0215.Kth-Largest-Element-in-an-Array/step2_quick_select.py b/0215.Kth-Largest-Element-in-an-Array/step2_quick_select.py new file mode 100644 index 0000000..1faa6ac --- /dev/null +++ b/0215.Kth-Largest-Element-in-an-Array/step2_quick_select.py @@ -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) diff --git a/0215.Kth-Largest-Element-in-an-Array/step2_quick_select_revised.py b/0215.Kth-Largest-Element-in-an-Array/step2_quick_select_revised.py new file mode 100644 index 0000000..c85bc5e --- /dev/null +++ b/0215.Kth-Largest-Element-in-an-Array/step2_quick_select_revised.py @@ -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) diff --git a/0215.Kth-Largest-Element-in-an-Array/step2_quick_select_tle.py b/0215.Kth-Largest-Element-in-an-Array/step2_quick_select_tle.py new file mode 100644 index 0000000..4d80760 --- /dev/null +++ b/0215.Kth-Largest-Element-in-an-Array/step2_quick_select_tle.py @@ -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) diff --git a/0215.Kth-Largest-Element-in-an-Array/step3_quick_select.py b/0215.Kth-Largest-Element-in-an-Array/step3_quick_select.py new file mode 100644 index 0000000..a715127 --- /dev/null +++ b/0215.Kth-Largest-Element-in-an-Array/step3_quick_select.py @@ -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)