堆處理海量資料----求前k個最小的數--時間複雜度(n * log k)
通過閱讀July的書籍,發現裡面求前k個最小數有很多方法。但在面對處理海量資料處理的時候,不能 把全部資料都放在電腦記憶體中。這時用堆來處理,並把資料放在外存中,通過讀取檔案的方式來讀取。感覺該方法十分巧妙,時間複雜度(n*log k)。程式碼如下:
#include<iostream> #include<assert.h> using namespace std; void MaxHeap(int heap[],int i,int len); void BuildHeap(int heap[],int len) { if(heap == NULL) return; int index = len/2; for(int i = index;i >= 1; i--) MaxHeap(heap,i,len); } void MaxHeap(int heap[],int i,int len) { int largeIndex = -1; int left = i * 2; int right = i *2 + 1; if(left<=len && heap[left]>heap[i]) largeIndex = left; else largeIndex = i; if(right<=len && heap[right]>heap[largeIndex]) largeIndex = right; if(largeIndex != i) { swap(heap[i],heap[largeIndex]); MaxHeap(heap,largeIndex,len); } } int main() { int k; cout<<"求最小的k個數,請輸入k的值:"; cin>>k; int *heap = new int [k+1]; FILE *fp = fopen("data.txt","r"); assert(fp); for(int i = 1;i<=k;i++) { fscanf(fp,"%d",&heap[i]); } BuildHeap(heap,k); int newData; while(fscanf(fp,"%d",&newData)!=EOF) { if(newData<heap[1]) { heap[1]=newData; MaxHeap(heap,1,k); } } for(int j=1;j<=k;j++) cout<<heap[j]<<" "; cout<<endl; fclose(fp); return 0; }
關於assert函式用法
assert巨集的原型定義在<assert.h>中,其作用是如果它的條件返回錯誤,則終止程式執行,原型定義:
#include <assert.h>void assert( int expression );
assert的作用是現計算表示式 expression ,如果其值為假(即為0),那麼它先向stderr列印一條出錯資訊,然後通過呼叫 abort 來終止程式執行。請看下面的程式清單badptr.c:
#include <stdio.h> #include <assert.h> #include<stdlib.h>int main( void ) { FILE *fp; fp = fopen( "test.txt", "w" );//以可寫的方式開啟一個檔案,如果不存在就建立一個同名檔案 assert( fp ); //所以這裡不會出錯 fclose( fp ); fp = fopen( "noexitfile.txt", "r" );//以只讀的方式開啟一個檔案,如果不存在就開啟檔案失敗 assert( fp );//所以這裡出錯 fclose( fp ); //程式永遠都執行不到這裡來return0; }
[[email protected] error_process]# gcc badptr.c
[[email protected] error_process]# ./a.out
a.out: badptr.c:14: main: Assertion `fp' failed.
已放棄使用assert()的缺點是,頻繁的呼叫會極大的影響程式的效能,增加額外的開銷。在除錯結束後,可以通過在包含#include <assert.h>的語句之前插入 #define NDEBUG 來禁用assert呼叫,示例程式碼如下:
#include <stdio.h>#define NDEBUG #include <assert.h>
用法總結與注意事項:
1)在函式開始處檢驗傳入引數的合法性如:
int resetBufferSize(int nNewSize) { //功能:改變緩衝區大小, //引數:nNewSize 緩衝區新長度 //返回值:緩衝區當前長度 //說明:保持原資訊內容不變 nNewSize<=0表示清除緩衝區 assert(nNewSize >=0); assert(nNewSize <= MAX_BUFFER_SIZE); ... }
2)每個assert只檢驗一個條件,因為同時檢驗多個條件時,如果斷言失敗,無法直觀的判斷是哪個條件失敗,如:
不好:
assert(nOffset>=0&& nOffset+nSize<=m_nInfomationSize);
好:
assert(nOffset >=0); assert(nOffset+nSize <= m_nInfomationSize);
3)不能使用改變環境的語句,因為assert只在DEBUG個生效,如果這麼做,會使用程式在真正執行時遇到問題,如:
錯誤:
assert(i++<100);
這是因為如果出錯,比如在執行之前i=100,那麼這條語句就不會執行,那麼i++這條命令就沒有執行。
正確:
assert(i <100); i++;
4)assert和後面的語句應空一行,以形成邏輯和視覺上的一致感。
5)有的地方,assert不能代替條件過濾。
還可以使用STL -- nth_element來逐步求前k個最小的值,簡單的例子說明如下:
#include<iostream>
#include<algorithm> //必須的
using namespace std;
int main()
{
int a[]={3,2,6,1,8};
for(int i=2;i>=0;i--)
{
nth_element(a,a+i,a+4);//求前3個最小的數
cout<<a[i]<<endl;
}
return 0;
}