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

快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握

這篇具有很好參考價(jià)值的文章主要介紹了快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法

大家好,這里是Dark FalmeMater。
這篇文章我將超級(jí)仔細(xì)地講解快速排序,快排之所以叫快排,到底有多快,為什么這么快,還有快速排序的優(yōu)化和改進(jìn),通過(guò)這篇文章你一定會(huì)對(duì)快排有進(jìn)一步的掌握。

快排的歷史及介紹
快速排序由C. A. R. Hoare在1962年提出。

它的基本思想是:通過(guò)一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨(dú)立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對(duì)這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序,整個(gè)排序過(guò)程可以遞歸 進(jìn)行,以此達(dá)到整個(gè)數(shù)據(jù)變成有序序列。

?其中Hoare大佬寫的版本由于沒有那么容易理解且容易出現(xiàn)錯(cuò)誤,在后人的智慧下,進(jìn)行了小小的改變,分化出了挖坑法和雙指針法。我們逐個(gè)進(jìn)行講解。

Hoare版

快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
遞歸函數(shù)分析思路如下
在數(shù)組中選定一個(gè)關(guān)鍵的點(diǎn),最右邊或者最右邊都可以。

?我們選擇最左邊進(jìn)行講解,找到關(guān)鍵位置后設(shè)置left和rght然后分別從數(shù)組左邊和最右邊開始遍歷,右邊先行,找到小于key位置的值就停下了,左邊的找大于key位置的值,找到后就互換,直到左下標(biāo)和右下標(biāo)相遇,相遇的時(shí)候說(shuō)明左邊已經(jīng)沒有大于key位置的值了,右邊沒有小于key位置的值了,交換相遇位置的值和key位置的值,就可以將該組數(shù)分割成以key位置的值分割的兩組數(shù)。

有了思路我們可以寫出以下的代碼:

//hoare版本
int PartSort1(int* a, int left, int right)
{
	int key = a[left];//設(shè)置關(guān)鍵點(diǎn)
	while (left < right)
	{
		while (a[right] > a[keyi])//從最右邊開始找到大于關(guān)鍵點(diǎn)的坐標(biāo)
		{
			--right;
		}
		//找大
		while (a[left] < a[keyi])//從左邊開始找到小于key的坐標(biāo)
		{
			++left;
		}
		Swap(&a[left], &a[right]);//交換
	}
	Swap(key, &a[left]);//當(dāng)left相遇,就交換關(guān)鍵點(diǎn)和相遇點(diǎn)的值
	return left;返回相遇點(diǎn)的坐標(biāo)
}

Hoare版解答疑惑及易錯(cuò)點(diǎn)指出:

  1. key的選擇

?之所以選擇最左邊的位置設(shè)置key,是因?yàn)槔眠@個(gè)值在寫代碼和進(jìn)行判斷時(shí)更方便一些,利用right位置的值當(dāng)做key當(dāng)然也可以,如果用遞歸函數(shù)傳參數(shù)組的第二個(gè)元素做key,在遞歸到每個(gè)小區(qū)間只有一個(gè)數(shù)時(shí)就會(huì)出現(xiàn)錯(cuò)誤,因?yàn)闆]有key。

  1. 相遇節(jié)點(diǎn)為什么可以和key位置的互換呢?為什么相遇點(diǎn)的數(shù)值一定會(huì)比key位置的值小呢?

大家可以思考一下…

這就要看左右指針誰(shuí)先行了,如動(dòng)圖所示
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
如果是right先行,會(huì)發(fā)生什么呢?其實(shí)除了最后left和right相遇,其他的都是一樣,如果是right先行的話。
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
在循環(huán)判斷中右邊先行是一定要注意的。


