2020-03-02:在无序数组中,如何求第K小的数?

郝思文
• 阅读 931

2020-03-02:在无序数组中,如何求第K小的数?

福哥答案2021-03-02:

1.堆排序。时间复杂度:O(N*lgK)。有代码。
2.单边快排。时间复杂度:O(N)。有代码。
3.bfprt算法。时间复杂度:O(N)。有代码。

代码用golang编写,代码如下:

package main

import (
    "container/heap"
    "fmt"
    "math/rand"
    "sort"
)

func main() {

    //1 2 3 4 5 6 7
    arr := []int{1, 2, 3, 4, 5, 10, 9, 8, 7, 6}
    ret := minKth1(arr, 7)
    fmt.Println("1.堆排序:", ret)

    ret = minKth2(arr, 7)
    fmt.Println("2.单边快排:", ret)

    ret = minKth3(arr, 7)
    fmt.Println("3.bfprt算法:", ret)

}

// 利用大根堆,时间复杂度O(N*logK)
func minKth1(arr []int, k int) int {
    maxHeap := &IntHeap{}
    heap.Init(maxHeap)
    for i := 0; i < k; i++ {
        heap.Push(maxHeap, arr[i])
    }
    for i := k; i < len(arr); i++ {
        heap.Push(maxHeap, arr[i])
        heap.Pop(maxHeap)
        //heap.Push(maxHeap, arr[i])
    }
    return heap.Pop(maxHeap).(int)
}

type IntHeap sort.IntSlice

