盗用下别人的标题,把我的顶上去,别人说的虽然有道理,但是缺乏实践。决定自己写个例子,实测结果如下:
总数1万个取最大100,快排略快,最小堆偶尔快。
总数10万个取最大100,最小堆略快,快排偶尔快。
总数100万个取最大100,最小堆完胜,快排没戏,而且最小堆大概快了2倍。
总数1000万个取最大100,最小堆完虐,快排没戏,而且最小堆快了大概2倍。
N个随机数取最大的K个和第K个其实是一个问题,区别在于K的位置。
当K很小,N很大时,用小顶堆。当K接近N的一半时,用快排。
结论:最小堆比快排优秀。
原因:
1.速度确实快。
2.最小堆不需要打乱原数据顺序,而快排会打乱。(并不是快的原因,而是最小堆的优点)
3.如果内存有限,无法加载所有数据,则最小堆快。
4.快排需要把所有数加到缓存里,而数据太大,查询过程中导致高速缓存置换,反而更慢了
1 #include <vector> 2 #include <iostream> 3 #include <time.h> 4 #include <random> 5 using namespace std; 6 7 //改良版部分区域快排 8 int Partition(vector<int>& arr, int low, int high) 9 { 10 int pivokey = arr[low];//因为arr已经是随机数了,不需要再有分区算法,反而会慢 11 while (low < high) 12 { 13 while (low < high && arr[high] <= pivokey) --high; 14 arr[low] = arr[high]; 15 while (low < high && arr[low] >= pivokey) ++low; 16 arr[high] = arr[low]; 17 } 18 arr[low] = pivokey; 19 return low; 20 } 21 int TopK_Qsort(vector<int>& arr, int start, int end, int k) 22 { 23 int index = 0; 24 if (start < end) 25 { 26 index = Partition(arr, start, end); 27 if (index < k)//还要从index的右边找k-index个数 28 { 29 index = TopK_Qsort(arr, index + 1, end, k); 30 } 31 else if (index > k)//k个数都在Index的左边 32 { 33 index = TopK_Qsort(arr, start, index - 1, k); 34 } 35 } 36 return index; 37 } 38 39 //小顶堆化 40 void HeapAdjust(vector<int>& nums, int pos) 41 { 42 for (int i = 2 * pos + 1; i < nums.size(); i = 2 * i + 1) 43 { 44 if (i<nums.size() - 1 && nums[i]>nums[i + 1]) 45 i++; 46 if (nums[i] >= nums[pos]) 47 break; 48 swap(nums[i], nums[pos]); 49 pos = i; 50 } 51 } 52 void TopK_Heap(vector<int>& arr, int k) 53 { 54 if (arr.size() <= k) 55 return; 56 vector<int> box; 57 box.resize(k); 58 for (int i = 0; i < k; i++) 59 box[i] = arr[i]; 60 for (int i = box.size() / 2; i >= 0; i--) 61 HeapAdjust(box, i); 62 for (int i = k; i < arr.size(); i++) 63 { 64 if (arr[i] > box[0]) 65 { 66 box[0] = arr[i]; 67 HeapAdjust(box, 0); 68 } 69 } 70 } 71 72 int main() 73 { 74 int nCounterA = 0; //代表小顶堆胜出次数 75 int nCounterB = 0; //代表快排胜出次数 76 int nCounterC = 0; //平手 77 for (int t = 750; t<= 770;++t)//t是随机种子,保证每次待排序列表里是新的随机数 78 { 79 default_random_engine e; 80 e.seed(t); 81 82 int nTotal = 10000; //总共有多少个数 83 int nTopK = 100; //前TOPK个数 84 //-------------------填充随机数----------------------- 85 vector<int> vecQsortResource; 86 vecQsortResource.reserve(nTotal); 87 vector<int> vecHeapResource; 88 vecHeapResource.reserve(nTotal); 89 for (int i = nTotal; i > 0; i--) 90 { 91 vecQsortResource.push_back(e() % nTotal); 92 } 93 vecHeapResource = vecQsortResource; 94 95 //-------------------两种算法开始计算----------------------- 96 clock_t start, finish; 97 start = clock(); 98 TopK_Heap(vecHeapResource, nTopK); 99 finish = clock(); 100 double nRes1 = (double)(finish - start); 101 //cout << " 小顶堆的运行时间为" << nRes1 / CLOCKS_PER_SEC << "秒!" << endl; 102 103 start = clock(); 104 TopK_Qsort(vecQsortResource, 0, nTotal - 1, nTopK - 1); 105 finish = clock(); 106 double nRes2 = (double)(finish - start); 107 //cout << " 快排的运行时间为" << nRes2 / CLOCKS_PER_SEC << "秒!" << endl; 108 109 if (nRes1 < nRes2) 110 { 111 nCounterA++; 112 } 113 else if (nRes1 > nRes2) 114 { 115 nCounterB++; 116 } 117 else 118 { 119 nCounterC++; 120 } 121 } 122 123 cout << nCounterA << endl; //代表小顶堆胜出次数 124 cout << nCounterB << endl; //代表快排胜出次数 125 cout << nCounterC << endl; //平手 126 return 0; 127 }