還有嗎?
還有呢。
判斷條件怎么寫呢?判斷條件是大于交換還是大于等于(相對(duì)left而言)交換呢?如果是left位置的值大于key的話,就交換有可能會(huì)發(fā)生森么情況呢?
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
?看這個(gè)圖,在交換了組邊的6和右邊的2之后,right開始–,因?yàn)榕袛鄺l件是a[right]>key,所以當(dāng)right找到5時(shí),5=key,right就跳出了循環(huán)。同理,因?yàn)椴粷M足left下標(biāo)的值小于key,left跳出循環(huán),交換以left為下標(biāo)和以right為下標(biāo)的值,交換的還是5和5,再次循環(huán),判斷l(xiāng)eft<right成立,繼續(xù)循環(huán),left和right還在原來(lái)的位置,繼續(xù)交換,就會(huì)陷入死循環(huán),所以這里的判斷條件要寫為a[left]>=key,a[right]<=key,然后再進(jìn)行交換。
看動(dòng)圖
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
判斷條件也要注意,這種情況很難想到,所以有時(shí)運(yùn)行錯(cuò)誤不知道問題出在哪里
更改上邊問題,將判斷條件改為a[right] >= key和a[left] <= a[keyi]


?思考了這么多,終于找到相遇的點(diǎn)后交換key和a[left](a[left]都可以),就可以將這個(gè)數(shù)組分為兩個(gè)大小不同的的區(qū)間啦!
真的嗎?
?前邊初始化key=a[left],交換key和遍歷后的a[left],然而a[0]并沒有和找到的值進(jìn)行交換,而是key這個(gè)數(shù)和找到的相遇點(diǎn)交換,這個(gè)時(shí)候a[left]就沒有改變?yōu)橄嘤鳇c(diǎn)找到的小于key的值,如果一直遞歸下去,會(huì)變成什么樣呢?(上邊動(dòng)圖部分,key改成a[left]

再來(lái)看排序該組數(shù)據(jù)
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
如果是創(chuàng)建的key和找到的點(diǎn)進(jìn)行交換,就會(huì)出現(xiàn)數(shù)據(jù)污染,好多數(shù)據(jù)都被更改了,這樣的排序一定是錯(cuò)誤的,所以初始化條件可以這樣。

int keyi=left;
int key=a[keyi]

在判斷時(shí)利用key,在交換時(shí)交換a[keyi]和a[left]即可。


終于結(jié)束啦,然而并沒有。
?要注意的是在right和left找的過(guò)程中l(wèi)eft也不能小于right,比如該數(shù)組是有序的,在排序的過(guò)程中,right先行,但是一直沒有找到小于a[keyi]的值,直到到達(dá)left的位置再次減一,就會(huì)越界訪問。
完整代碼如下

//hoare版本
int PartSort1(int* a, int left, int right)
{
	//int midi=GetMidi(a,left,right)
	//Swap(&a[left[,&a[midi]);
	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]);
	}
	Swap(&a[keyi], &a[left]);
	return left;
}

是不是細(xì)節(jié)滿滿,一不小心就會(huì)出來(lái)好多bug。


挖坑法

同樣先看圖
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
挖坑法相較于Hoare大佬的方法,步驟更加清晰明了一點(diǎn),一步一步實(shí)現(xiàn)

上邊的動(dòng)圖表現(xiàn)得十分清晰,設(shè)置一個(gè)坑位,保存left位置的值,設(shè)置hole變量等于left,然后從右邊找小于a[keyi]即上圖key的值,填入坑中,這時(shí)將找到的值的位置right設(shè)置為坑,然后從左往右找大于a[keyi]的值填入hole,將left所在位置設(shè)置為hole,不斷循環(huán),直到left等于right,將之前保存的key,即a[keyi]填進(jìn)坑里,就完成了前邊相同的操作。

代碼如下,很容易理解

//挖坑法
int PartSort2(int* a, int left, int right)
{
	int key = a[left];
	//保存key值以后。左邊形成一個(gè)坑
	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;
}

最后返回right,left,hole都可以,反正最后他們?cè)谕粋€(gè)位置上。


雙指針法

