国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

這篇具有很好參考價(jià)值的文章主要介紹了【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

目錄

一、基本介紹

二、快排的實(shí)現(xiàn)

1. 調(diào)試環(huán)境

2.快排的單趟排序

(1)Hoare版本

(2)挖坑法

(3)前后指針?lè)?/p>

2.遞歸過(guò)程

三、快排的優(yōu)化

1. 優(yōu)化取key方式,防止棧溢出

2. 小區(qū)間優(yōu)化

四、快排的非遞歸方式


前言:

????????排序算法是日常使用最頻繁的一個(gè)算法,生活中也很常見(jiàn)什么排隊(duì)呀按照高矮次序呀,分?jǐn)?shù)按照一個(gè)從高到低的排序等等,但是如果是要設(shè)計(jì)出來(lái)面對(duì)基數(shù)很大又要很快的排序方法這就是需要很大難度了,先給大家看看排序的種類(lèi)有哪些,和其對(duì)應(yīng)的時(shí)間空間復(fù)雜度。

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

? ? ? ? ?今天我要分享的便是當(dāng)今使用最為廣泛的快速排序算法??焖倥判蚴怯捎?guó)計(jì)算機(jī)專(zhuān)家Tony Hoare大佬在他26歲時(shí)發(fā)布的算法,快速排序整體的綜合性能和使用場(chǎng)景都是比較好的,所以才敢叫快速排序。接下來(lái),我將介紹快排的主要內(nèi)容。

一、基本介紹

????????快速排序的基本思想為:任取待排序元素序列中的某元素作為基準(zhǔn)值,按照該排序碼將待排序集合分割成兩子序列,左子序列中所有元素均小于基準(zhǔn)值,右子序列中所有元素均大于基準(zhǔn)值,然后最左右子序列重復(fù)該過(guò)程,直到所有元素都排列在相應(yīng)位置上為止。

快速排序算法通過(guò)多次比較和交換來(lái)實(shí)現(xiàn)排序,其排序流程如下:?

  • 首先設(shè)定一個(gè)分界值Key,通過(guò)該分界值將數(shù)組分成左右兩部分。?
  • 將大于或等于分界值的數(shù)據(jù)集中到數(shù)組右邊,小于分界值的數(shù)據(jù)集中到數(shù)組的左邊。此時(shí),左邊部分中各元素都小于分界值,而右邊部分中各元素都大于或等于分界值。??
  • 然后,左邊和右邊的數(shù)據(jù)可以獨(dú)立排序。對(duì)于左側(cè)的數(shù)組數(shù)據(jù),又可以取一個(gè)分界值,將該部分?jǐn)?shù)據(jù)分成左右兩部分,同樣在左邊放置較小值,右邊放置較大值。右側(cè)的數(shù)組數(shù)據(jù)也可以做類(lèi)似處理。?
  • 重復(fù)上述過(guò)程,可以看出,這是一個(gè)遞歸定義。通過(guò)遞歸將左側(cè)部分排好序后,再遞歸排好右側(cè)部分的順序。當(dāng)左、右兩個(gè)部分各數(shù)據(jù)排序完成后,整個(gè)數(shù)組的排序也就完成了。

上述為快速排序遞歸實(shí)現(xiàn)的主框架,簡(jiǎn)而言之就是快排分為兩步:

(1)通過(guò)找Key將數(shù)組分成一大一小兩部分

(2)遞歸

我們不難發(fā)現(xiàn)這與二叉樹(shù)前序遍歷規(guī)則非常像,所以在寫(xiě)遞歸框架時(shí)可想想二叉樹(shù)前序遍歷規(guī)則即可快速寫(xiě)出來(lái),后序只需分析如何按照基準(zhǔn)值來(lái)對(duì)區(qū)間中數(shù)據(jù)進(jìn)行劃分的方式即可。因此,快排也可以說(shuō)是基于二叉樹(shù)的排序算法。


二、快排的實(shí)現(xiàn)

1. 調(diào)試環(huán)境

為了方便測(cè)試寫(xiě)出的快排的正確性以及效率,我們首先寫(xiě)出下面兩段用于測(cè)試的代碼。

