面试题 10.10. 数字流的秩

68 阅读2分钟

题目:
假设你正在读取一串整数。每隔一段时间,你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。请实现数据结构和算法来支持这些操作,也就是说:

实现 track(int x) 方法,每读入一个数字都会调用该方法;

实现 getRankOfNumber(int x) 方法,返回小于或等于 x 的值的个数。

算法:
方法一:树状数组
因为数组index的限制,实际上要求限制数据范围[0,5000],否则需要将track的数字进行离散化。

type StreamRank struct {
    BIT []int
}


func Constructor() StreamRank {
    return StreamRank{make([]int,50002)}
}


func (this *StreamRank) Track(i int)  {
    i ++
    for i < len(this.BIT) {
        this.BIT[i] = this.BIT[i] + 1
        i = i + lowBit(i)
    }
}


func (this *StreamRank) GetRankOfNumber(i int) int {
    ans := 0
    i ++
    for i > 0 {
        ans = ans + this.BIT[i]
        i = i - lowBit(i)
    }
    return ans
}


func lowBit(x int) int {
    return x & (-x)
}

方法二:插入+二分查找
小知识点get,如何在slice的中间插入元素。利用copy的性质,不创建临时数组,将内存使用率降到更低:

利用append,创建临时数组: 图片.png 利用copy,不创建临时数组: 图片.png

type StreamRank struct {
    BIT []int
}


func Constructor() StreamRank {
    return StreamRank{make([]int,0)}
}


func (this *StreamRank) Track(i int)  {
    index := this.GetRankOfNumber(i)
    // 方法一 创建临时切片
    // this.BIT = append(this.BIT[:index], append([]int{i}, this.BIT[index:]...)...)
    // 方法二 不创建临时切片
    this.BIT = append(this.BIT, 0)
    copy(this.BIT[index + 1:], this.BIT[index:])
    this.BIT[index] = i
}

// 小于等于i的元素个数
func (this *StreamRank) GetRankOfNumber(i int) int {
    left, right := 0, len(this.BIT) - 1
    i = i + 1
    // 找到小于i+1的元素个数
    for left <= right {
        // fmt.Println(left, right)
        mid := (left + right) / 2
        if this.BIT[mid] < i{
            left = mid + 1
        } else if this.BIT[mid] > i{
            right = mid - 1
        } else {
            if mid > 0 && this.BIT[mid - 1] == i {
                right = mid - 1
            } else {
                return mid
            }
        }
    }
    return left
}

方法三:方法二二分的另一种写法

图片.png

type StreamRank struct {
    BIT []int
}


func Constructor() StreamRank {
    return StreamRank{make([]int,0)}
}


func (this *StreamRank) Track(i int)  {
    index := this.GetRankOfNumber(i)
    // 方法一 创建临时切片
    // this.BIT = append(this.BIT[:index], append([]int{i}, this.BIT[index:]...)...)
    // 方法二 不创建临时切片
    this.BIT = append(this.BIT, 0)
    copy(this.BIT[index + 1:], this.BIT[index:])
    this.BIT[index] = i
}

// 小于等于i的元素个数
func (this *StreamRank) GetRankOfNumber(i int) int {
    left, right := 0, len(this.BIT) - 

    //  二分方法二
    for left <= right {
        mid := (left + right) / 2
        if this.BIT[mid] <= i{
            left = mid + 1
        } else {
            right = mid - 1
        } 
    }
    return left
}