1. 程式人生 > 實用技巧 >215. 陣列中的第K個最大元素(快速排序)

215. 陣列中的第K個最大元素(快速排序)

215. 陣列中的第K個最大元素

思路:

快速排序

3.8 快速排序

演算法描述

演算法思想: 對給定陣列中的元素進行重新排列, 確定陣列中元素的一個位置q, 得到一個快速排序的劃分

QUICKSORT功能,將陣列A的A[p]到A[q]變為有序(比如從小到大)

QUICKSORT(A, p, r)

  1. if p < r
  2. then q \(\leftarrow\) PARTITION(A, p, r)
  3. ​ QUICKSORT(A, p, q)
  4. ​ QUICKSORT(A, q + 1, r)

PARTITION在A[p]到A[r]以A[p]為界劃分成兩部分A[p]的左邊比A[p]小,A[p]的右邊比A[p]大

PARTITION(A, p, r)

  1. x \(\leftarrow\) A[p], i \(\leftarrow\) p + 1, j \(\leftarrow\) r
  2. while i \(\leq\) j
  3. while A[j] \(\geq\) x and j \(>\) p
  4. j \(\leftarrow\) j -1
  5. while A[i] \(\leq\) x and i \(<\) r
  6. i \(\leftarrow\) i + 1
  7. if i < j then A[i] \(\leftrightarrow\) A[j]
  8. i \(\leftarrow\) i + 1, j \(\leftarrow\)
    j - 1
  9. A[p] $ A[j], return j

(1)快速排序演算法複雜性分析

快速排序演算法的執行時間依賴於:

  • 劃分的平衡與否
  • 劃分的平衡與否依賴於演算法的輸入
  • 如果劃分平衡, 時間複雜性為\(O(n log n)\)
  • 如果劃分不平衡, 時間複雜性為\(O(n^2)\)
最壞時間複雜性

Quicksort 的最壞情況發生在Partition 輸出的兩個區域中, 一個僅包含1 個元素, 另一個包含n - 1個元素的情況;假設上述不平衡的劃分發生在演算法的每一步迭代中, 則

排序過程中每次都出現上述情況就是最壞情況

\[T(n) = \begin{cases} O(1)&\text{,n} \leq {1}\\[2ex] T(n - 1) + O(n)&\text{,n > 1}\\[2ex] \end{cases} \]

每次問題的規模只減小了1,易知時間複雜度為\(O(n^2)\)

最優時間複雜性

設如果Partition 演算法產生兩個大小為n=2 的區域,則

\[T(n) = 2T(\frac{n}{2}) + O(n) \]

根據主定理, 可以得出

\[T(n) = O(n log n) \]

(2)隨機化快速排序演算法
  • 快速排序演算法取決於劃分的對稱性
  • 採用隨機策略進行劃分
  • 演算法每一步在陣列A[p, r] 中隨機選出一個元素作為劃分元素, 可以期望劃分是較對稱的
RANDOMIZED-QUICKSORT演算法

RANDOMIZED-QUICKSORT(A, p, r)

  1. if p < r
  2. then q =RANDOMIZED-PATITION(A, p, r)
  3. ​ RANDOMIZED-QUICKSORT(A, p, q)
  4. ​ RANDOMIZED-QUICKSORT(A, q + 1, r)
RANDOMIZED-PARTITION演算法

RANDOMIZED-PARTITION(A, p, r)

  1. ​ i=Random(p, r)
  2. exchange A[p] \(\leftrightarrow\) A[i]
  3. Return PARTITION(A, p, r)

程式碼如下:

package main

import (
	"fmt"
	"math/rand"
)

func findKthLargest(nums []int, k int) int {
	left := 0
	right := len(nums)-1
	length :=len(nums)-k

	for{
		if left == right{// 迴圈終止條件
			return nums[right]
		}
		p := partition(nums,left,right)
		if p == length{
			return nums[p]
		}else if p<length{
			left = p+1
		}else{
			right = p-1
		}
	}

}
func partition(nums []int,left int,right int)( int){
	// 產生一個隨機數
	randlf := right -left
	rand_get := rand.Intn(randlf)
	rand_fin := left+rand_get

	// 將產生的隨機數對應的陣列中的元素和num[left]互換
	randnum :=rand_fin
	temp_1 :=nums[randnum]
	temp_2 := nums[left]
	nums[left],nums[randnum] = temp_1,temp_2

	x := nums[left]
	i := left+1
	j := right
	for i<=j{
		for nums[j]>=x && j>left{
			j = j-1
		}
		for nums[i]<=x && i<right{
			i = i+1
		}
		if i<j{
			temp := nums[i]
			nums[i],nums[j] = nums[j],temp
			i = i+1
			j = j-1
		}
	}
	t := nums[left]
	nums[left],nums[j] = nums[j],t
	return j
}

func main(){
	nums := []int{1,2,4,5,6,7,8}
	fmt.Println(findKthLargest(nums,2))

}