快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
看幾遍動(dòng)圖,不管他們用什么方法進(jìn)行實(shí)現(xiàn),最終實(shí)現(xiàn)的效果都是相同的。雙指針法只要懂得了原理,還是很容易實(shí)現(xiàn)的。
雙指針法和前邊兩種方法略有不同,來(lái)體驗(yàn)一下。先看代碼后進(jìn)行講解

//前后指針法
int PartSort3(int* a, int left, int right)
{
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);

	int prev = left;
	int cur = prev + 1;

	int keyi = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi])
		{
			++prev;
			if (prev != cur)
			{
				Swap(&a[prev], &a[cur]);
			}
		}
		++cur;
	}
	Swap(&a[prev], &a[keyi]);
	return prev;
}

還是一樣選擇left位置的值作為key,設(shè)置兩個(gè)指針,一個(gè)指向數(shù)組最前邊的位置,一個(gè)在他的后邊,前后指針法因此得名。
具體思路:

?設(shè)置前后指針,后邊的指針只要不越界訪問,cur就一直往后走,如果prev的下一個(gè)位置不是cur,且cur找到小于key的值時(shí),就交換prev位置和cur位置的值。直到最后cur大于right結(jié)束循環(huán)。此時(shí)prev位置的值一定小于或等于key,因?yàn)閏ur在后邊查找的過(guò)程中最不濟(jì)的情況也就是一個(gè)也沒找到,prev和他自己換,照樣也不變,在找到第一個(gè)之前cur和prev一直一起移動(dòng)并一直在cur的后一位,當(dāng)cur找到大于key位置的坐標(biāo)后,prev不動(dòng),cur繼續(xù)移動(dòng),所以當(dāng)cur找到小于key的位置時(shí),prev的下一個(gè)位置一定是大于key的,可以放心交換。
直到最后cur走到數(shù)組盡頭,就如前邊所說(shuō),prev的下一位一定大于key,而prev位置的值一定小于等于key,交換a[prev]和a[keyi]的值,就可以將數(shù)組分成以prev位置為中心的兩組,左邊的值都小于等于key,右邊的值都大于key。

循環(huán)有一種更簡(jiǎn)單的方法表示,效果相同

while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[prev], &a[cur]);
		}
		++cur;
	}

&&操作符,當(dāng)前一條件為假,后一條件就不走,如果前一條件為真后,才會(huì)走后一條件,然后prev才會(huì)++,與上邊實(shí)現(xiàn)的效果相同。


遞歸函數(shù)

void QuickSort1(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort1(a, begin, end);
	QuickSort1(a, begin, keyi - 1);
	QuickSort1(a, keyi + 1, end);
}

begin和end是傳進(jìn)來(lái)的數(shù)組段的第一個(gè)位置和最后一個(gè)位置的下標(biāo)。要注意接下來(lái)的遞歸數(shù)組的區(qū)間還有結(jié)束判斷條件。
舉一個(gè)例子說(shuō)明遞歸流程。
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法

遞歸結(jié)束后,數(shù)組就變?yōu)橛行虻牧?。至于先往右遞歸還是先往左都可以,沒有區(qū)別。
上邊遞歸時(shí)用的Hoare大佬的版本,用挖坑法和雙指針法也都可以。

時(shí)間復(fù)雜度與空間復(fù)雜度

快排的時(shí)間復(fù)雜度為O(nlogN),空間復(fù)雜度為O(N),如果情況比較好的話為O(log2n),空間是可以復(fù)用的,一邊用完另一邊還可以用。
時(shí)間復(fù)雜度最壞的情況是若該數(shù)組為有序數(shù)組。最好情況就是一直取到中間位置。
最壞情況:
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
若數(shù)組數(shù)量為n,一次只排好了一個(gè)數(shù)據(jù),right第一次跑n次,第二次跑n-1次,則時(shí)間復(fù)雜度明顯為O(n^2)。
假使每次都能找到中間值,此時(shí)時(shí)間復(fù)雜度最低。2^h=n,所以n=log2 ^n。
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
最好情況時(shí)間復(fù)雜度為O(n
logn)。
至于空間復(fù)雜度從上邊的圖就可以看出,因?yàn)檫f歸開辟的空間是在棧上的,每次開辟的空間都可知小于n,故每次遞歸開辟空間為O(1),所以快排的時(shí)間復(fù)雜度為遞歸深度乘于O(1),最壞情況為O(N),最好的情況為O(logN)。

