您的位置:首页 > 其它

数组------最小的k个数

2016-06-20 15:32 435 查看
问题描述: 求数组中的最小k个数。

分析:

<1>使用线性选择算法来快速实现(O(n)),具体来说就是借助快速排序的思想,把数组分为比主元v小的部分s1,和比主元v大的部分s2。

1.若k <= |s1|,则第k小的元素在s1,就递归的对s1进行相同的操作。

2.若k = |s1| + 1,那就表示主元就是第k小的元素,那就结束算法,此时数组的前k个元素就是要找的最小k个元素。

3.若k > |s1| + 1,则第k小的元素在s2中,那就递归的对s2进行相同操作。

4.若数组太短,就直接使用插入排序即可。

void QuickSelect(int arr[], int k, int left, int right)
{
if(arr == NULL || left < 0 || right < left)
{
cout << "something error!" << endl;
exit(1)
}

if(left <= right - 3)
{
int i, j;
int mid = left + (right - left)/2 ;
SortFirstMidLast(arr, left, mid, right);
swap(arr[mid], arr[right - 1]);
int index = right - 1;
int indexValue = arr[index];
i = left + 1;
j = right - 2;
for(;;)
{
while(arr[i] < indexValue)
i += 1;
while(arr[j] > indexValue)
j -= 1;
if(i < j)
swap(arr[i], arr[j]);
else
break;
}
swap(arr[i], arr[index]);
if(k <= i)
QuickSelect(arr, k, left, i - 1);
else if(k > i + 1)
QuickSelect(arr, k, i + 1, right);
}
else
InsertSort(arr, left, right);
}

void SortFirstMidLast(int arr[], int first, int mid, int end)
{
if(arr[first] > arr[mid])
swap(arr[first], arr[mid]);
if(arr[mid] > arr[end])
swap(arr[mid], arr[end]);
if(arr[first] > arr[mid])
swap(arr[first], arr[mid]);
}

void InsertSort(int arr[], int left, int right)
{
for(int un = left + 1; un <= right; un++)
{
int tmp = arr[un];
int lcoal = un;
while(local > 0 && arr[local - 1] > tmp)
{
arr[local] = arr[local - 1];
local--;
}
arr[local] = tmp;
}
}


<2> 上述方法需要改变原来的数组,若要使得原来数组不变,则可以使用一个大小为k的最大堆,先取数组前k个数入堆并排序,然后从数组的k+1个元素开始,每次与堆顶元素p进行比较,若arr[k+1] >= p,则继续比较下一个元素。若k[k+1] < p,则删除堆顶元素,并将arr[k+1]插入堆中并将堆排序,然后继续比较arr[k+2]…(此方法不会改变原始数组,并且适合处理海量数据。其时间复杂度为O(nlogk)。//实现时,可以用一个长为k的数组来实现一个最大堆)

void SortByHeap(int *arr, int *heap, int len)
{
if(len < N)
{
cout << "the array too short!" << endl;
return;
}
for(int i = 0; i < N; i++)  //将数组前N个元素放入堆中
heap[i] = arr[i];

for(int index = N/2; index >= 0; index--)  //堆排序
{
heapRebuild(index, heap, N);
}
for(int i = N; i < len; i++)  //从第N+1个元素开始一一对比
{
if(arr[i] < heap[0])
{
heap[0] = arr[i];
for(int index = N/2; index >= 0; index--)  //堆排序
{
heapRebuild(index, heap, N);
}
}
}
}

void heapRebuild(int rootIndex, int *arr, int count) //创建大顶堆
{
if(rootIndex * 2 + 1 <= count - 1)  //有左孩子结点
{
int largerChildIndex = rootIndex * 2 + 1;
if(largerChildIndex + 1 <= count - 1)  //有右孩子结点
{//获得孩子结点中的较大的那个的下标
int rightIndex = largerChildIndex + 1;
if(arr[rightIndex] > arr[largerChildIndex])
largerChildIndex = rightIndex;
}
if(arr[rootIndex] < arr[largerChildIndex])
{//若孩子结点中较大的那个大于了根节点,那就交换它们,并递归的对每一层进行比较
swap(arr[rootIndex], arr[largerChildIndex]);
heapRebuild(largerChildIndex, arr, count);   //对每一层都进行相同操作
}
}
}


<3> 使用基于红黑树的multiset来实现。

typedef multiset<int, greate<int> > intSet;
typedef multiset<int, greate<int> >::iterator setIterator;

void getLeastNumbers(const vector<int>& data, intSet& leastNumbers, int k){
leastNumbers.clear();
if(k < 1 || data.size() < k)
return ;
vector<int>::const_iterator iter = data.begin();
for(; iter != data.end(); ++iter){
if(leastNumbers.size() < k){
leastNumbers.insert(*iter);
}else{
setIterator iterGreat = leastNumbers.begin();
if(*iter < *(leastNumbers.begin())){
leastNumber.erase(iterGreat);//删除iterGreat位置上的元素
leastNumber.insert(*iter);
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  最小的k个数