1. 程式人生 > >四種快排與兩種歸併和堆和插入排序 大資料量執行時間比較

四種快排與兩種歸併和堆和插入排序 大資料量執行時間比較

#include"iostream"
#include"iomanip"
#include"stdlib.h"
#include"time.h"
#include"string"
/*由於我電腦記憶體有限所以資料量最大能執行在20w*/




//三路快排適用於有大量重複值的資料 
//四種快排與兩種歸併和堆和插入排序 大資料量執行時間比較 
//快排一:quickSort 有可能退化為O(n^2)
//快排二:quickSort2對快排一的問題進行了優化 
//快排三:quickSort3採用三路快排,用的是快排一的編碼方式 
//快排四:quickSort4採用三路快排,用的是快排二的編碼方式 
//歸併一:merge_sort採用遞迴自下向上排序 
//歸併二:merge_sortBU採用非遞迴自上向下排序,適用於連結串列排序 
//堆排序:heapSort
//插入:insert_sort
//測試得 “快排四”執行的相對時間最少 
using namespace std;
int *Factory(int n,int x,int y)//產生x和y之間的n個數 
{
srand(time(NULL));
int *arr=new int[n];
for(int i=0;i<n;i++){
arr[i]=rand()%(y-x)+x;
}
return arr;
}
void clock_calculate(string name,void (*sort)(int *a,int n),int *a,int n)//測試時間函式 
{
time_t start=clock();
sort(a,n);
time_t end=clock();
cout<<name<<":\t\t"<<(end-start)*1.0/1000<<"s"<<endl; 
}
int *copyArr(int *a,int n)//拷貝陣列函式 
{
int *a1=new int[n];
for(int i=0;i<n;i++){
a1[i]=a[i];
}
return a1;
}
/****************************************************************************/
void __quickSort4(int *a,int l,int r){
if(r>l){
swap(a[l],a[rand()%(r-l+1)+l]);
int t=a[l];
int i=l+1,j=r,lt=l+1;//lt始終指向最前面的等於t的元素,i是第一個沒有處理的資料 
while(i<=j)//j指向最後一個沒處理的
{
while(i<=j&&a[i]<=t){
if(a[i]==t){//遇見相等的則i向前走,lt指向第一個等於t的元素 
i++;
}else if(i==lt){//當i==lt說明沒有遇見跟t相等的元素 
i++,lt++;
}else{//i!=lt說明有重複的t,將i跟lt進行交換 
swap(a[i],a[lt]);
i++,lt++;
}
}
while(i<=j&&a[j]>t){
j--;
}
if(i>j)
break;
swap(a[i],a[j]);
j--;

swap(a[l],a[lt-1]);
__quickSort4(a,l,lt-2);
__quickSort4(a,j+1,r);
}
}
void quickSort4(int a[],int len){
srand(time(NULL));
__quickSort4(a,0,len-1);
}


/***********************************************************************/
void __quickSort3(int *a,int l,int r){
if(r>l){
swap(a[l],a[rand()%(r-l+1)+l]);
int t=a[l];
int i=l+1,j=r,lt=l+1;
while(i<=j)//j指向最後一個沒處理的
{
if(a[i]<t) { 
swap(a[i],a[lt]);
i++,lt++;
}else if(a[i]>t){
swap(a[i],a[j]);
j--;
}else{
i++;
}

swap(a[l],a[lt-1]);
__quickSort3(a,l,lt-2);
__quickSort3(a,j+1,r);
}
}
void quickSort3(int a[],int len){
srand(time(NULL));
__quickSort3(a,0,len-1);
}
/****************************************************************************/
int partition(int a[],int l,int r){

swap(a[l],a[rand()%(r-l+1)+l]);//隨機產生中間的標誌 
int t=a[l];//當陣列近乎有序的時候退化為O(n^2) 所以新增以上程式碼 
int i,j;
for(j=l,i=l+1;i<=r;i++){
if(a[i]<t){
swap(a[j+1],a[i]);
j++;
}//j+1一直指向不小於t的數 j一直指向不大於t的數 
}
swap(a[l],a[j]);
return j;
}
void __quickSort(int *a,int l,int r){
if(r>l){
int p=partition(a,l,r);
__quickSort(a,l,p-1);
__quickSort(a,p+1,r);
}
}
void quickSort(int a[],int len){
srand(time(NULL));
__quickSort(a,0,len-1);
}
/******************************************************************************/
int partition2(int a[],int l,int r){
swap(a[l],a[rand()%(r-l+1)+l]);
int t=a[l];
int i=l+1,j=r;
while(i<=j){//用i==j的條件去確定j的位置 
while(i<=j&&t>=a[i]) i++;
while(i<=j&&t<=a[j]) j--; 
//執行前面的程式碼 不可能讓i==j 因為不可能有一個數既滿足i又滿足j 
if(i>j)//一種情況利用i==j的情況 然後讓i ,j遞變 讓j指向中間位置 
break;
swap(a[i],a[j]);
i++;
j--;//如果用i<j條件 1.當執行完這步後i,j指向的兩個都小於t,再進迴圈就只判斷了i, 不會進j的迴圈,那j和i指向的就是中間位置
// 2.當執行完最後一步後兩個都滿足j,那麼就不會進入i的迴圈,j等於i,i的前一個就是中間位置 

swap(a[l],a[j]);
return j;
}
void __quickSort2(int *a,int l,int r){
if(r>l){
int p=partition2(a,l,r);
__quickSort2(a,l,p-1);
__quickSort2(a,p+1,r);
}
}
void quickSort2(int a[],int len){
srand(time(NULL));
__quickSort2(a,0,len-1);
}
/*******************************************************************************/
void merge(int *a,int l,int m,int r)
{
int tmp[r-l+1];
int i,j,k;
for(i=l;i<=r;i++){
tmp[i-l]=a[i];
}
for(i=l,j=m+1,k=l;k<=r;k++){
if(i>m){
a[k]=tmp[j-l];
j++;
}else if(j>r){
a[k]=tmp[i-l];
i++;
}else if(tmp[i-l]>tmp[j-l]){
a[k]=tmp[j-l];
j++;
}else{
a[k]=tmp[i-l];
i++;
}
}
}
/*自底向上歸併*/
void _merge_sort(int *a,int left,int right){
int mid=(left+right)/2;
if(right!=left){
_merge_sort(a,left,mid);
_merge_sort(a,mid+1,right);
if(a[mid]>a[mid+1]);{
merge(a,left,mid,right);
}
}
}
void merge_sort(int *a,int len){
_merge_sort(a,0,len-1);
}
/***********************************************************************************/
void insert_sort(int a[],int n){
int result=1;
for(int i=0;i<n&&result;i++){
result=0;
for(int j=0;j<n-i+1;j++){
if(a[j]>a[j+1]){
swap(a[j],a[j+1]);
result=1;
}
}
}
}
/*******************************************************************************/
/*自頂向下歸併*/
int min(int a,int b){
return a>b?b:a;
}
void merge_sortBU(int a[],int len){
for(int sz=1;sz<=len;sz+=sz){//每次歸併的長度 
for(int i=0;i+sz<len;i+=sz+sz){//i+sz<len保證歸併有右半部分(至少有兩個部分) 
//對a[i...i+zs-1]和a[i+sz...i+sz+sz-1]進行歸併 
merge(a,i,i+sz-1,min(i+sz+sz-1,len-1));//沒有使用陣列索引 更方便對連結串列這種資料結構進行排序 
}
}
}
/**********************************************************************************/
//堆排序 
void __shiftDown(int a[],int n,int k)
{
while((k*2+1)<n)
{
int j=k*2+1;
if((j+1)<n&&a[j+1]>a[j])
{
j++;
}
if(a[k]>=a[j])
break;
swap(a[k],a[j]);
k=j;
}

}
void heapSort(int a[],int n)
{
for(int i=(n-1)/2;i>=0;i--)
{
//將陣列a的n個元素的第i個節點進行位置調整形成最大堆 
//陣列索引從0開始的堆 共有 (n-1)/2個非葉子節點 
__shiftDown(a,n,i);
}
//建立好最大堆後進行堆排序
for(int i=n-1;i>0;i--)
{
swap(a[0],a[i]);
//將共有i個節點的堆a,第0個節點進行調整 
__shiftDown(a,i,0);
}
}


int main()
{
int N=200000;//資料量 
int min_data=0,max_data=1000;
int *a=Factory(N,min_data,max_data);//產生N個[min_data,max_data)之間的數 
int *a2=copyArr(a,N);//拷貝產生的第一個隨機數,以供其他排序演算法使用 
int *a3=copyArr(a,N);
int *a4=copyArr(a,N);
int *a5=copyArr(a,N);
int *a6=copyArr(a,N);
int *a7=copyArr(a,N);
int *a8=copyArr(a,N);
printf("資料量:%d\n",N);
printf("資料取值範圍:[%d,%d)\n",min_data,max_data);
printf("\n----------------------------------------------------\n\n");
clock_calculate("quickSort",quickSort,a,N);//計算排序的執行時間 
clock_calculate("quickSort2",quickSort2,a2,N);
clock_calculate("quickSort3",quickSort3,a3,N);
clock_calculate("quickSort4",quickSort4,a4,N);
clock_calculate("merge_sort",merge_sort,a5,N);
clock_calculate("merge_sortBU",merge_sortBU,a6,N);
clock_calculate("heapSort",heapSort,a7,N);
clock_calculate("insert_sort",insert_sort,a8,N);
delete [] a;
delete [] a2;
delete [] a3;
delete [] a4;
delete [] a5;
delete [] a6;
delete [] a7;
delete [] a8;

}