實現原理
快速排序思想:如果要排數組p到r之間的一組數據,選擇p到r之間任意一個一個數據作為pivot(分區點,這里選擇的是s[r]作為pivot)。遍歷p到r之間的數據,將小於pivot的數據放在左邊,其他的放右邊。經過這一步驟后數據p到r被分成了三份,前面p~q-1的數據小於pivot,q+1~r的數據大於pivot。接着遞歸分治實現剩下子分區的排序。
如下圖所示是一次分區的結果,以數組的最后一個節點4作為pivot:
代碼實現(golang)
1 package main 2 3 import "fmt" 4 5 func quicklySort(s []int) []int { 6 quickylySortImpl(s, 0, len(s)-1) 7 return s 8 } 9 10 func quickylySortImpl(s []int, p, r int) { 11 if p >= r { 12 return 13 } 14 q := partition(s, p, r) 15 quickylySortImpl(s, p, q-1) 16 quickylySortImpl(s, q+1, r) 17 } 18 19 func partition(s []int, p, r int) int { 20 pivot := s[r] 21 i := p 22 for j := p; j <= r; j++ { 23 if s[j] < pivot { 24 s[i], s[j] = s[j], s[i] 25 i = i + 1 26 } 27 } 28 s[i], s[r] = s[r], s[i] 29 return i 30 } 31 32 func main() { 33 fmt.Println(quicklySort([]int{7, 10, 2, 3, 6, 1, 4})) 34 }
程序運行輸出 1,2,3,4,6,7,10
時間復雜度O(nlogn), 空間復雜度O(1)
擴展應用
O(n)時間復雜度求數組的第K大數據
1 package main 2 3 import "fmt" 4 5 func findKthLargest(nums []int, k int) int { 6 q := findKthLargestImpl(nums, 0, len(nums)-1, k) 7 return nums[q] 8 } 9 10 func findKthLargestImpl(nums []int, p, r, k int) int { 11 if p >= r { 12 return p 13 } 14 q := partition(nums, p, r) 15 fmt.Println("pq=", q) 16 if (q + 1) == k { 17 fmt.Println("q=", q) 18 return q 19 } else if (q + 1) < k { 20 q = findKthLargestImpl(nums, q+1, r, k) 21 } else { 22 q = findKthLargestImpl(nums, p, q-1, k) 23 } 24 return q 25 } 26 27 func partition(s []int, p, r int) int { 28 pivot := s[r] 29 i := p 30 for j := p; j <= r; j++ { 31 if s[j] > pivot { 32 s[i], s[j] = s[j], s[i] 33 i = i + 1 34 } 35 } 36 s[i], s[r] = s[r], s[i] 37 return i 38 } 39 func main() { 40 fmt.Println(findKthLargest([]int{3, 2, 1, 5, 6, 4}, 2)) 41 }
程序運行輸出 5
需要特別注意的是程序32行由大於號變成了小於號,即把比pivot大的數據放左邊,讓數據從大到小,從而符合第K大的判斷要求。如果求第K小則不用動。
第一遍運行數組的排序是 5, 6,4,3,2,1 q = 2即元素4,進入了代碼第24行的邏輯 findKthLargestImpl(nums, 0, 1, 2)
第二遍運行數組的排序是 6,5,4,3,2,1 q = 0即元素6,進入了代碼第20行的邏輯 findKthLargestImpl(nums, 1, 1, 2)
第三遍運行直接返回1,即元素5