其中PrintArray是數(shù)組打印函數(shù)

TestQuickSort函數(shù)用于測(cè)試代碼的正確性

TestOP函數(shù)用于測(cè)試快排的效率

void PrintArray(int* a, int n)
{
	for (int i = 0; i < n; ++i)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}

void TestQuickSort()
{
	int a[] = { 9,8,7,6,5,4,3,2,1,0 };
	QuickSort(a, 0, 9);
	PrintArray(a, 10);
}
void TestOP()
{
	srand(time(0));
	const int N = 1000000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
	}

	int begin1 = clock();
	QuickSort(a1, 0, N - 1);
	int end1 = clock();
	printf("QuickSort:%d\n", end1 - begin1);
}

現(xiàn)在我們有了測(cè)試函數(shù)之后,可以進(jìn)行快排的具體實(shí)現(xiàn)過(guò)程了。


2.快排的單趟排序

按照上文中所說(shuō)的步驟,我們首先進(jìn)行第一步,找key將數(shù)組分成一大一小的兩部分,也就是快排的單趟排序??炫虐l(fā)展到現(xiàn)在,單趟排序一共有三種主流的方式,下面我將逐一解釋這三種方式的具體做法。

(1)Hoare版本

第一種做法是Hoare大佬最初設(shè)計(jì)快排時(shí)提出并采用的方法。首先我們定義leftright兩個(gè)指針?lè)謩e指向數(shù)組的頭和尾,然后將數(shù)組中第一個(gè)數(shù)據(jù)定義為key。

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?????????我在這里按照升序演示,因?yàn)槲覀冃枰WC數(shù)組左邊的數(shù)據(jù)比key小,右邊的數(shù)據(jù)比key大,所以我們讓right指針先出發(fā)找小,left指針找大,找到后兩指針指向的數(shù)據(jù)交換。

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?然后讓left和right繼續(xù)前進(jìn),分別找大找小交換后,直到兩指針相遇。

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?兩指針相遇后,將相遇位置的數(shù)據(jù)與key交換,此時(shí)快排的第一步單趟排序結(jié)束。

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

注意:?你可能會(huì)對(duì)最后一步有疑問(wèn),為什么直接將key與相遇位置交換,相遇位置有沒(méi)有可能比key大,直接交換是不是會(huì)出錯(cuò)?

答案是不會(huì),因?yàn)槲覀冏屪筮叺谝粋€(gè)數(shù)做key,right先走

  • R停下來(lái),L撞到R,相遇位置比key小
  • L停下來(lái),R撞到L,相遇位置比key小

因此,如果選右邊第一個(gè)數(shù)做key的話(huà),就需要讓left先走了。

?這便是Hoare版本的具體過(guò)程,下面請(qǐng)看具體代碼。

(返回值是為了后面要提及的遞歸)

//快排的單趟排序(hoare版本)
int PartSort1(int* a, int left, int right)
{
	int keyi = left;
	while (left < right)
	{		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	int meeti = left;
	Swap(&a[keyi], &a[meeti]);
	return meeti;
}

(2)挖坑法

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解?

?【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-484306.html

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?下面是具體的代碼。

//單趟排序--挖坑法
int PartSort2(int* a, int left, int right)
{
	int key = a[left];
	int hole = left;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			left++;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

(3)前后指針?lè)?/h4>

?【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

下面是具體代碼。

//前后指針?lè)?int PartSort3(int* a, int left, int right)
{
	int key = a[left];
	int prev = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < key && ++prev < cur)
			Swap(&a[prev], &a[cur]);
		cur++;
	}
	Swap(&a[left], &a[prev]);
	return prev;
}

2.遞歸過(guò)程

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	int keyi = PartSort3(a, begin, end);

	QuickSort(a, begin, keyi - 1);
	QuickSort(a, keyi + 1, end);
	
}

