この質問は、多くの知識につながる非常に強力です
タイトル
未ソート配列のk番目の最大の要素を検索します。あなたが見つける必要があることに注意してくださいすることは、アレイ内の最大の要素がソートされる最初のkはなく、k個の異なる要素です。
例1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
例2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
注:
あなたは、常に有効なKを想定することができ、配列の1つの≤K≤長さ。
答え
質問は、次の解決策を考えるのが容易になりK大要素の順序を見つけるために尋ねました:
図1は、ソート順序に、Kの最後から二番目を除去しました。クイックソートログ(n)がN・
図2に示すように、小サイズKパイル上部を維持する小さなスタックの最上部は、スタックの最上部には、Kのスタックの上に、順次スタック要素に配列要素よりも大きい大きさです。ヒープを維持する(インサートO(1)、ログ(n)を削除)、総時間の複雑性は・ログ(K)nは、時間複雑性はログ(N)、ここでログ(K)であり、空間の複雑これはO(K)です。
PS:ビッグトップヒープ缶
図3に示すように、高速選択アルゴリズムは、常に次に高速partation処理を行、基準を選択基準がされています。この場合、基準ホーミング(参照配列よりも低いが右側よりも大きい、その左側に配置されています)配列位置をソートし、再N分析探し - 基準位置が正確にkに等しい場合Kは、以下の基準座標位置以上である場合にk個の要素と基準座標との関係は、k番目の配列のより小さい数は、基準となります唯一の再帰左側の部分、それ以外の場合は、再帰の右半分のみ。
ソートアルゴリズムが速い場合は、クイックソートを再帰的の2つの部分があるだろう、時間複雑度は、N・ログ(N)、およびここで、既知のNは見通しのために - 、要素k少量ずつ私たちは、これは平均的な時間計算量はO(n)が低くなり、二つの部分を処理する必要はありません。
このアルゴリズムは、各参照配列は中間位置、O(N)の時間支援度で分割されている最良の場合であり、最悪の場合は、各基準エッジ位置が分割されることで、時間計算量はO(N ^ 2であります)。この問題を解決する線形複雑度はO(n)時間で、基準の選択を最適化するための第四の方法。
4、BFPRT、BFPRTアルゴリズムを基準に問題であり、参照は、配列の中央にそれぞれ位置は、時間計算量はO(N)であり、BFPRTソリューションソリューション迅速な選択との唯一の違いは、基準内にあることを保証するように選択することができます選択に、私たちは、参照処理を選択について話しています。
最初のステップ:私たちはグループに配列ごとに5つの隣接する数字を、数5の後ろの番号は、グループ分けされていない場合でしょう。
ステップ2:各グループ番号については、我々は5の中央値、すべてのグループの中央値は、中央値アレイ(中央値アレイ)を構成することがわかり。
第三段階:私たちは、中央値、中央値、配列、その後、中央値が算出されたベースラインを見つけることができます。
ステップ4:partationプロセスは、この参照によってこれに下記の従来の解決策を行いました。
BFPRTは、時間複雑度はO(N)であり、TOP-Kの問題を追求するように設計されています。
コードにより、次のとおりです。
1、並び替え
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
return sorted(nums)[-k]
2、小さなヒープの最上部
from heapq import *
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
l = [] # 存储堆
for x in nums:
if l and len(l)==k and x>l[0]: # 堆满并且x大于堆顶,pop堆顶,x入堆
heapreplace(l, x)
if not l or len(l)<k: # 堆没满,直接入堆
heappush(l, x)
return l[0]
# 或者直接return nlargest(k, nums)[-1]
3、クイック選択
import random
class Solution:
def findKthLargest(self, nums, k):
def partition(left, right, base):
temp = nums[base]
nums[base], nums[right] = nums[right], nums[base] # 基准和末尾元素互换
max_index = left
for i in range(left, right): # 把所有小于基准的移到左边
if nums[i] < temp:
nums[max_index], nums[i] = nums[i], nums[max_index]
max_index += 1
nums[right], nums[max_index] = nums[max_index], nums[right] # 基准归位
return max_index
def select(left, right, k_smallest):
"""在 nums[left, right] 找第k小的元素"""
if left == right: # 递归终止条件
return nums[left]
pivot_index = random.randint(left, right) # 随机选择基准(比固定选第一个要好)
base_index = partition(left, right, pivot_index) # 选第一个(left)为基准,并归位。
if base_index == k_smallest: # 判断目前已归位的基准,是不是第k_smallest位
return nums[k_smallest]
elif k_smallest < base_index: # go to 左半部分
return select(left, base_index - 1, k_smallest)
else: # go to 右半部分
return select(base_index + 1, right, k_smallest)
return select(0, len(nums) - 1, len(nums) - k) # 第k大,是第n-k小
4、BFPRT
class Solution:
def findKthLargest(self, nums, k):
def getmedian(lis):
"""返回序列lis中位数,在BFPRT中就是求每5个数小组的中位数"""
begin = 0
end = len(lis)-1
sum = begin+end
mid = sum//2 + sum % 2 # 这个地方加上sum%2是为了确保偶数个数时我们求的是中间两个数的后一个
return sorted(lis)[mid]
def BFPRT(nums, left, right):
"""分成每5个数一个小组,并求出每个小组内的中位数"""
num = right-left+1
offset = 0 if num % 5 == 0 else 1 # 最后如果剩余的数不足5个,我们也将其分成一个小组,和前面同等对待
groups = num//5 + offset
median = [] # 中位数数组
for i in range(groups):
begin = left+i*5
end = begin + 4
Median = getmedian(nums[begin:min(end, right)+1])
median.append(Median)
return select(median, 0, groups-1, groups//2) # 求出生成好的median数组的中位数,作为partation函数的划分值
def partition(nums, left, right, base):
"""在 nums[left, right] 将基准base归位"""
temp = nums[base]
nums[base], nums[right] = nums[right], nums[base] # 基准和末尾元素互换
max_index = left
for i in range(left, right): # 把所有小于基准的移到左边
if nums[i] <= temp: # 要等于啊!这里好坑的说.. 否则通不过[3, 3, 3, 3, 4, 3, 3, 3, 3] k = 1
nums[max_index], nums[i] = nums[i], nums[max_index]
max_index += 1
nums[right], nums[max_index] = nums[max_index], nums[right] # 基准归位
return max_index
def select(nums, left, right, k_smallest):
"""在 nums[left, right] 找第k小的元素"""
if left == right: # 递归终止条件
return nums[left]
# pivot_index = random.randint(left, right)
base = BFPRT(nums, left, right)
base_index = partition(nums, left, right, nums.index(base)) # 选base为基准,并归位。
if base_index == k_smallest: # 判断目前已归位的基准,是不是第k_smallest位
return nums[k_smallest]
elif k_smallest < base_index: # 递归左半部分
return select(nums, left, base_index - 1, k_smallest)
else: # 递归右半部分
return select(nums, base_index + 1, right, k_smallest)
return select(nums, 0, len(nums) - 1, len(nums) - k) # 第k大,是第n-k小
PS:ビッグトップヒープを書くと窮乏することができ、奇妙な投稿ではありません。..
from heapq import *
class Solution:
# 用负数入堆建立大顶堆,pop()k次,就是第k大。时间复杂度最坏n·log(n),最好k·log(n),空间复杂度O(n)
def findKthLargest(self, nums: List[int], k: int) -> int:
l = []
for x in nums:
heappush(l, -x) # 平均O(1),最坏log(n)
for x in range(k):
c = heappop(l) # log(n)
return -c