歸並排序和快速排序


這篇博客記錄一下兩種比較快速的排序。

歸並排序和快速排序。


歸並排序(\(O(nlogn)\)

歸並排序,顧名思義就是先遞歸后合並,這里畫了一張圖簡單理解歸並過程:

代碼肯定要用到遞歸啦,遞歸到不能再細分就可以開始合並了。而合並是通過申請額外的內存空間來完成的,合並時在左右兩個區間內進行比較,按照大小依次放入額外的空間中,最后復制回原數組,這樣一直遞歸往上,就實現了排序。

代碼:

void merge_sort(int arr[], int a,int b)//遞歸
{
	if (a >= b)
		return;
	int mid = (a + b) / 2;
	merge_sort(arr, a, mid);
	merge_sort(arr, mid + 1, b);
	merge(arr, a, mid, b);
}

void merge(int arr[], int l, int mid, int r)//合並
{
	int *temp=new int[r - l + 1];//申請額外空間
	int i = l, j = mid + 1, k = 0;
	while (i <= mid && j <= r)//比較並存放在額外的空間里
	{
		if (arr[i] <= arr[j])
			temp[k++] = arr[i++];
		else
			temp[k++] = arr[j++];
	}
	/*將剩下元素的放入額外空間*/
	while (i <= mid)
		temp[k++] = arr[i++];
	while (j <= r)
		temp[k++] = arr[j++];
	for (int i = l; i <= r; i++)//復制回原空間
		arr[i] = temp[i - l];
	delete[]temp;
}

時間復雜度推算:

歸並排序的一大好處就是不管數據是什么樣的,它的時間復雜度都為\(O(nlogn)\)。但它的缺點就是會用額外空間,空間復雜度為\(O(n)\)


快速排序

快速排序也是利用二分法和遞歸進行排序。

下面是簡單的快排過程:

快排的核心就在\(partition\)這個函數,他的作用就是再區間里隨機找一個點(一般找最右邊哪個元素),將這個點作為中心點,將比他小的元素放在左邊,比他大的元素放在右邊(升序),然后不斷二分最后就得到一個排列好的數組了。

void quick_sort(int arr[], int a, int b)
{
	if (a >= b)
		return;
	int p = partition(arr, a, b);
	quick_sort(arr, a, p - 1);
	quick_sort(arr, p + 1, b);//這里一定是p+1,將中心點排除在外,不然會在區間里有重復元素的時候陷入死循環!!!!
}

int partition(int arr[], int a, int b)
{
	int i = a, j;
	int p = arr[b];//記錄中心點
	for (j = a; j < b; j++)
	{
		if (arr[j] < p)//找到比中心點小的元素就交換
		{
			swap(arr[i], arr[j]);
			i++;
		}
	}
	swap(arr[i], arr[b]);//將中心點交換到中間
	return i;//返回中心點下標
}

因為快排是個空間復雜度為\(O(lgn)\)的只有遞歸空間消耗的原地排序,所以在找中心點的時候為了不用額外空間算法設計的很巧妙。

這里將\(i\)作為一個游標,他的左邊是已經處理的區間,右邊為未處理區間,用\(j\)去找那些比中心點小的元素並和\(i\)交換,最后把選定的中心點和\(i\)交換就完成處理了。

值得注意的是快排是一種時間復雜度不穩定的算法,它一般情況下時間復雜度為\(O(nlogn)\),但在極端條件下會退化為\(O(n^2)\)


快速排序和歸並排序的區別

快速排序和歸並的區別就在於它正好和遞歸的排序反過來。快速排序先排序再遞歸細分,排序是從上到下的。歸並排序先遞歸細分再排序,排序是從下到上的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM