数组------最小的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.若数组太短,就直接使用插入排序即可。
<2> 上述方法需要改变原来的数组,若要使得原来数组不变,则可以使用一个大小为k的最大堆,先取数组前k个数入堆并排序,然后从数组的k+1个元素开始,每次与堆顶元素p进行比较,若arr[k+1] >= p,则继续比较下一个元素。若k[k+1] < p,则删除堆顶元素,并将arr[k+1]插入堆中并将堆排序,然后继续比较arr[k+2]…(此方法不会改变原始数组,并且适合处理海量数据。其时间复杂度为O(nlogk)。//实现时,可以用一个长为k的数组来实现一个最大堆)
<3> 使用基于红黑树的multiset来实现。
分析:
<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); } } } }
相关文章推荐
- 【剑指offer】最小的k个数
- 编程算法 - 最小的k个数 代码(C)
- 查找n个数中最小的k个元素
- 最小的k个数(剑指offer)
- 寻找最小的K个数
- 微软面试题系列(五):查找最小的 k 个元素
- 最小的K个数
- 最小的k个数
- 剑指offer——最小的K个数
- 剑指offer:最小的k个数(java)
- 最小的k个数
- 求第k个数,最小的K个数
- 最小的K个数
- 基于快速排序的几种算法变型
- 剑指offer面试题[30]-最小的k个数
- 剑指offer-求数组中最小的k个数
- 【剑指offer】最小的k个数
- 最小的K个数java实现
- 用堆实现最小的K个数的查找:输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
- 剑指Offer:最小的K个数