優(yōu)化

借助題目:排序數(shù)組
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
給一個(gè)數(shù)組,要求給他排序,要求很簡(jiǎn)單,卻只有50%的通過(guò)率,力扣標(biāo)記簡(jiǎn)單不一定簡(jiǎn)單,標(biāo)記中等那一定是有點(diǎn)難。
這題很顯然,普通的排序比如冒泡排序,插入排序,選擇排序是過(guò)不了的,我們剛剛學(xué)習(xí)了快排,何不嘗試一波。

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

 void QuickSort1(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	int keyi = PartSort1(a, begin, end);
	QuickSort1(a, begin, keyi - 1);
	QuickSort1(a, keyi + 1, end);
}
//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]);
	}
	Swap(&a[keyi], &a[left]);
	return left;
}
int* sortArray(int* nums, int numsSize, int* returnSize){
    QuickSort1(nums, 0, numsSize-1);
    *returnSize=numsSize;
    
    return nums;
}

快排解題的代碼如下
然而卻發(fā)現(xiàn)
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
?用hoare大佬的方法走一遍,發(fā)現(xiàn)時(shí)間復(fù)雜度在這組用例上為O(N^2),right一直向右移,不會(huì)分成左右兩組,一直遞歸n次,right向前的次數(shù)從n到1,這樣的話花費(fèi)的時(shí)間太多了,針對(duì)這種情況進(jìn)行優(yōu)化,就要介紹三數(shù)取中。

三數(shù)取中

如何防止上面最壞的情況發(fā)生?
只需要防止最左邊的數(shù)為該組數(shù)里最小的數(shù)即可。

?可以找出left位置的值和right位置的值及(left+right)/2數(shù)組中間下標(biāo)的值,找出他們?nèi)齻€(gè)中間大小的一個(gè),與a[left]進(jìn)行交換,這樣就可以防止left是數(shù)組中最小的元素這種情況的發(fā)生。

將三數(shù)取中抽離成函數(shù)

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

在每次partsort前搞來(lái)中間值的坐標(biāo),與left進(jìn)行交換

int midi=GetMidi(a,left,right)
Swap(&a[left[,&a[midi]);

加上三數(shù)取中再次運(yùn)行
然而。。。。
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
?再次出乎意料,如果是全部數(shù)字都一樣或者有大量重復(fù)數(shù)據(jù),right還是像剛才有序數(shù)組的情況一樣,right一直向左,時(shí)間復(fù)雜度為O(N^2),此時(shí)三數(shù)取中也沒用了,無(wú)論怎么取,取到的都是2。針對(duì)這種情況怎么辦?

三路分化

將hoare大佬的方法和雙指針法結(jié)合起來(lái),將數(shù)組分為三個(gè)部分,左邊小于key,中間的區(qū)段是等于key的,右邊的部分是大于key的。
設(shè)置三個(gè)指針,兩個(gè)指針用于位置的交換是數(shù)組劃分為三個(gè)區(qū)間,一個(gè)指針用于遍歷。
看思路
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
最后在left和right之間的部分就是等于5的部分,left之前皆小于key,right之后皆大于key。
總結(jié)下來(lái)只有以下三種情況

  1. cur的值小于key,交換cur的值和left的值,cur++,left++。
  2. cur的值等于key,直接++cur
  3. cur的值大于key,交換cur和right的值,cur不能動(dòng)。

代碼實(shí)現(xiàn)

void QuickSort1(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int begin = left;//記錄左右邊界
	int end = right;
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);

	int key = a[left];
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < key)//重點(diǎn)理解
		{
			Swap(&a[cur], &a[left]);
			++left;
			++cur;
		}
		else if (a[cur] > key)
		{
			Swap(&a[cur], &a[right]);
		}
		else
		{
			++cur;
		}
	}
	QuickSort1(a, begin, left-1);
	QuickSort1(a, right+1, end);
}

