标签:
求一维数组中最小的K个数。
先把数组从小到大排序,取前K个数。时间复杂度为O(nlogn)。如果数组过大,机器内存无法同时容纳整个数组,则需要使用外部排序。
以下程序使用的是标准库algorithm中的排序方法----std::sort,代码如下:
//排序法,算法复杂度O(nlogn) void GetTopK_Sort( int nArray[], int nCount, int k ) { //标准库中的排序算法 sort( nArray, nArray+nCount ); if ( K_SIZE <= 20 ) { for( int i = 0; i < k; ++i ) cout << nArray[i] << " "; cout << endl << "----------------------------------------" << endl; } }
初始化一个大顶堆,然后扫描一遍数组,往堆里插入数据,如果堆的元素个数已经到达K,那么新元素就要和堆顶比较,如果小于堆顶,则移除堆顶,插入新元素;最终我们得到k个最小的元素。时间复杂度为O(nlogk)。
代码实现见后面
使用快速排序分区函数:选择一个数,把数组的数分在两部分,把比选中的数小的或者相等的数移到数组的左边,把比选中的数大的移到数组的右边,返回分区后选中数的下标。
分区结束后,如果返回的下标为K-1,那么左边k个元素即我们所找;如果返回的下标大于K-1,那么我们需要在右半部分找;如果返回的下标小于K-1,则在左半部分继续找。这样重复操作,直到找到分区函数返回的下标为K-1。由于我们并没有实现真正的快速排序,每次只会在两部分中的一部分中查找,而且划分的基准都是随机的,所以算法的平均时间复杂度为O(n)(本人还没太理解这个复杂度是怎么计算得到的,嘿嘿)。
代码说明:由于快速排序在最坏的情况下,时间复杂度为O(n^2),比如找最小元素时,总是在最大的元素处划分,或者所有元素都相同。所以代码中对分区函数作了一个优化,在一次划分中,记录是否有元素交换,如果没有,说明这部分已经有序,无需在操作下去了。
代码实现见后面
代码:
///////////////////////////// //求一维数组中最小的K个数 #include <iostream> #include <algorithm> #include <ctime> #include <cstdlib> using namespace std; #define MAX_TEST 10000/*0000*/ //数据数量 #define K_SIZE 20 //k值 #define SRAND_TEST 7 //随机种子 //排序法,算法复杂度O(nlogn) void GetTopK_Sort( int nArray[], int nCount, int k ) { //标准库中的排序算法 sort( nArray, nArray+nCount ); if ( K_SIZE <= 20 ) { for( int i = 0; i < k; ++i ) cout << nArray[i] << " "; cout << endl << "----------------------------------------" << endl; } } ////////////////////////////////////////////////////// //使用堆 O(nlogk) //数组nHeap的0号单元存放堆的大小 //从底部往上调整成大顶堆 void AdjustHeapFromDown( int nHeap[], int i ) { int j = i / 2; while( j > 0 ) { if ( nHeap[j] < nHeap[i] ) { int temp = nHeap[j]; nHeap[j] = nHeap[i]; nHeap[i] = temp; j = j / 2; } else break;//调整结束(因为是在大顶堆的前提下插入元素) } } //从上面往下调整成大顶堆 void AdjustHeapFromUp( int nHeap[], int i ) { int s = i; while ( s <= nHeap[0]/2 )//堆中最后一个非终端叶子结点为nHeap[0]/2 { int m = 2*s; if ( (2*s+1) <= nHeap[0] && nHeap[2*s+1] > nHeap[m] ) m = 2*s+1;//右孩子更大 if ( nHeap[s] < nHeap[m] ) { int temp = nHeap[s]; nHeap[s] = nHeap[m]; nHeap[m] = temp; //转到下一层 s = m; } else { break; } } } void InsertHeap( int nHeap[], int val, int k ) { if ( nHeap[0] < k ) { nHeap[++nHeap[0]] = val; AdjustHeapFromDown( nHeap, nHeap[0] ); } else { //堆个数已达到k,需要和堆顶比较,如果小于堆顶,则移除堆顶,插入新元素 if ( nHeap[1] > val ) { nHeap[1] = val; AdjustHeapFromUp( nHeap, 1 ); } } } void GetTopK_Heap( int nArray[], int nCount, int k ) { //创建大小为k+1的堆,0号单元放堆的大小 int* pHeap = new int[k+1]; pHeap[0] = 0; for( int i = 0; i < nCount; ++i ) { InsertHeap( pHeap, nArray[i], k ); } //输出结果 if ( K_SIZE <= 20 ) { for( int i = 1; i <= k; ++i ) cout << pHeap[i] << " "; cout << endl << "----------------------------------------" << endl; } if ( NULL != pHeap ) { delete pHeap; pHeap = NULL; } } /////////////////////////////////// //使用快速排序思想 // int Partition( int* pArray, int low, int high ) // { // int temp = pArray[low]; // while( low < high ) // { // while( low < high && pArray[high] >= temp ) --high; // pArray[low] = pArray[high]; // // while( low < high && pArray[low] <= temp ) ++low; // pArray[high] = pArray[low]; // } // pArray[low] = temp; // // return low; // } //int PartitionRandom( int* pArray, int low, int high ) int PartitionRandom( int* pArray, int low, int high, bool& bLeftSwap, bool& bRightSwap ) { //随机产生基准元素 int i = low + rand() % (high-low); int temp = pArray[i]; pArray[i] = pArray[low]; pArray[low] = temp; temp = pArray[low]; bLeftSwap = bRightSwap = false; while( low < high ) { while( low < high && pArray[high] >= temp ) { --high;//同时进行起泡操作。。。 if ( !bRightSwap &&/* pArray[high] >= temp &&*/ high != low && pArray[high] > pArray[high+1] ) { bRightSwap = true; int temp2 = pArray[high]; pArray[high] = pArray[high+1]; pArray[high+1] = temp2; } } //pArray[low] = pArray[high]; if ( pArray[low] != pArray[high] ) { pArray[low] = pArray[high]; bRightSwap = true; } while( low < high && pArray[low] <= temp ) { ++low;//同时进行起泡操作。。。 if ( !bLeftSwap &&/* pArray[low] <= temp && */low != high && pArray[low] < pArray[low-1] ) { bLeftSwap = true; int temp2 = pArray[low]; pArray[low] = pArray[low-1]; pArray[low-1] = temp2; } } //pArray[high] = pArray[low]; if ( pArray[high] != pArray[low] ) { pArray[high] = pArray[low]; bLeftSwap = true; } } pArray[low] = temp; return low; } void QSortSelectK( int nArray[], int low, int high, int k ) { if ( low < high ) { bool bLeftSwap = false; bool bRightSwap = false; int pivot = PartitionRandom( nArray, low, high, bLeftSwap, bRightSwap ); if ( pivot == k-1 ) return; else if ( pivot > k-1 ) { if ( !bLeftSwap ) return; QSortSelectK( nArray, low, pivot-1, k ); } else { if ( !bRightSwap ) return; QSortSelectK( nArray, pivot+1, high, k ); } } } void GetTopK_QSort( int nArray[], int nCount, int k ) { // //int pivot = Partition( nArray, 0, nCount - 1 ); // int pivot = PartitionRandom( nArray, 0, nCount - 1 ); // while( pivot != k - 1 ) // { // if ( pivot > k - 1 ) // { // //pivot = Partition( nArray, 0, pivot - 1 ); // pivot = PartitionRandom( nArray, 0, pivot - 1 ); // } // else // { // //pivot = Partition( nArray, pivot + 1, nCount - 1 ); // pivot = PartitionRandom( nArray, pivot + 1, nCount - 1 ); // } // } QSortSelectK( nArray, 0, nCount-1, k ); //结果输出 if ( K_SIZE <= 20 ) { for( int i = 0; i < k; ++i ) cout << nArray[i] << " "; cout << endl << "----------------------------------------" << endl; } } //生成测试数据 void RandomTestData( int* pArray, int nCount ) { for( int i = 0; i < nCount; ++i ) pArray[i] = rand()%100000000; //for( int i = 0; i < nCount; ++i ) pArray[i] = nCount-i; //for( int i = 0; i < nCount; ++i ) pArray[i] = 2; //for( int i = 0; i < nCount; ++i ) pArray[i] = i; } void PrintArray( int nArray[], int nCount ) { for( int i = 0; i < nCount; ++i ) { cout << nArray[i] << " "; } cout << endl; } int main() { clock_t start, finish; double duration = 0.0; // int nArray1[] = { 1, 2, 3, 4 }; // QuickSort( nArray1, 0, _countof(nArray1)-1 ); // PrintArray( nArray1, _countof(nArray1) ); // GetTopK_Sort( nArray1, _countof(nArray1), 10 ); // // int nArray2[] = { 2, 1, 56, 34, 23, 56, 12, 10, 6, 4, 19, 22, 89, 3, 5, 7 }; // GetTopK_QSort( nArray2, _countof(nArray2), 5 ); // GetTopK_Heap( nArray2, _countof(nArray2), 10 ); /////////////////////////////////////////// //排序法 //srand( (unsigned)time(NULL) ); srand( SRAND_TEST ); int* pArray = new int[MAX_TEST]; RandomTestData( pArray, MAX_TEST ); ////////////////////////// start = clock(); ///////////////////////// GetTopK_Sort( pArray, MAX_TEST, K_SIZE ); /////////////////// finish = clock(); duration = (double)(finish-start); cout << "使用排序法----时间消耗:" << duration << "ms" << endl << endl; /////////////////// //////////////////////////////////////// //使用堆 srand( SRAND_TEST ); RandomTestData( pArray, MAX_TEST ); ////////////////////////// start = clock(); ///////////////////////// GetTopK_Heap( pArray, MAX_TEST, K_SIZE ); /////////////////// finish = clock(); duration = (double)(finish-start); cout << "使用堆----时间消耗:" << duration << "ms" << endl << endl; /////////////////// /////////////////////////////////// //使用快速排序思想 srand( SRAND_TEST ); RandomTestData( pArray, MAX_TEST ); ////////////////////////// start = clock(); ///////////////////////// //srand( (unsigned)time(NULL) ); //int nArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; //GetTopK_QSort( nArray, _countof(nArray), K_SIZE ); GetTopK_QSort( pArray, MAX_TEST, K_SIZE ); /////////////////// finish = clock(); duration = (double)(finish-start); cout << "使用快排分区----时间消耗:" << duration << "ms" << endl << endl; /////////////////// if ( NULL != pArray ) { delete pArray; pArray = NULL; } return 0; }
运行性能对比:
1、以下是从100000000条有序数据中找最小的2000个数的运行结果
2、以下是从1000000条有序数据中找最小的20个数的运行结果
3、以下是为10000条随机数据中找最小的20个数的运行结果
4、以下是在1亿条随机数据中找最小的20个数的运行结果
系列文章说明:
1.本系列文章[算法练习],仅仅是本人学习过程的一个记录以及自我激励,没有什么说教的意思。如果能给读者带来些许知识及感悟,那是我的荣幸。
2.本系列文章是本人学习陈东锋老师《进军硅谷,程序员面试揭秘》一书而写的一些心得体会,文章大多数观点均来自此书,特此说明!
3.文章之中,难免有诸多的错误与不足,欢迎读者批评指正,谢谢.
作者:山丘儿
转载请标明出处,谢谢。原文地址:http://blog.csdn.net/s634772208/article/details/46695961
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://blog.csdn.net/s634772208/article/details/46695961