最差情況為線性時間的選擇
阿新 • • 發佈:2019-02-17
這個演算法寫了我好久,在這裡記一下。
演算法的原理是利用中位數來作為劃分元素選擇第M小的元素,中位數需要遞迴自身來求得。演算法的最優,平均,最差時間複雜度都為O(N)。相對於隨機演算法改善了最差時間複雜度。
和快排用了同樣的partition,但是這個演算法所使用的pivot是確定的,即中位數。
程式碼版本為golang 1.8.0。
路徑goWorkSpace/algorithms/worseLinearSelect.go
package algorithms import ( "fmt" ) func WorseLinearSelect(array []int, startIndex, stopIndex, rank int)(selectedValue int) { if startIndex + 5 > stopIndex { insertionSort(array) return array[rank] } midValueArrays := getMidValueArray(array, startIndex, stopIndex) midValue := WorseLinearSelect(midValueArrays, 0, len(midValueArrays), (len(midValueArrays) - 1)/2) devideIndex := partitionWithPiovt(array, midValue) if devideIndex == rank { return array[devideIndex] } else if devideIndex > rank { return WorseLinearSelect(array, startIndex, devideIndex, rank) } else { return WorseLinearSelect(array, devideIndex + 1, stopIndex, rank) } } //sort array by groups and return the mid value array func getMidValueArray(array []int, startIndex, stopIndex int)(midValues []int) { array = array[startIndex : stopIndex] arrayGroups := len(array) / 5 if len(array) % 5 == 0 { midValues = make([]int, arrayGroups) } else { midValues = make([]int, arrayGroups + 1) } //j := 0 for i, j := 0, 0; i < len(array); i += 5 { if i + 5 <= len(array) { b := array[i : i + 5] insertionSort(b) midValues[j] = array[i + 2] } else { insertionSort(array[i : len(array)]) midValues[j] = array[(i + len(array)) / 2] } //(i + 5 <= len(array)) ? (insertionSort(array[i : i + 5])) : (insertionSort(array[i : len(array)])) j ++ } return midValues } func partitionWithPiovt(array []int, pivotValue int)(firstIndex int) { firstIndex = -1 for secondIndex := 0; secondIndex != len(array); secondIndex ++ { if array[secondIndex] < pivotValue { firstIndex ++ swap(&array[firstIndex], &array[secondIndex]) } else if array[secondIndex] == pivotValue { firstIndex ++ swap(&array[firstIndex], &array[secondIndex]) swap(&array[0], &array[firstIndex]) } } swap(&array[0], &array[firstIndex]) return firstIndex } func insertionSort(array []int) { defer func() { if r := recover(); r != nil { fmt.Println(r) } }() for i := 1; i != len(array); i++ { selectValue := array[i] for j := i - 1; j >= 0; j -- { if array[j] > selectValue { swap(&array[j], &array[j + 1]) } else { break } } } } func swap(a *int, b *int) { temp := *b *b = *a *a = temp } func Partition(array []int, pivotValue int) int { return partitionWithPiovt(array, pivotValue) }
main包路徑goWorkSpace/golangTest/test.go,測試程式碼如下:
package main import ( "fmt" "algorithms" "sort" "time" "math/rand" ) func main() { //to new a random slice rand.Seed(time.Now().UnixNano()) arrayLen := rand.Intn(400) + 200 array := make([]int, arrayLen) for index, _ := range array { array[index] = rand.Intn(5 * arrayLen) fmt.Print(",", array[index]) } rank := rand.Intn(arrayLen) fmt.Println("array befor select\n", array) fmt.Println("rank", rank) fmt.Println("array length", arrayLen) //run the select function selectedValue := algorithms.WorseLinearSelect(array[:], 0, len(array), rank) sort.Ints(array[:]) fmt.Println("\nselectedValue by sort", array[rank]) fmt.Println("\nselectedValue", selectedValue) }
後來發現程式碼還有小缺陷,partition方法會影響到切片中不需要重新劃分的部分,但不影響演算法的複雜度,只是多了一點無意義的操作。
程式碼對於所有測試用例都能通過。