1. 程式人生 > >100萬個無序數排序演算法的執行時間比較

100萬個無序數排序演算法的執行時間比較

  1. 氣泡排序(時間複雜度N2,空間複雜度1)
    依次比較陣列相鄰兩個元素,將最大或最小元素放到後面,慢慢“浮”到數列的最後。
  2. 選擇排序(時間複雜度N2,空間複雜度1)
    首先找到陣列最小元素,將它和陣列的第一個元素交換位置。再次,在剩下的元素中找到最小的元素,將它與陣列第二個元素交換,如此往復。
  3. 插入排序(時間複雜度介於N和N2之間,空間複雜度1)
  4. 將陣列分為有序和無序兩個部分,預設陣列左變第一個元素是有序的,依次遍歷陣列第二個元素到最後,與陣列左變有序序列比較。如果小於左變有序序列則交換位置。
  5. 希爾排序(時間複雜度NlogN,空間複雜度1)
    分組+插入排序。插入排序只能交換相鄰的元素,元素只能一點一點從陣列的一端移動到另一端。希爾排序的思想是使陣列中任意間隔為gap的陣列都是有序的。經驗公式:gap=gap/3+1,如果有12個元素,那麼同一組元素相鄰元素間隔4,下一次分組間隔2,最後一次間隔1。
  6. 歸併排序(時間複雜度NlogN,空間複雜度N(遞迴臨時變數))
    將兩個有序陣列歸併到第三個陣列中,遞迴在發生在處理陣列之後。
  7. 快速排序(時間複雜度NlogN,空間複雜度lgN))
    與歸併一樣,都是利用分治的排序演算法。將陣列分成兩個子陣列,將兩部分獨立排序,遞迴發生在處理陣列之前。不需要額外的記憶體空間儲存一個臨時陣列。
  8. 堆排序(時間複雜度NlogN,空間複雜度1)
    利用堆(一種特殊的完全二叉樹)而設計的一種排序演算法。如果是正序排序,先構建一個最大堆,交換a[1]和a[n],此時a[n]就是陣列中最大元素。n–,a[1]向下調整保持最大堆特性,進行下一次交換。
  9. 桶排序(時間複雜度lg(M+N),空間複雜度N)
    構建一個臨時陣列book,長度為要排序陣列的最大值。遍歷一次陣列a,記錄a[i]的值的次數:book[a[i]]++j.最後遍歷一遍陣列book,將i值和出現的數次寫到陣列a中。

這裡是引用

#include<iostream>
#include<time.h>
#include <sys/timeb.h>
using namespace std;
//效率測試
#define NUMBER 1000000
long long getSystemTime()
{
	struct timeb t;
	ftime(&t);
	return 1000 * t.time + t.millitm;
}


 void bucketSort(int *a,int *book, int len)
{
	 for (int i = 0; i < len; i++)
	 {
		 int tem = a[i];
		 book[tem]++;
	 }
	 int k = 0;
	 for (int i = 0; i < NUMBER; i++)
	 {
		 int ncount = book[i];
		 for (int j = 0; j < ncount; j++)
		 {
			 a[k++] = i;
		 }
	 }
}

 //-----------------三向切分的快速排序 ---------------------
void quick3WaySort(int *a, int left, int right)
{
	if (left > right) return;
	int lt = left;
	int i = left + 1;
	int gt = right;
	int tem = a[left];
	while (i <= gt)
	{
		if (a[i] < tem)  //小於切分元素的放在lt左邊,lt和i整體右移
		{
			//exchange(a, lt++, i++);
			int tmp = a[i];
			a[i] = a[lt];
			a[lt] = tmp;
			lt++; i++;
		}
		else if (a[i] > tem)//大於切分元素的放在gt右邊,因此gt左移
		{
			//exchange(a, i, gt--);
			int tmp = a[i];
			a[i] = a[gt];
			a[gt] = tmp;
			gt--;
		}
		else
			i++;
	}
	quick3WaySort(a,left,lt-1);
	quick3WaySort(a, gt+1, right);
}