這次我們信心滿滿,如果數(shù)組的元素全部為2的話,遍歷一遍,right和left直接相遇,甚至只需要O(N)的時(shí)間就可以完成。
更改代碼后運(yùn)行。
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
?還是超出時(shí)間限制,這又是為什么呢?這道題對(duì)快速排序做了針對(duì)性的處理,普通的快排很難過(guò)這道題,他會(huì)檢測(cè)我們的三數(shù)取中,然后搞出對(duì)應(yīng)的一串?dāng)?shù)字,讓每次取出的數(shù)都是數(shù)組中倒數(shù)第二大,這樣的話時(shí)間復(fù)雜度還是很大。
解決方法:

?選取數(shù)組中任意位置的數(shù)和left位置和right作比較選出keyi,而不是一直取中間坐標(biāo)與他們相比較,用rand函數(shù)進(jìn)行操作,使我們選數(shù)位置沒有規(guī)律可循,這樣編譯器就不能根據(jù)我們所寫的代碼搞出針對(duì)性用例。

代碼如下:

int GetMidi(int* a, int left, int right)
{
	//int mid = (left + right) / 2;
	int mid =left+(rand()%(right-left));
	//left mid right
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}

void Swap(int* x, int* y)
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

void QuickSort1(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int begin = left;//記錄左右邊界
	int end = right;
	int midi = GetMidi(a, left, right);
	Swap(&a[left], &a[midi]);

	int key = a[left];
	int cur = left + 1;
	while (cur <= right)
	{
		if (a[cur] < key)
		{
			Swap(&a[cur], &a[left]);
			++left;
			++cur;
		}
		else if (a[cur] > key)
		{
			Swap(&a[cur], &a[right]);
			--right;
		}
		else
		{
			++cur;
		}
	}
	QuickSort1(a, begin, left-1);
	QuickSort1(a, right+1, end);
}
int* sortArray(int* nums, int numsSize, int* returnSize){
	srand(time(NULL));
    QuickSort1(nums, 0, numsSize-1);
    *returnSize=numsSize;
    
    return nums;
}

運(yùn)行后如圖
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
可以發(fā)現(xiàn)所用時(shí)間還是很長(zhǎng),這道題如果用堆排序和shell排序都是很好過(guò)的,放在這里是為了提升我們對(duì)快排的理解和掌握。

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

?在上邊顯示遞歸流程的圖中,我們可以看到遞歸的過(guò)程,類比樹的結(jié)構(gòu),樹的每一個(gè)節(jié)點(diǎn)都是一次遞歸,樹的最后一排的節(jié)點(diǎn)個(gè)數(shù)占全部節(jié)點(diǎn)的50%,倒數(shù)第二行占總個(gè)數(shù)的25%,如果分成的數(shù)組的量的變小到一定的個(gè)數(shù)的時(shí)候可以不再使用遞歸,而是選擇其他的方法進(jìn)行排序,可以減少大量的遞歸次數(shù)。
這種方法叫做小區(qū)間優(yōu)化,使用什么排序比較好呢?冒泡排序和選擇排序的時(shí)間復(fù)雜度為O(N2),選擇排序的時(shí)間復(fù)雜度最高為O(N2),可以使用選擇排序進(jìn)行小區(qū)間優(yōu)化,從而減少大量的遞歸次數(shù)
插入排序及小區(qū)間優(yōu)化后的代碼如下


