標籤:style blog http color 使用 os io strong
在面對大資料量的排序時(100W以上量級資料),通常用以下三種的排序方法:快速排序、歸併排序,堆排序。在這個量級上,其他冒泡,選擇,插入排序等已經根本沒法看了,效率極低,跟前面三種排序差了千百倍,因此不作比較。
這三種排序的平均時間複雜度均為O(nlogn),快速排序,歸併排序在面對基本有序序列排序時,效率反會降低。且歸併排序需要用到O(n)的臨時儲存空間。而堆排序沒有明顯缺點,特別在面對經常會插入新元素的排序需求,堆排序效果最好。
下面是三種排序對100W個無序數組進行排序的時間對比,可以看出在平均情況下,時間效率:快排>歸併>堆排序
基礎概念
什麼是堆?
堆:一種資料結構,全稱為:二元堆積資料結構,是一種數組對象。
當所有節點都大於各自左右子節點時,叫大頂堆;
當所有節點都小於各自左右子節點時,叫小頂堆。
在堆排序中,使用大頂堆結構。
排序原理
若輸出堆頂的最大值之後,使得剩餘n-1個元素的序列重新又建成一個堆,則得到n個元素中個次大值。如此反覆執行,便能得到一個有序序列,這個過程就稱之為堆排序。
因此堆排序的實現思路,可以細分為兩部分:
1、如何將一個無序數組排列成大頂堆(建堆過程)
2、拿走最大值後如何從剩下的堆中找出次大值,重建立立大頂堆(篩選過程)
時間複雜度
堆排序可分細分為兩部分:建堆過程+排序過程。
建堆過程時間複雜度為O(n),即將一個無序數組建立成堆只需要線性時間。
排序過程需要對n個資料進行篩選時,每次篩選需要O(logn)時間,所以整個排序過程的時間為O(nlogn)
因此堆排序總的已耗用時間為: O(nlogn) = O(n) + O(nlogn)
演算法實現
#include "stdafx.h"#include <iostream>#include <ctime>using namespace std;int a[1000000];#define BEGIN_RECORD \{ clock_t ____temp_begin_time___; ____temp_begin_time___=clock();#define END_RECORD(dtime) \dtime=float(clock()-____temp_begin_time___)/CLOCKS_PER_SEC;}/* 目標:篩選區域為以索引i為樹根的子樹,找出該子樹最大值,將其存放到索引i 過程:從索引為i的結點開始往下,與較大的子節點交換值,向下搜尋直到子樹底部 a - 待排序數組 i - 篩選起始結點索引 len - 排序元素數量*/void sift(int a[], int i, int len) { int temp = a[i]; int j = 2 * i; while(j <= len) { if(j < len && a[j] < a[j+1]) //如果右結點比左結點大,則拿右結點跟父節點比較 j++; if(a[i] < a[j]) //如果子節點比父節點大,則兩者交換值,子節點成為新的父節點,繼續向下篩選 { a[i] = a[j]; a[j] = temp; i = j; j = 2 * i; } else //如果父節點比子節點大,則說明找到了該子樹的最大值,結束篩選 { break; } } a[i] = temp;}/*堆排序(大頂堆)a - 待排序的數組len - 數組長度*/void heapSort(int a[], int len){ int temp; int i; for (i = len-1; i > 0; i--) //堆排序只能從下標為1開始排序,因此要把數組所有資料後一移位。下標0的資料不處理 { a[i] = a[i - 1]; } for (i = len/2; i >= 1; i--) //建堆過程(使得全樹的父節點都比子節點大) { sift(a, i, len); } for (i = len - 1; i >= 2; i--) //排序過程:每次從樹根取值(該值必為最大值),放到樹的最後一個結點n,並把該結點從樹中移除。重複排序過程,直到將所有結點從樹移除,排序結束 { temp = a[1]; a[1] = a[i]; a[i] = temp; sift(a, 1, i - 1); //從樹根取出最大值,取最尾樹結點放到樹根,此時樹根不再為最大值,需要再對樹根進行一次篩選過程,以確保樹根仍然為最大值 }}void printArray(int a[], int length){ cout << "數組內容:"; for(int i = 0; i < length; i++) { if(i == 0) cout << a[i]; else cout << "," << a[i]; } cout << endl;}int _tmain(int argc, _TCHAR* argv[]){ float tim; BEGIN_RECORD //int a[1000000]; for (int i = 0; i < 1000000; i++) { a[i] = int(rand() % 100000); } //printArray(a, sizeof(a)/sizeof(int)); heapSort(a, sizeof(a)/sizeof(int)); //printArray(a, sizeof(a)/sizeof(int)); END_RECORD(tim) cout << "已耗用時間:" << tim << "s" << endl; system("pause"); return 0;}