#if 0
void quickSort(int *a, int left, int right)
{
	if (left > right) return;
	int i = left;
	int j = right;
	int tem = a[left];
	while (i != j) 
	{
		while (a[j]>tem && i<j)
			j--;
		while (a[i] <= tem &&i<j)
			i++;
		if (i < j)
		{
			int t = a[j];
			a[j] = a[i];
			a[i] = t;
		}
	}

	a[left] = a[i];
	a[i] = tem;

	quickSort(a, left, i - 1);
	quickSort(a, i+1, right);
	return;
}
#endif
//---------------堆排序--------------------
void shiftDown(int *a,int len,int i)
{
	int index, flag = 0;
	//----結點有左孩子-----
	while (i * 2 <= len && flag == 0)
	{
		if (a[i] < a[i * 2])
			index = i * 2;
		else
			index = i;
		//----結點有左孩子-----
		if (i * 2 + 1 <= len)
		{
			if (a[index] < a[i * 2 + 1])
				index = i * 2 + 1;
		}
		if (index != i)
		{
			//---交換i節點與子節點,更新編號為下一次向下調整準備
			int tem = a[i];
			a[i] = a[index];
			a[index] = tem;
			i = index;  
		}
		else
		{
			flag = 1;
		}
	}
}


void heapSort(int *a, int len)
{
	//-------------建最大堆----------------
	for (int i = len / 2; i >= 1; i--)
	{
		shiftDown(a,len,i);
	}
	//--------------排序-------------------
	while (len>1)
	{
		int tem = a[len];
		a[len] = a[1];
		a[1] = tem;
		len--;
		shiftDown(a,len,1);
	}
}


//-------------------歸併排序-------------------------
void merge(int *a, int first, int mid, int last, int *temp)
{
	int i = first;      // 第1個有序序列的開始下標
	int j = mid + 1;    // 第2個有序序列的開始下標
	int len = 0;
	// 合併兩個有序序列
	while (i <= mid && j <= last)
	{
		if (a[i] < a[j])
		{
			// 找二者中比較小的數
			temp[len] = a[i];
			i++;
		}
		else
		{
			temp[len] = a[j];
			j++;
		}
		len++;
	}
	while (i <= mid)
	{
		temp[len] = a[i];
		i++;
		len++;
	}
	while (j <= last)
	{
		temp[len] = a[j];
		j++;
		len++;
	}
	// 找到原來的第一個有序序列的開始位置覆蓋無序序列
	for (int k = 0; k < len; k++)
	{
		a[first+k] = temp[k];
	}
}

void mergeSort(int *a, int first, int last, int *temp)
{
	if (first == last)
		return;
	int mid =  first+ (last - first) / 2; //防止first+last溢位
	mergeSort(a, first, mid, temp);
	mergeSort(a, mid+1, last, temp);
	merge(a, first, mid, last, temp);
}

//-------------------希爾排序-------------------
void shellSort(int *a, int len)
{
	int gap = len;
	int index, tem;
	while (gap > 1)
	{
		gap = gap / 3 + 1;
		// =======分組, 對每一組, 進行插入排序
		for (int i = 0; i < gap; i++)
		{
			//----插入排序----------
			for (int j = i + gap; j < len; j += gap)
			{
				index=j;  
				tem=a[j];
				for (int k = j; k > i; k -= gap)
				{
					if (tem < a[k - gap])
					{
						a[k] = a[k - gap];
						index = k - gap;
					}
					else
					{
						break;
					}
				}
				a[index] = tem;
			}
		}
	}
}


//--------------------插入排序---------------------
void insertSort(int *a, int len)
{
	int index;
	int tem;
	// 遍歷無序序列
	for (int i = 1; i < len; i++)
	{
		index = i;  //儲存當前基準數位置
		tem = a[i];
		// 遍歷有序序列
		for (int j = i; j >0; j--)
		{

			if (tem < a[j - 1])  //找到比基準數大的位置,則將有序序列後移,並記錄有序序列位置
			{
				a[j] = a[j - 1];
				index = j - 1;
			}
			else
			{
				break;
			}
		}
		a[index] = tem; //將基準數填入
	}
}

//--------------選擇排序---------------

void selectSort(int *a, int len)
{
	for (int i = 0; i < len; i++)
	{
		int min = i;//基準書位置
		for (int j = i + 1; j < len; j++)
		{
			if (a[min] > a[j])
				min = j; //沒遍歷一次,找到最小值的序號
		}
		
		int tem = a[min];  //交換基準數和最小元素
		a[min] = a[i];
		a[i] = tem;
	}
}