//插入排序
void InsertSort(int* a, int n)
{
	//[0,end]有序,把end+1的位置插入到前序序列
	//控制[0,end+1]有序
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		int tmp = a[end + 1];
		while (end >= 0)
		{
			if (tmp < a[end])
			{
				a[end + 1] = a[end];
			}
			else
			{
				break;
			}-+
			--end;
		}
		a[end + 1] = tmp;
	}
}
//加上小區(qū)間優(yōu)化
void QuickSort2(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	if ((end - begin + 1) > 10)//如果一個(gè)數(shù)組的元素小于10,就進(jìn)行插入排序
	{
		int keyi = PartSort2(a, begin, end);
		QuickSort2(a, begin, keyi - 1);
		QuickSort2(a, keyi + 1, end);
	}
	else
	{
		InsertSort(a + begin, end - begin + 1);
	}
}

//加上小區(qū)間優(yōu)化
void QuickSort2(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	if ((end - begin + 1) > 10)
	{
		int keyi = PartSort2(a, begin, end);
		QuickSort2(a, begin, keyi - 1);
		QuickSort2(a, keyi + 1, end);
	}
	else
	{
		InsertSort(a + begin, end - begin + 1);
	}
}

快排非遞歸版
復(fù)習(xí)一下:它的基本思想是:通過(guò)一趟排序?qū)⒁判虻臄?shù)據(jù)分割成獨(dú)立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對(duì)這兩部分?jǐn)?shù)據(jù)分別進(jìn)行快速排序,整個(gè)排序過(guò)程可以遞歸 進(jìn)行,以此達(dá)到整個(gè)數(shù)據(jù)變成有序序列。
將一整個(gè)數(shù)組不斷細(xì)分,在不斷劃分的過(guò)程中進(jìn)行有序化,最終完成排序的功能。在前邊partsort函數(shù)中,我們只要通過(guò)某種方法得到分成一組一組的小數(shù)組段的left和right即可。
上邊的說(shuō)明遞歸的流程圖是直接全部展開的,實(shí)際上是一直向左劃分,直到分出的數(shù)組left和right相等,然后回到上步。
如圖所示
快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握,數(shù)據(jù)結(jié)構(gòu),leetcode,算法,數(shù)據(jù)結(jié)構(gòu),排序算法,推薦算法
結(jié)合代碼就很容易明白。
在非遞歸版本我們想和遞歸的步驟相同,如何得到上述遞歸過(guò)程中的left和right?這是問題之所在,而且在遞歸過(guò)程中向partsort1傳入不同的left和right。
可以借助棧先進(jìn)先出的功能來(lái)實(shí)現(xiàn),push起始的left和right,進(jìn)行第一次partsort,知道了left和right后,將他們?cè)賞op掉。

void QuickSortNonR(int* a, int begin, int end)
{
	ST st;
	STInit(&st);
	STPush(&st, end);
	STPush(&st, begin);
	while (!STEmpty(&st))
	{
		int left = STTop(&st);
		STPop(&st);
		int right = STTop(&st);
		STPop(&st);

		int keyi = PartSort1(a, left, right);
		if (keyi + 1 < right)
		{
			STPush(&st, right);
			STPush(&st, keyi + 1);
		}
		if (left < keyi - 1)
		{
			STPush(&st, keyi - 1);
			STPush(&st, left);
		}
	}
	STDestroy(&st);
}

快排講到這里就結(jié)束啦,如果你有耐心看完的話你一定會(huì)對(duì)快排的掌握有所提升噠。
加油!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-715933.html

