假如有一个数组(无序),让你找出里面前k个小的元素,设计一个算法
思路1:
快速排序的思想,找出第k位置的元素,在此元素左边都比他小,输出即可,时间复杂度为O(n)
思路2:
查找前k小,构建最大值堆,先将k个元素构建一个最大值堆,然后再遍历剩余元素,若剩余元素比堆顶小,让堆顶等于这个小的元素,再调整一下位置,这样遍历结束,前k个最小的元素都在堆中了。时间复杂度O(n)
下面将两个思路整合在一起了
class Solution {
private:
vector<int> heap;
int leftChild(int pos)
{
return 2 * pos + 1;
}
int rightChild(int pos)
{
return 2 * pos + 2;
}
bool isLeaf(int pos)
{
return (pos >= heap.size() / 2) && (pos < heap.size());
}
void siftDown(int pos)
{
while (!isLeaf(pos))
{
int left = leftChild(pos);
int right = rightChild(pos);
if (right<heap.size() && heap[right]<heap[left])
right = left;
if (heap[pos] > heap[right])
return;
swap(heap[pos], heap[right]);
pos = right;
}
}
void buildHeap(int size)
{
for (int i = size / 2 - 1; i >= 0; --i)
siftDown(i);
}
int randomInRange(vector<int>& nums, int start, int end)
{
if (start == end)
return nums[start];
srand((unsigned)time(NULL));
if (start > end)
return rand() % (start - end + 1) + end;
else
return rand() % (end - start + 1) + start;
}
int partition(vector<int>&nums, int start, int end)
{
if (start == end)
return nums[start];
int index = randomInRange(nums, start, end);
swap(nums[index], nums[end]);
int small = start - 1;
for (int i = start; i < end; ++i)
{
if (nums[i] < nums[end])
{
small++;
if (small != i)
swap(nums[small], nums[i]);
}
}
small++;
swap(nums[small], nums[end]);
return small;
}
public:
void findKthLargest(vector<int>& nums, int k)
{
int start = 0, end = nums.size() - 1;
int index = partition(nums, start, end);
while (index != k - 1)
{
if (index > k - 1)
end = index - 1;
else
start = index + 1;
index = partition(nums, start, end);
}
for (int i = 0; i <= index; ++i)
cout << nums[i] << " ";
}
void heapFindK(vector<int>& nums, int k)
{
for (int i = 0; i < k; ++i)
heap.push_back(nums[i]);
buildHeap(heap.size());
for (int i = k; i < nums.size(); ++i)
{
if (heap[0] > nums[i])
{
heap[0] = nums[i];
siftDown(0);
}
}
for (int i = 0; i < k; ++i)
cout << heap[i] << " ";
}
};