这周我们算法课讲了分治策略,讲到了如何运用分治策略来解决selection problem,于是我课后在leetcode找了一道寻找第k大的数的算法题,以加深自己的理解。这里是课上讲解的求第k小的大致思路,在解决第k大问题的时候我也是按照差不多的思路进行实现的
通过在数集中取一个随机的数v,通过它将数组划分为3个部分,分别是小于它,等于它和大于它的三个部分,然后通过k值,判断所求的数会在哪个集合,因而达到将集合快速缩小的目的。另外,通过保证每次v在讨论的数集中处在偏向中间的位置,可以使得算法复杂度最坏情况也是O(n)。
以下是leetcode中寻找数组中第k大的数的算法题及我的解答。由于每次将数组划分为3部分来进行讨论,情况比较复杂,我也没想到更简洁的实现方法,暂时先贴出自己最初版本的代码,其速度超过了61.8%的用户,还算可以接受吧。
百行代码预警(如果想到了更简洁的实现方法,我会另外写一篇博客)
#include <time.h>
#include <stdlib.h>
class Solution {
public:
void swap(vector<int>::iterator iter, int i, int j) {
int temp = iter[i];
iter[i] = iter[j];
iter[j] = temp;
}
int getIndex(vector<int>::iterator iter, int a, int b) {
if (a == b) {
return a;
} else {
if (b - a > 2) {
float sl = 0, sr = 0;
int index = (rand() % (b - a)) + a;
for (int i = a; i <= b; i++) {
if (iter[i] <= iter[index]) {
sl = sl + 1;
}
if (iter[i] >= iter[index]) {
sr = sr + 1;
}
}
if ((sl / (b - a + 1) >= 0.25)&&(sr / (b - a + 1) >= 0.25)) {
return index;
} else return getIndex(iter, a, b);
} else return (rand() % (b - a)) + a;
}
}
int findByRecursion(vector<int>::iterator iter, int k, int left, int right) {
int i = left;
int j = right;
int randomIndex = getIndex(iter, left, right);
int v = iter[randomIndex];
int x = randomIndex;
int y = x;
int sr = 0, sl = 0, sv = 0;
while (!((i >= x)&&(j <= y))) {
bool flag = false;
if (i < x) {
while (iter[i] < v) {
i++;
}
}
if (j > y) {
while (iter[j] > v) {
j--;
}
}
if ((i < x)&&(iter[i] == v)) {
swap(iter, i, --x);
flag = true;
}
if ((j > y)&&(iter[j] == v)) {
swap(iter, j, ++y);
flag = true;
}
if (!flag) {
if ((i >= x)&&(j > y)) {
if (y == j - 1) {
swap(iter, x, j);
x++;
y++;
} else {
swap(iter, x, ++y);
swap(iter, j, x++);
}
} else if ((i < x)&&(j <= y)) {
if (x == i + 1) {
swap(iter, y, i);
x--;
y--;
} else {
swap(iter, y, --x);
swap(iter, i, y--);
}
} else if ((iter[i] > v)&&(iter[j] < v)) {
swap(iter, i, j);
i++;
j--;
}
}
}
sl = i - left;
sv = y - x + 1;
sr = right - j;
if (k <= sr) {
return findByRecursion(iter, k, y + 1, right);
} else if (k <= sv + sr) {
return v;
} else {
return findByRecursion(iter, k - sr - sv, left, x - 1);
}
}
int findKthLargest(vector<int>& nums, int k) {
vector<int>::iterator iter = nums.begin();
srand (time(NULL));
return findByRecursion(iter, k, 0, nums.size() - 1);
}
};