到了這里,關(guān)于快排&超詳細(xì),Leetcode排序數(shù)組題目帶你升華掌握的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(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ù)組(一篇帶你掌握數(shù)組)

    數(shù)組(一篇帶你掌握數(shù)組)

    ? ? 在之前,我們想要存儲(chǔ)一個(gè)數(shù)據(jù)的時(shí)候可以創(chuàng)建變量,例如存儲(chǔ)一個(gè)整形的變量,我們使用int類型的變量來(lái)存儲(chǔ),那么如果存儲(chǔ)一組相同類型的數(shù)據(jù)呢?這時(shí)我們就引入了 數(shù)組 的概念。 目錄 一、一維數(shù)組的創(chuàng)建和初始化 1.1數(shù)組的創(chuàng)建 1.2 數(shù)組的初始化 1.3 一維數(shù)組的使

    2023年04月08日
    瀏覽(20)
  • 【排序算法】 快速排序(快排)!超詳細(xì)看這一篇就夠了”保姆級(jí)教學(xué)“

    【排序算法】 快速排序(快排)!超詳細(xì)看這一篇就夠了”保姆級(jí)教學(xué)“

    ?? 嶼小夏 : 個(gè)人主頁(yè) ??個(gè)人專欄 : 算法—排序篇 ?? 莫道桑榆晚,為霞尚滿天! 什么是快排?快排的速度到底有多快呢?它們的思想和實(shí)現(xiàn)是什么樣的? 本文會(huì)對(duì)這快速排序進(jìn)行詳解,絕對(duì)細(xì)致入微!讓你徹底搞懂快排! 英國(guó)計(jì)算機(jī)科學(xué)家Tony Hoare在1960年為了解決計(jì)算

    2024年02月05日
    瀏覽(22)
  • 帶你刷算法——數(shù)組/字符串的完全掌握(一)

    帶你刷算法——數(shù)組/字符串的完全掌握(一)

    「作者主頁(yè)」 :雪碧有白泡泡 「?jìng)€(gè)人網(wǎng)站」 :雪碧的個(gè)人網(wǎng)站 「推薦專欄」 : ★ java一站式服務(wù) ★ ★ 前端炫酷代碼分享 ★ ★ uniapp-從構(gòu)建到提升 ★ ★ 從0到英雄,vue成神之路 ★ ★ 解決算法,一個(gè)專欄就夠了 ★ ★ 架構(gòu)咱們從0說(shuō) ★ ★ 數(shù)據(jù)流通的精妙之道★ 給你兩個(gè)

    2024年02月13日
    瀏覽(24)
  • 代碼隨想錄額外題目| 數(shù)組03 ●34排序數(shù)組查首尾位置 ●922按奇偶排序數(shù)組II●35搜索插入位置

    代碼隨想錄額外題目| 數(shù)組03 ●34排序數(shù)組查首尾位置 ●922按奇偶排序數(shù)組II●35搜索插入位置

    #34排序數(shù)組查首尾位置 medium,我寫的:1 暴力 我寫的,做了個(gè)類似二分搜索的方法: 隨想錄:從兩頭都做類似二分搜索 #922?按奇偶排序數(shù)組II 我的解法,有點(diǎn)蠢: inplace解法: 把odd idx放的偶數(shù),給換到even idx放的奇數(shù) 注意j是從1開始,而且每輪i,j都是繼續(xù)增加不回去 空間表

    2024年02月15日
    瀏覽(27)
  • 手把手教你 ,帶你徹底掌握八大排序算法【數(shù)據(jù)結(jié)構(gòu)】

    手把手教你 ,帶你徹底掌握八大排序算法【數(shù)據(jù)結(jié)構(gòu)】

    直接插入排序是一種簡(jiǎn)單的插入排序法,其基本思想:是把待排序的記錄按其關(guān)鍵碼值的大小逐個(gè)插入到一個(gè)已經(jīng)排好序的有序序列中,直到所有的記錄插入完為止,得到一個(gè)新的有序序列 可以理解為一遍摸撲克牌,一邊進(jìn)行排序 在待排序的元素中,假設(shè)前面n-1(其中n=2)個(gè)數(shù)

    2024年02月02日
    瀏覽(106)
  • C語(yǔ)言題目 - 調(diào)用qsort函數(shù)對(duì)數(shù)組進(jìn)行排序

    如題 其實(shí)沒什么難的,只要嚴(yán)格按照 qsort 函數(shù)的參數(shù)來(lái)填充即可,這里要用到 函數(shù)指針 。 qsort 函數(shù)的原型如下: void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *)); 參數(shù)說(shuō)明: base :指向需要排序的數(shù)組的指針,數(shù)組中每個(gè)元素的大小應(yīng)為 size 字節(jié)。

    2024年02月11日
    瀏覽(21)
  • 【C語(yǔ)言初階】帶你輕松掌握指針基礎(chǔ)知識(shí)完結(jié)篇——野指針,指針運(yùn)算,指針和數(shù)組,二級(jí)指針

    【C語(yǔ)言初階】帶你輕松掌握指針基礎(chǔ)知識(shí)完結(jié)篇——野指針,指針運(yùn)算,指針和數(shù)組,二級(jí)指針

    君兮_的個(gè)人主頁(yè) 勤時(shí)當(dāng)勉勵(lì) 歲月不待人 C/C++ 游戲開發(fā) Hello,這里是君兮_,今天繼續(xù)給大家更新0基礎(chǔ)入門C語(yǔ)言的內(nèi)容,我們這次主要更新的依然是初階指針的基礎(chǔ)知識(shí) 廢話不多說(shuō)咱們直接開始吧!! 概念: 野指針就是指針指向的位置是不可知的(隨機(jī)的、不正確的、沒有

    2024年02月16日
    瀏覽(22)
  • 【LeetCode題目詳解】 977.有序數(shù)組的平方 209.長(zhǎng)度最小的子數(shù)組59.螺旋矩陣II day2

    【LeetCode題目詳解】 977.有序數(shù)組的平方 209.長(zhǎng)度最小的子數(shù)組59.螺旋矩陣II day2

    看完這個(gè)題目第一想法就是直接暴力解決,直接將全部平方然后進(jìn)行排序。比如快排。 代碼如下: 時(shí)間復(fù)雜度是 O(nlogn)或者說(shuō)【O(n + nlogn)】,括號(hào)里面這個(gè)是為了比較接下來(lái)的方法。 然后看了代碼隨想錄的視頻學(xué)習(xí)了用雙指針來(lái)寫這道題的方法(說(shuō)實(shí)話不看視頻真沒想到可

    2024年02月13日
    瀏覽(25)
  • leetcode 912.排序數(shù)組

    leetcode 912.排序數(shù)組

    ?? leetcode鏈接:排序數(shù)組 思路: 此題如果使用冒泡插入選擇這些時(shí)間復(fù)雜度 O ( N 2 ) O(N^2) O ( N 2 ) 的算法會(huì)超時(shí),使用快排 + 優(yōu)化也過(guò)不去,因?yàn)槔锩嬗幸粋€(gè)測(cè)試用例全是 2 即使加了三數(shù)取中也會(huì)是 O ( N 2 ) O(N^2) O ( N 2 ) ,以下實(shí)現(xiàn)主要使用歸并排序。 如果需要其他排序的可

    2024年02月14日
    瀏覽(17)
  • leetcode 33.搜索旋轉(zhuǎn)排序數(shù)組

    leetcode 33.搜索旋轉(zhuǎn)排序數(shù)組

    ?? leetcode鏈接:搜索旋轉(zhuǎn)排序數(shù)組 ps: 本題是二分查找的變形,旋轉(zhuǎn)排序數(shù)組之后其實(shí)會(huì) 形成兩個(gè)有序的區(qū)間 。算出平均下標(biāo)先判斷是否與 target 相等,因?yàn)檫@樣可以減少代碼的冗余。如果前者不成立則使用平均下標(biāo)元素 midIndex 與 數(shù)組最后一個(gè)元素判斷大?。ㄒ?yàn)槲覀兛?/p>

    2024年02月13日
    瀏覽(18)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包