????????只要寫(xiě)完快排的單趟排序,遞歸過(guò)程就非常簡(jiǎn)單了。上面的單趟排序中返回值便是排序好后key的位置,也就是說(shuō)快排的單趟排序可以確實(shí)一個(gè)元素的正確位置,并且將數(shù)組分成了比key小和比key大的兩個(gè)部分。所以直接遞歸【begin,key-1】 和 【key+1,end】就可以了。


三、快排的優(yōu)化

1. 優(yōu)化取key方式,防止棧溢出

????????經(jīng)過(guò)上面的講解我們已經(jīng)有了一個(gè)快排的初步代碼,現(xiàn)在我們可以使用開(kāi)頭給出的測(cè)試函數(shù)檢查一下快排的正確性,以及快排的效率怎么樣。

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

????????首先,我們寫(xiě)的快排正確性沒(méi)有問(wèn)題,然后我給出了測(cè)試有一百萬(wàn)個(gè)隨機(jī)數(shù)的數(shù)組,排序用了?183ms,速度還是比較快的。

讓我們來(lái)計(jì)算一下快排的時(shí)間復(fù)雜度,不難得出快排的時(shí)間復(fù)雜度為O(NlogN)

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?但是,讓我把測(cè)試函數(shù)改一下的話(huà),則會(huì)出現(xiàn)一些狀況。

void TestOP()
{
	srand(time(0));
	const int N = 1000000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
	}
	QuickSort(a1, 0, N - 1);
	int begin1 = clock();
	QuickSort(a1, 0, N - 1);
	int end1 = clock();
	printf("QuickSort:%d\n", end1 - begin1);
}

我們讓快排去給一個(gè)有序的數(shù)組排序,按理說(shuō)應(yīng)該速度更快,結(jié)果卻棧溢出了。

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?


????????剛才我們計(jì)算時(shí)間復(fù)雜度的時(shí)候是按照較為理想的情況,如果一個(gè)數(shù)組有序或者接近有序,我們?cè)谑褂脛偛艑?xiě)出的代碼進(jìn)行排序時(shí),每個(gè)key選擇的都是左邊第一個(gè),那么遞歸深度就會(huì)很大,接近N,此時(shí)就是出現(xiàn)棧溢出的情況。

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?所以,為了避免這種情況,我們就需要優(yōu)化選key的方式,在這里,我給出三種方法:

  • 隨機(jī)選一個(gè)位置做key
  • 針對(duì)有序,選正中間做key
  • 三數(shù)取中:選出數(shù)組的begin end mid 三個(gè)位置然后取中間值做key。

其中,第三種方式最常用,實(shí)現(xiàn)過(guò)程也比較簡(jiǎn)單,下面我直接給出代碼。?

//三數(shù)取中選key
int getMid(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	if (a[left] < a[mid])
	{
		if (a[right] < a[left])
		{
			return left;
		}
		else if (a[right] > a[mid])
		{
			return mid;
		}
		else
			return right;
	}
	else  //a[left] > a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[right] > a[left])
		{
			return left;
		}
		else
			return right;
	}
}

2. 小區(qū)間優(yōu)化

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

我們?cè)谶f歸的過(guò)程中,小區(qū)間占用的函數(shù)棧幀最多,此時(shí)數(shù)組的長(zhǎng)度較小,使用快排的遞歸反而會(huì)導(dǎo)致效率下降,因此我們可以?xún)?yōu)化一下小區(qū)間使用其他排序方式排序。

????????我們進(jìn)行如下優(yōu)化,當(dāng)遞歸區(qū)間長(zhǎng)度小于8時(shí),我們使用插入排序,這樣可以進(jìn)一步提高快排的效率。

void QuickSort(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}

	// 小區(qū)間優(yōu)化
	if (end - begin < 8)
	{
		InsertSort(a + begin, end - begin + 1);
	}
	else
	{
		int keyi = PartSort1(a, begin, end);

		QuickSort(a, begin, keyi - 1);
		QuickSort(a, keyi + 1, end);
	}
}

四、快排的非遞歸方式

【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解

?

