topK问题的几种解法及C++实现

topK问题是经典的算法问题,其大意是从一个序列中找出最小(大)的k个数,面对这个问题最简单的方法当然是先排序后取前k个数,但这样有些浪费时间,比较经典的方法是借助快排和堆排的思想。
注:为了方便讨论,以下均选择找出最小k个数。

解法1:快速选择
借助快排是思想,进行局部的排序。

void quickselect(vector<int>& input, int left, int right, int k) {
    if (left >= right) {
        return;
    }
    int num = input[left];
    int i = left;
    int j = right + 1;
    while (i <= j) {
        while (i < right && input[++i] < num) {}
        while (j > 0 && input[--j] > num) {}
        if (i <= j) {
            swap(input[i], input[j]);
        }
    }
    swap(input[left], input[j]);
    if (k == j + 1) {
        return;
    }
    else if (k < j + 1) {
        quickselect(input, left, j - 1, k);
    }
    else {
        quickselect(input, left, j - 1, k);
        quickselect(input, i, right, k);
    }
}

解法2:借助小顶堆
对数组建堆后执行k次提取最小值操作就行

//下滤
void PercDown(vector<int>& input, int i, int N) {
    int Child;
    while (2 * i + 1 < N) {
        Child = 2 * i + 1;
        if (Child + 1 < N && input[Child + 1] < input[Child])
            ++Child;
        if (input[i] > input[Child])
            swap(input[i], input[Child]);
        else break;
        i = Child;
    }
}

//建堆(小顶堆)
void Heapsort(vector<int>& input) {
    int N = input.size();
    for (int i = N / 2 - 1; i >= 0; --i) {
        PercDown(input, i, N);
    }
}

//提取最小值
int DeleteMin(vector<int>& input) {
    int N = input.size();
    int MinElement = input[0];
    int LastElement = input[N-1];
    int i = 0, Child;
    //下滤
    while (2 * i + 1 < N) {
        Child = 2 * i + 1;
        if (Child + 1 < N && input[Child + 1] < input[Child])
            ++Child;
        if (LastElement > input[Child])
            input[i] = input[Child];
        else break;
        i = Child;
    }
    input[i] = LastElement;
    input.pop_back();
    return MinElement;
}

vector<int> topK(vector<int>& input, int k) {
    Heapsort(input);
    vector<int> res;
    for (int i = 0; i < k; ++i) {
        res.push_back(DeleteMin(input));
    }
    return res;
}

解法3:借助大顶堆
维持一个大小为k的大顶堆,当堆中元素不足k时直接插入元素,当堆中元素个数为k时,遍历到的元素若小于堆顶元素则替换堆顶元素再下滤,最后堆中的即为topk元素。

//下滤
void PercDown(vector<int>& input, int i, int N) {
    int Child;
    while (2 * i + 1 < N) {
        Child = 2 * i + 1;
        if (Child + 1 < N && input[Child + 1] > input[Child])
            ++Child;
        if (input[i] < input[Child])
            swap(input[i], input[Child]);
        else break;
        i = Child;
    }
}

//建堆(大顶堆)
void Heapsort(vector<int>& input) {
    int N = input.size();
    for (int i = N / 2 - 1; i >= 0; --i) {
        PercDown(input, i, N);
    }
}

//插入
void Insert(vector<int>& res, int Element) {
    res.push_back(Element);
    int N = res.size();
    if (N <= 1) return;
    for (int i = N - 1; i > 0; i = (i - 1) / 2) {
        if (res[(i - 1) / 2] < Element) {
            swap(res[(i - 1) / 2], res[i]);
        }
    }
}

vector<int> topK(vector<int>& input, int k) {
    vector<int> res;
    for (int i = 0; i < input.size(); ++i) {
        if (res.size() < k) {
            Insert(res, input[i]);
        }
        else {
            if (input[i] < res[0]) {
                res[0] = input[i];
                PercDown(res, 0, k);
            }
        }
    }
    return res;
}

你可能感兴趣的:(数据结构与算法,算法)