//----------------氣泡排序------------------
void bubbleSort(int *a, int len)
{
	int flag = 0;//0表示沒有排序好,1表示排序好了
	for (int i = 0; i < len && flag == 0; i++)
	{
		//每進行一次冒泡,首先預設已經排好
		flag = 1;
		for (int j = 1; j < len - i; j++)
		{
			if (a[j] < a[j - 1])
			{
				int tem = a[j - 1];
				a[j - 1] = a[j];
				a[j] = tem;
				flag = 0;
			}

		}
	}
}

int main()
{
	long long timeStart, timeEnd, timeUse;
	srand((unsigned)time(NULL));

	int(*array)[NUMBER] = new int[7][NUMBER];
	int *arrayHeap = new int[NUMBER+1];

	//-----歸併和桶排序額外的記憶體---
	int *mergeArray = new int[NUMBER];
	int *bucketArrayBook = new int[NUMBER]();
	for (int i = 0; i < NUMBER; ++i)
	{
		//生成一個小於len的隨機數
		int number = rand() % NUMBER;
		for (int j = 0; j < 7; ++j)
		{
			array[j][i] = number;
		}
		arrayHeap[i+1] = number;
	}


	cout << "對第" << NUMBER << "個數進行排序" << endl;
	timeStart = getSystemTime();
	bucketSort(array[0], bucketArrayBook, NUMBER);
	timeEnd = getSystemTime();
	timeUse = timeEnd - timeStart;
	cout << "bucketSort     耗時:" << timeUse << "ms" << endl;;

	timeStart = getSystemTime();
	quick3WaySort(array[1], 0, NUMBER-1);
	timeEnd = getSystemTime();
	timeUse = timeEnd - timeStart;
	cout << "quick3WaySort  耗時:" << timeUse << "ms" << endl;;

	timeStart = getSystemTime();
	heapSort(arrayHeap, NUMBER);
	timeEnd = getSystemTime();
	timeUse = timeEnd - timeStart;
	cout << "heapSort       耗時:" << timeUse << "ms" << endl;;

	timeStart = getSystemTime();
	mergeSort(array[2],0, NUMBER-1, mergeArray);
	timeEnd = getSystemTime();
	timeUse = timeEnd - timeStart;
	cout << "mergeSort      耗時:" << timeUse << "ms" << endl;;

	timeStart = getSystemTime();
	shellSort(array[3], NUMBER);
	timeEnd = getSystemTime();
	timeUse = timeEnd - timeStart;
	cout << "shellSort      耗時:" << timeUse << "ms" << endl;;

	timeStart = getSystemTime();
	insertSort(array[4], NUMBER);
	timeEnd = getSystemTime();
	timeUse = timeEnd - timeStart;
	cout << "insertSort     耗時:" << timeUse << "ms" << endl;;

	timeStart = getSystemTime();
	selectSort(array[5], NUMBER);
	timeEnd = getSystemTime();
	timeUse = timeEnd - timeStart;
	cout << "selectSort     耗時:" << timeUse << "ms" << endl;;

	timeStart = getSystemTime();
	bubbleSort(array[6], NUMBER);
	timeEnd = getSystemTime();
	timeUse = timeEnd - timeStart;
	cout << "bubbleSort     耗時:" << timeUse << "ms" << endl;

	delete []array;
	delete arrayHeap;
	delete mergeArray;
	delete bucketArrayBook;
}

在這裡插入圖片描述
在這裡插入圖片描述
總結:
1.在這8中排序演算法當中,桶排序是最快的,氣泡排序最慢。
但是,桶排序要求構建一個待排序最大元素的陣列,往往排序的時候我們並不能確定最大元素的值,另一方面和並歸排序一樣,當陣列很大的時候,也要考慮額外的記憶體分配問題。
2.在陣列非常長的排序,各種排序演算法中,三向切分快速排序是最快的(較快速排序提升了20%到30%)。但是,需要考慮遞迴產生臨時是否會導致棧溢位的問題。堆排序時間複雜度ONlogN,空間複雜度1,並且對已經排序好的陣列插入一個元素的時候,堆是一種優先佇列的時間,查詢和插入的時間複雜度較優。
3.對於小陣列,快速排序比插入排序慢。因此在設計排序的時候可以考慮通過判斷長度來切換排序演算法。