void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	StackInit(&st);
	StackPush(&st, begin);
	StackPush(&st, end);

	while ( !StackEmpty(&st) )
	{
		int right = StackTop(&st);
		StackPop(&st);
		int left = StackTop(&st);
		StackPop(&st);
		int mid = PartSort3(a, left, right);

		if (mid + 1 < right)
		{
			StackPush(&st, mid+1);
			StackPush(&st, right);
		}

		if (mid - 1 > left)
		{
			StackPush(&st, left);
			StackPush(&st, mid-1);
		}

	}
	StackDestroy(&st);
}

到了這里,關(guān)于【數(shù)據(jù)結(jié)構(gòu)】快速排序詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀(guān)點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【數(shù)據(jù)結(jié)構(gòu)】快速排序,歸并排序

    【數(shù)據(jù)結(jié)構(gòu)】快速排序,歸并排序

    1.hoare版本 根據(jù)動(dòng)圖的演示,整理的思路如下, 1.定義left,right,key。key默認(rèn)是左邊第一個(gè)元素,像兩個(gè)指針,左邊找比key大的,右邊找比k小的,找到的話(huà),交換二者,往返這個(gè)過(guò)程,當(dāng)left與right相遇時(shí),交換key和此時(shí)相遇的值. 單趟下來(lái),6出現(xiàn)在正確的位置。 1.為什么大循環(huán)

    2024年01月20日
    瀏覽(30)
  • 【數(shù)據(jù)結(jié)構(gòu)】排序(2)—冒泡排序 & 快速排序

    【數(shù)據(jù)結(jié)構(gòu)】排序(2)—冒泡排序 & 快速排序

    ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 目錄 一. 冒泡排序 基本思想 代碼實(shí)現(xiàn) 時(shí)間和空間復(fù)雜度 穩(wěn)定性 二. 快速排序 基本思想 代碼實(shí)現(xiàn) hoare法 挖坑法 前后指針?lè)?時(shí)間和空間復(fù)雜度 穩(wěn)定性 ? ? ? ? ? ?冒泡排序是一種交換排序。兩兩比較數(shù)組元素,如果是逆序(即排列順序

    2024年02月08日
    瀏覽(94)
  • 數(shù)據(jù)結(jié)構(gòu)——排序算法之快速排序

    數(shù)據(jù)結(jié)構(gòu)——排序算法之快速排序

    ? ??個(gè)人主頁(yè): 日刷百題 系列專(zhuān)欄 : 〖C/C++小游戲〗 〖Linux〗 〖數(shù)據(jù)結(jié)構(gòu)〗 ? 〖C語(yǔ)言〗 ?? 歡迎各位 → 點(diǎn)贊 ??+ 收藏 ??+ 留言 ??? ? ? 快速排序是Hoare于1962年提出的一種二叉樹(shù)結(jié)構(gòu)的交換排序方法。 基本思想: 任取待排序元素序列中 的某元素作為基準(zhǔn)值,按照

    2024年01月21日
    瀏覽(23)
  • 【數(shù)據(jù)結(jié)構(gòu)--八大排序】之快速排序

    【數(shù)據(jù)結(jié)構(gòu)--八大排序】之快速排序

    ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???? ?? ?? ?? 個(gè)人主頁(yè) :阿然成長(zhǎng)日記 ??點(diǎn)擊可跳轉(zhuǎn) ?? 個(gè)人專(zhuān)欄: ??數(shù)據(jù)結(jié)構(gòu)與算法??C語(yǔ)言進(jìn)階 ?? 不能則學(xué),不知?jiǎng)t問(wèn),恥于問(wèn)人,決無(wú)長(zhǎng)進(jìn) ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 前言: 前面,我花費(fèi)

    2024年02月08日
    瀏覽(86)
  • 數(shù)據(jù)結(jié)構(gòu)--快速排序

    數(shù)據(jù)結(jié)構(gòu)--快速排序

    快速排序是通過(guò)二叉樹(shù)的思想,先設(shè)定一個(gè)值,通過(guò)比較,比它大的放在它的右邊,比它小的放在它的左邊;這樣相當(dāng)于在二叉樹(shù)中,小的放在左子樹(shù),大的放在右子樹(shù),設(shè)定的值就是根;再通過(guò)遞歸的思想,將它們繼續(xù)按這種方式進(jìn)行排序,排到最后就排好了;這就是快速

    2024年02月08日
    瀏覽(34)
  • 數(shù)據(jù)結(jié)構(gòu)——lesson11排序之快速排序

    數(shù)據(jù)結(jié)構(gòu)——lesson11排序之快速排序

    hello hello~ ,這里是大耳朵土土垚~???? ,歡迎大家點(diǎn)贊????關(guān)注????收藏?????? ?? 個(gè)人主頁(yè):大耳朵土土垚的博客 ?? 所屬專(zhuān)欄:數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)筆記 、排序算法合集 ??對(duì)于數(shù)據(jù)結(jié)構(gòu)順序表、鏈表、堆以及排序有疑問(wèn)的都可以在上面數(shù)據(jù)結(jié)構(gòu)專(zhuān)欄和排序合集專(zhuān)欄進(jìn)行

    2024年04月16日
    瀏覽(31)
  • 【數(shù)據(jù)結(jié)構(gòu)】第十三站:排序(中)快速排序

    【數(shù)據(jù)結(jié)構(gòu)】第十三站:排序(中)快速排序

    快速排序是Hoare于1962年提出的一種二叉樹(shù)結(jié)構(gòu)的交換排序方法,其基本思想為:任取待排序元素序列中的某元素作為基準(zhǔn)值,按照該排序碼將待排序集合分割成兩子序列,左子序列中所有元素均小于基準(zhǔn)值,右子序列中所有元素均大于基準(zhǔn)值,然后最左右子序列重復(fù)該過(guò)程,

    2024年02月01日
    瀏覽(22)
  • 【數(shù)據(jù)結(jié)構(gòu)與算法】:選擇排序與快速排序

    【數(shù)據(jù)結(jié)構(gòu)與算法】:選擇排序與快速排序

    ?? 個(gè)人主頁(yè) : Quitecoder ?? 專(zhuān)欄 :數(shù)據(jù)結(jié)構(gòu)與算法 我的博客即將同步至騰訊云開(kāi)發(fā)者社區(qū),邀請(qǐng)大家一同入駐:騰訊云 歡迎來(lái)到排序的第二個(gè)部分:選擇排序與快速排序! 選擇排序是一種簡(jiǎn)單直觀(guān)的比較排序算法。該算法的基本思想是在每一輪中選出當(dāng)前未排序部分的最

    2024年03月17日
    瀏覽(25)
  • 【數(shù)據(jù)結(jié)構(gòu)初階】八大排序(二)——快速排序&&冒泡排序

    【數(shù)據(jù)結(jié)構(gòu)初階】八大排序(二)——快速排序&&冒泡排序

    大家好我是沐曦希?? 書(shū)接【數(shù)據(jù)結(jié)構(gòu)初階】八大排序(一)——希爾排序堆排序直接插入排序直接選擇排序 基本思想:所謂交換,就是根據(jù)序列中兩個(gè)記錄鍵值的比較結(jié)果來(lái)對(duì)換這兩個(gè)記錄在序列中的位置,交換排序的特點(diǎn)是: 將鍵值較大的記錄向序列的尾部移動(dòng),鍵值較小

    2024年02月21日
    瀏覽(312)
  • 數(shù)據(jù)結(jié)構(gòu)與算法:快速排序

    數(shù)據(jù)結(jié)構(gòu)與算法:快速排序

    荷蘭國(guó)旗問(wèn)題 想要理解快速排序,就先理解這個(gè)問(wèn)題: [LeetCode75.顏色分類(lèi)] 荷蘭國(guó)旗是由紅白藍(lán)三色組成的: 現(xiàn)在將其顏色打亂 然后根據(jù)一定的算法,將其復(fù)原為紅白藍(lán)三色,這就叫做荷蘭國(guó)旗問(wèn)題。 在LeetCode的題目中,其將荷蘭國(guó)旗的三個(gè)顏色用0,1,2來(lái)表達(dá),也就是說(shuō)

    2024年01月15日
    瀏覽(24)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包