func (h IntHeap) Len() int           { return len(h) }
func (h IntHeap) Less(i, j int) bool { return !(h[i] < h[j]) }
func (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

//func (h IntHeap) Len() int           { return sort.IntSlice(h).Len() }
//func (h IntHeap) Less(i, j int) bool { return !sort.IntSlice(h).Less(i, j) }
//func (h IntHeap) Swap(i, j int)      { sort.IntSlice(h).Swap(i, j) }

func (h *IntHeap) Push(x interface{}) {
    //fmt.Println("push----")
    // Push and Pop use pointer receivers because they modify the slice's length,
    // not just its contents.
    *h = append(*h, x.(int))
}

func (h *IntHeap) Pop() interface{} {

    old := *h
    n := len(old)
    x := old[n-1]
    *h = old[0 : n-1]
    return x
}

// 改写快排,时间复杂度O(N)
// k >= 1
func minKth2(arr []int, k int) int {
    arrc := make([]int, len(arr))
    copy(arrc, arr)
    return process2(arrc, 0, len(arr)-1, k-1)
}

// arr 第k小的数
// process2(arr, 0, N-1, k-1)
// arr[L..R]  范围上,如果排序的话(不是真的去排序),找位于index的数
// index [L..R]
func process2(arr []int, L int, R int, index int) int {
    if L == R { // L = =R ==INDEX
        return arr[L]
    }
    // 不止一个数  L +  [0, R -L]
    pivot := arr[L+rand.Intn(R-L)]
    rang := partition(arr, L, R, pivot)
    if index >= rang[0] && index <= rang[1] {
        return arr[index]
    } else if index < rang[0] {
        return process2(arr, L, rang[0]-1, index)
    } else {
        return process2(arr, rang[1]+1, R, index)
    }
}

func partition(arr []int, L int, R int, pivot int) []int {
    less := L - 1
    more := R + 1
    cur := L
    for cur < more {
        if arr[cur] < pivot {
            less++
            arr[less], arr[cur] = arr[cur], arr[less]
            cur++
        } else if arr[cur] > pivot {
            more--
            arr[cur], arr[more] = arr[more], arr[cur]
        } else {
            cur++
        }
    }
    return []int{less + 1, more - 1}
}

// 利用bfprt算法,时间复杂度O(N)
func minKth3(arr []int, k int) int {
    arrc := make([]int, len(arr))
    copy(arrc, arr)
    return bfprt(arrc, 0, len(arr)-1, k-1)
}

// arr[L..R]  如果排序的话,位于index位置的数,是什么,返回
func bfprt(arr []int, L int, R int, index int) int {
    if L == R {
        return arr[L]
    }
    // L...R  每五个数一组
    // 每一个小组内部排好序
    // 小组的中位数组成新数组
    // 这个新数组的中位数返回
    pivot := medianOfMedians(arr, L, R)
    rang := partition(arr, L, R, pivot)
    if index >= rang[0] && index <= rang[1] {
        return arr[index]
    } else if index < rang[0] {
        return bfprt(arr, L, rang[0]-1, index)
    } else {
        return bfprt(arr, rang[1]+1, R, index)
    }
}

// arr[L...R]  五个数一组
// 每个小组内部排序
// 每个小组中位数领出来,组成marr
// marr中的中位数,返回
func medianOfMedians(arr []int, L int, R int) int {
    size := R - L + 1
    offset := 0
    if size%5 != 0 {
        offset = 1
    }
    mArr := make([]int, size/5+offset)
    for team := 0; team < len(mArr); team++ {
        teamFirst := L + team*5
        // L ... L + 4
        // L +5 ... L +9
        // L +10....L+14
        mArr[team] = getMedian(arr, teamFirst, getMin(R, teamFirst+4))
    }
    // marr中,找到中位数
    // marr(0, marr.len - 1,  mArr.length / 2 )
    return bfprt(mArr, 0, len(mArr)-1, len(mArr)/2)
}

func getMedian(arr []int, L int, R int) int {
    insertionSort(arr, L, R)
    return arr[(L+R)/2]
}

func insertionSort(arr []int, L int, R int) {
    for i := L + 1; i <= R; i++ {
        for j := i - 1; j >= L && arr[j] > arr[j+1]; j-- {
            arr[j], arr[j+1] = arr[j+1], arr[j]
        }
    }
}
func getMin(a int, b int) int {
    if a < b {
        return a
    } else {
        return b
    }
}

执行结果如下:
2020-03-02:在无序数组中,如何求第K小的数?


左神java代码
评论

点赞
收藏
评论区
推荐文章
复杂度分析:如何分析、统计算法的执行效率和资源消耗
我们都知道,数据结构和算法本身解决的是“快”和“省”的问题,即如何让代码运行得更快,如何让代码更省存储空间。所以,执行效率是算法一个非常重要的考量指标。那如何来衡量你编写的算法代码的执行效率呢?这里就要用到我们今天要讲的内容:时间、空间复杂度分析。
DaLongggggg DaLongggggg
4年前
python-算法训练 区间k大数查询
问题描述给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个。输入格式第一行包含一个数n,表示序列长度。第二行包含n个正整数,表示给定的序列。第三个包含一个正整数m,表示询问个数。接下来m行,每行三个数l,r,K,表示询问序列从左往右第l个数到第r个数中,从大往小第K大的数是哪个。序列元素从1开始标号。输出格式总共输出m行,每行一个数
Stella981 Stella981
3年前
Redis 列表命令记录
新增的常用命令从列表右端插入值(1N个)(rpushlistkeycba)rpushkeyvalue1value2...valueN时间复杂度为O(1N)从列表左端插入值(1N个)(lpushlistkeycba)lpushkeyvalue1
Wesley13 Wesley13
3年前
Java面试总结(排序算法)
1.冒泡排序算法描述:两两比较,大的放后面2.选择排序算法描述:在m元数组中找到最小值的位置,然后将最小值的位置和第n(n0,1,2,....m1)位的值对调,排序k次则m元数组中前k(k<m)位的值已经排序好,m元数组中前k位的值不需要再进行排序,此时需要排序的元素只有mk个3.插入排序算
Wesley13 Wesley13
3年前
BFPRT线性查找算法
介绍:BFPRT算法解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分析,BFPRT可以保证在最坏情况下仍为线性时间复杂度。该算法的思想与快速排序思想相似,当然,为使得算法在最坏情况下,依然能达到o(n)的时间复杂度,五位算法作者做了精妙的处理。时间复杂度O(N)算法步骤
京东云开发者 京东云开发者
9个月前
时间复杂度为 O(n^2) 的排序算法
作者:京东保险王奕龙对于小规模数据,我们可以选用时间复杂度为O(n2)的排序算法。因为时间复杂度并不代表实际代码的执行时间,它省去了低阶、系数和常数,仅代表的增长趋势,所以在小规模数据情况下,O(n2)的排序算法可能会比O(nlogn)的排序算法执行效率高
深度学习 深度学习
1星期前
NOIP 2005 普及组 洛谷1048题 解题思路和步骤 C++实现带注释
一、解题思路:‌问题分析‌:给定背包容量T和M个物品(草药),每个物品有采摘时间t‌:若当前物品时间超过剩余时间:dp‌:使用滚动将空间复杂度从O(NV)降为O(V),需逆序遍历时间。二、代码实现:Cincludeusingnamespacestd;i
菜园前端 菜园前端
2年前
什么是时间复杂度?
原文链接:什么是时间复杂度?定性描述该算法的运行时间,一个函数、用大O表示,例如O(1)、O(n)、O(logN)...常见的时间复杂度量级常数阶O(1)对数阶O(logN)线性阶O(n)线性对数阶O(nlogN)平方阶O(n²)立方阶O(n)K次方阶O(
菜园前端 菜园前端
2年前
什么是空间复杂度?
原文链接:什么是空间复杂度?算法在运行过程中临时占用存储空间大小的度量,和时间复杂度表示一样,一个函数,用大O表示,例如O(1)、O(n)、O(^2)...基础案例O(1)这段代码因为只声明了单个变量,单个变量所占用的内存永远是1。javascriptle
菜园前端 菜园前端
2年前
什么是顺序搜索?
原文链接:什么是顺序搜索?顺序搜索是一种比较低效的搜索算法,但是实现起来相对简单。主要步骤如下:1.遍历数组2.找到跟目标值相等的元素,就返回它的下标3.遍历结束后,如果没有搜索到目标值,则返回1基础案例时间复杂度:O(n)空间复杂度:O(1)javasc
时间复杂度为 O (n^2) 的排序算法 | 京东物流技术团队
对于小规模数据,我们可以选用时间复杂度为O(n2)的排序算法。因为时间复杂度并不代表实际代码的执行时间,它省去了低阶、系数和常数,仅代表的增长趋势,所以在小规模数据情况下,O(n2)的排序算法可能会比O(nlogn)的排序算法执行效率高。不过随着数据规模增