【python算法系列二】快速排序算法
快速排序的思想是:取數(shù)組中的一個(gè)數(shù)作為基準(zhǔn)值,把所有小于基準(zhǔn)值的數(shù)都放在它的一側(cè),再把所有大于基準(zhǔn)值的數(shù)都放在它的另一側(cè)。隨后,對(duì)基準(zhǔn)值左右兩側(cè)的數(shù)組分別進(jìn)行快速排序。由此可以看出,快速排序的整個(gè)排序過(guò)程也是遞歸進(jìn)行的。
快速排序的平均時(shí)間復(fù)雜度是 O(nlgn),最好情況下的時(shí)間復(fù)雜度是 O(nlgn)。最壞情況下,快速排序的時(shí)間復(fù)雜度可能退化成 O(n2),但這種情況很少見(jiàn)。它的空間復(fù)雜度是 O(nlgn)。它是一個(gè)不穩(wěn)定的排序算法。如果使用得當(dāng),快速排序的速度可以達(dá)到歸并排序和堆排序的數(shù)倍,所以快速排序是一種極其常用的算法。
以升序排序?yàn)槔?#xff0c;快速排序的流程如下:
圖 1:快速排序
一般情況下,我們?nèi)?shù)組的第一個(gè)數(shù)作為基準(zhǔn)進(jìn)行快速排序。在第一步中,基準(zhǔn)數(shù)為 5。可以看出,在第二行的數(shù)組中,比 5 小的元素:3、4、1、2,都被置于 5 的左側(cè),而比 5 大的元素則被置于 5 的右側(cè)。這時(shí),元素 5 在有序數(shù)組中的位置就確定了。
第三行中,我們?cè)偃∽笥覂蓚€(gè)無(wú)序數(shù)組的第一個(gè)數(shù) 3 和 6,分別作為它們的基準(zhǔn)數(shù),然后再次對(duì)數(shù)組進(jìn)行分拆。分拆結(jié)束之后,3 和 6 在有序數(shù)組中的位置也確定了。
接下來(lái),繼續(xù)處理分拆出來(lái)的 4 個(gè)子數(shù)組:[1,2]、[4]、[]、[8,7]。其中,一個(gè)子數(shù)組只剩一個(gè)數(shù),一個(gè)為空。這意味著 [4] 與 [] 已經(jīng)完成了對(duì)自己的快速排序。而其他的兩個(gè)子數(shù)組則需繼續(xù)處理。全部處理完畢后,我們將得到一個(gè)完整的有序數(shù)組。
可以看出,快速排序也是通過(guò)這樣的分治思想來(lái)排序的。關(guān)于它的分治思想我們之后會(huì)繼續(xù)講解。?
快速排序代碼(基礎(chǔ)版)
nums = [5,3,6,4,1,2,8,7] def QuickSort(num):if len(num) <= 1: #邊界條件return numkey = num[0] #取數(shù)組的第一個(gè)數(shù)為基準(zhǔn)數(shù)llist,rlist,mlist = [],[],[key] #定義空列表,分別存儲(chǔ)小于/大于/等于基準(zhǔn)數(shù)的元素for i in range(1,len(num)): #遍歷數(shù)組,把元素歸類(lèi)到3個(gè)列表中if num[i] > key:rlist.append(num[i])elif num[i] < key:llist.append(num[i])else:mlist.append(num[i])return QuickSort(llist)+mlist+QuickSort(rlist) #對(duì)左右子列表快排,拼接3個(gè)列表并返回 print(QuickSort(nums))運(yùn)行程序,輸出結(jié)果為:
[1,2,3,4,5,6,7,8]
在 QuickSort( ) 函數(shù)中,首先是邊界條件:如果傳入函數(shù)的列表長(zhǎng)度小于等于 1,那么這一段列表必定是有序的,可以直接返回。如果不滿足邊界條件,則繼續(xù)執(zhí)行函數(shù)。先用 key 存儲(chǔ)基準(zhǔn)值,再定義 3 個(gè)列表存儲(chǔ)小于基準(zhǔn)數(shù)的元素 llist,大于基準(zhǔn)數(shù)的元素 rlist 和等于基準(zhǔn)數(shù)的元素 mlist。由于接下來(lái) for 循環(huán)的范圍不包括列表中的第一個(gè)數(shù),所以對(duì) mlist 初始化時(shí),多加一個(gè)初始元素 key。
接下來(lái)的 for 循環(huán)把數(shù)組內(nèi)的元素分別歸入 3 個(gè)列表中。隨后,再次調(diào)用 QuickSort( ) 函數(shù),對(duì) llist 和 rlist 進(jìn)行排序。這樣,llist 和 rlist 就是有序的了,而 mlist 內(nèi)的元素剛好處于它們中間的連接部分。所以,排序完成后,把 llist、mlist、rlist 按順序拼接到一起并輸出。
這是實(shí)現(xiàn)快速排序的一種方式。但是,這樣實(shí)現(xiàn)快速排序需要額外開(kāi)辟空間給用于歸類(lèi)的列表。并且,相似的思路應(yīng)用于其他的編程語(yǔ)言時(shí)效率較低。那么,該如何優(yōu)化這個(gè)算法,使得數(shù)組可以原地排序呢?
我們需要優(yōu)化的是把基準(zhǔn)值移動(dòng)到正確位置的那一部分代碼。具體的移動(dòng)流程如下:
我們用一個(gè)變量存儲(chǔ)基準(zhǔn)值。然后,再使用兩個(gè)指針,一個(gè)從左往右遍歷,一個(gè)從右往左遍歷。開(kāi)始遍歷時(shí),可以把基準(zhǔn)值在數(shù)組中的位置,也就是第一個(gè)元素,視作一個(gè)沒(méi)有元素的空位。
1) 如圖 2 所示,移動(dòng)右邊的指針,一直到指針指向的元素小于基準(zhǔn)值為止。
圖 2:優(yōu)化快速排序第一步
2) 如圖 3 所示,把右邊的指針指向的值 2 賦給左邊的指針指向的位置。這時(shí)候,原來(lái) 2 所在的位置實(shí)際上是空出來(lái)的空位,空位在圖 2 中用淺色字體表示。
圖 3:優(yōu)化快速排序第二步
?3) 如圖 4 所示,移動(dòng)左邊的指針,等到它指向了一個(gè)大于等于基準(zhǔn)值的數(shù)再停下。類(lèi)似地,把左邊的指針指向的值賦給右邊的指針指向的位置。左邊指針指向的位置成為空位。
圖 4:優(yōu)化快速排序第三步
?4) 重復(fù)以上步驟,不斷地交替移動(dòng)左邊的指針和右邊的指針,并賦值,如圖 5 所示。
圖 5:重復(fù)相似步驟
?5) 如圖 6 所示,當(dāng)左指針和右指針重合時(shí),所有必要的移動(dòng)都已經(jīng)完成。左指針和右指針共同指向的位置就是基準(zhǔn)值在有序數(shù)組中的位置。它的值大于它左側(cè)的所有元素,并小于等于它右側(cè)的所有元素(如果有相等的元素出現(xiàn))。剩余的步驟為遞歸地排序左右子數(shù)組,直到全部數(shù)組排序完畢。
?圖 6:當(dāng)前范圍內(nèi)移動(dòng)完成
快速排序代碼(“原地”版):
nums = [5,3,6,4,1,2,8,7] def QSort(left,right): #子數(shù)組第一個(gè)元素和最后一個(gè)元素在原數(shù)組中的位置if(left >= right): #邊界條件returnl,r,key = left,right,nums[left] #初始化左指針,右指針和基準(zhǔn)值while(l < r): #調(diào)整元素位置while l < r and nums[r] >= key:r -= 1nums[l] = nums[r]while l < r and nums[l] < key:l += 1nums[r] = nums[l]nums[l] = key #把基準(zhǔn)值賦給左指針和右指針共同指向的位置QSort(left,l-1) #左側(cè)數(shù)組排序QSort(l+1,right) #右側(cè)數(shù)組排序 QSort(0,len(nums)-1) print(nums)運(yùn)行程序,輸出結(jié)果為:
[1,2,3,4,5,6,7,8]
這段代碼沒(méi)有采用直接將數(shù)組傳入函數(shù)的方法,而是把子數(shù)組第一個(gè)和最后一個(gè)元素的位置傳入函數(shù)中,從而確定循環(huán)范圍。邊界條件仍然不變:只有當(dāng)子數(shù)組的長(zhǎng)度(right-left+1)大于 1 時(shí)才繼續(xù)遞歸。左指針 l 和右指針 r 初始化為第一個(gè)元素的下標(biāo)和最后一個(gè)元素的下標(biāo),變量 key 用于存儲(chǔ)基準(zhǔn)值。
隨后,while 循環(huán)就實(shí)現(xiàn)了前面圖 2 ~圖 6 展示的調(diào)整元素位置的過(guò)程。最后把兩個(gè)子數(shù)組中間的位置賦值為 key,再對(duì)兩個(gè)子數(shù)組分別排序。在函數(shù)外部,先調(diào)用 QSort( ) 函數(shù)對(duì) nums 數(shù)組進(jìn)行排序,再輸出 nums 數(shù)組。?
?
【python算法系列一】冒泡排序算法采用重復(fù)遍歷數(shù)組并依次比較相鄰元素的方法來(lái)排序。由于在冒泡算法進(jìn)行排序的過(guò)程中,最大數(shù)/最小數(shù)會(huì)慢慢“浮”到數(shù)組的末尾,所以算法由此命名。https://blog.csdn.net/m0_70372647/article/details/124736577【python算法系列二】快速排序算法快速排序的思想是:取數(shù)組中的一個(gè)數(shù)作為基準(zhǔn)值,把所有小于基準(zhǔn)值的數(shù)都放在它的一側(cè),再把所有大于基準(zhǔn)值的數(shù)都放在它的另一側(cè)。隨后,對(duì)基準(zhǔn)值左右兩側(cè)的數(shù)組分別進(jìn)行快速排序。由此可以看出,快速排序的整個(gè)排序過(guò)程也是遞歸進(jìn)行的。快速排序的平均時(shí)間復(fù)雜度是 O(nlgn),最好情況下的時(shí)間復(fù)雜度是 O(nlgn)。最壞情況下,快速排序的時(shí)間復(fù)雜度可能退化成 O(n2),但這種情況很少見(jiàn)。它的空間復(fù)雜度是 O(nlgn)。它是一個(gè)不穩(wěn)定的排序算法。如果使用得當(dāng),快速排序的速度可以達(dá)到歸并排序和堆排序的數(shù)倍,所以.https://blog.csdn.net/m0_70372647/article/details/124758205【python算法系列三】 希爾排序算法希爾排序,又叫“縮小增量排序”,是對(duì)插入排序進(jìn)行優(yōu)化后產(chǎn)生的一種排序算法。它的執(zhí)行思路是:把數(shù)組內(nèi)的元素按下標(biāo)增量分組,對(duì)每一組元素進(jìn)行插入排序后,縮小增量并重復(fù)之前的步驟,直到增量到達(dá) 1。一般來(lái)說(shuō),希爾排序的時(shí)間復(fù)雜度為 O(n1.3)~O(n2),它視增量大小而定。希爾排序的空間復(fù)雜度是 O(1),它是一個(gè)不穩(wěn)定的排序算法。進(jìn)行希爾排序時(shí),元素一次移動(dòng)可能跨越多個(gè)元素,從而可能抵消多次移動(dòng),提高了效率。下面是使用(數(shù)組長(zhǎng)度/2)作為初始增量的升序希爾排序,每一輪排序過(guò)后,增量都縮小一半。1) 如https://blog.csdn.net/m0_70372647/article/details/124808637【python算法系列四】堆排序算法堆排序,就像它的名字一樣,利用了堆的特性來(lái)進(jìn)行排序。實(shí)現(xiàn)堆排序的思路是,把數(shù)組構(gòu)建成一棵二叉樹(shù),并隨著每次堆的變化更新堆頂?shù)淖畲?最小值。堆排序的時(shí)間復(fù)雜度在所有情況下都是 O(nlgn),它也是一個(gè)不穩(wěn)定的算法。在開(kāi)始編寫(xiě)堆排序的程序之前,我們首先要了解“堆”的概念。堆是一種數(shù)據(jù)結(jié)構(gòu),它是一種特殊的完全二叉樹(shù):如果這個(gè)堆是一個(gè)大頂堆(最大的元素在堆頂),那么每個(gè)節(jié)點(diǎn)上的元素都應(yīng)該比它的子節(jié)點(diǎn)上的元素要大,最大的元素在根節(jié)點(diǎn)上;反之,如果是小頂堆,那么每個(gè)節(jié)點(diǎn)上的元素都應(yīng)該比它的子節(jié)點(diǎn)小,最小的元素在根節(jié)https://blog.csdn.net/m0_70372647/article/details/124870580【python算法系列五】桶排序算法由于桶排序算法把每個(gè)數(shù)都放到合適的“桶”里進(jìn)行排序,因此而得名。桶排序的算法原理可以理解為創(chuàng)建一個(gè)新的數(shù)組,把數(shù)依次放入合適的桶內(nèi),再按一定順序輸出桶。當(dāng)每個(gè)桶的數(shù)據(jù)范圍為 1 且數(shù)據(jù)皆為整數(shù)時(shí),桶排序的時(shí)間復(fù)雜度在所有情況下都是 O(n),因?yàn)樗且粋€(gè)線性的排序算法。但是,它的空間需求要視排序數(shù)據(jù)的范圍而定,所以極有可能浪費(fèi)很多空間。假設(shè)我們有 10 個(gè)整數(shù) [1,1,3,19,35,49,50,5,10,16],它們的范圍在 1~50。如圖 1 所示,我們建立 50 個(gè)存放數(shù)據(jù)的桶。圖 1:https://blog.csdn.net/m0_70372647/article/details/124871084【python算法系列六】選擇排序算法選擇排序表示從無(wú)序的數(shù)組中,每次選擇最小或最大的數(shù)據(jù),從無(wú)序數(shù)組中放到有序數(shù)組的末尾,以達(dá)到排序的效果。選擇排序的平均時(shí)間復(fù)雜度是O(n2),最好情況下的時(shí)間復(fù)雜度和最壞情況下的時(shí)間復(fù)雜度都是 O(n2)。另外,它是一個(gè)不穩(wěn)定的排序算法。選擇排序的過(guò)程如下;1) 如圖 1 所示,我們?nèi)砸赃f增排序的算法為例,先遍歷未排序的數(shù)組,找到最小的元素。然后,把最小的元素從未排序的數(shù)組中刪除,添加到有序數(shù)組的末尾。因?yàn)樽钚〉脑厥?1,所以 1 被添加到仍為空的有序數(shù)組末尾。圖 1:選擇并放置第一個(gè)元素https://blog.csdn.net/m0_70372647/article/details/124897318【python算法系列七】插入排序算法排序通常指把毫無(wú)規(guī)律的數(shù)據(jù),按照一種特定的規(guī)律,整理成有序排列的狀態(tài)。一般情況下,排序算法按照關(guān)鍵字的大小,以從小到大或從大到小的順序?qū)?shù)據(jù)排列。排序算法是最基礎(chǔ)也是最重要的算法之一,在處理大量數(shù)據(jù)時(shí),使用一個(gè)優(yōu)秀的排序算法可以節(jié)省大量時(shí)間和空間。因?yàn)椴煌呐判蛩惴〒碛胁煌奶攸c(diǎn),所以我們應(yīng)根據(jù)情況選擇合適的排序算法。初級(jí)排序算法是指幾種較為基礎(chǔ)且容易理解的排序算法。初級(jí)排序算法包括插入排序、選擇排序和冒泡排序 3 種。雖然它們的效率相對(duì)于高級(jí)排序算法偏低,但是在了解初級(jí)排序算法之后,再去學(xué)習(xí)相對(duì)復(fù)https://blog.csdn.net/m0_70372647/article/details/124907803【python算法系列八】歸并排序算法相比起初級(jí)排序算法,高級(jí)排序算法往往有更加復(fù)雜的邏輯,但也會(huì)有更高的時(shí)間或空間效率。其中有些高級(jí)排序算法是由初級(jí)排序算法優(yōu)化而來(lái)的。在處理大量數(shù)據(jù)時(shí),高級(jí)排序算法的一般更加常用。本節(jié)教程介紹的第一種高級(jí)排序算法是歸并排序。“歸并”一詞,意為“合并”。顧名思義,歸并排序算法就是一個(gè)先把數(shù)列拆分為子數(shù)列,對(duì)子數(shù)列進(jìn)行排序后,再把有序的子數(shù)列合并為完整的有序數(shù)列的算法。它實(shí)際上采用了分治的思想,之后我們會(huì)深度講解分治思想。歸并排序的平均時(shí)間復(fù)雜度是 O(nlgn),最好情況下的時(shí)間復(fù)雜度是 O(nlg.https://blog.csdn.net/m0_70372647/article/details/124908304【Python算法系列九】 順序查找算法定義查找的定義為:在一個(gè)數(shù)據(jù)元素集合中,通過(guò)一定的方法確定與給定關(guān)鍵字相同的數(shù)據(jù)元素是否存在于集合中。一般來(lái)說(shuō),如果查找成功,程序會(huì)返回?cái)?shù)據(jù)的位置或相關(guān)信息;如果查找失敗,則返回相應(yīng)的提示。查找的方法可以分為兩種:比較查找法與計(jì)算式查找法。比較查找法基于兩種數(shù)據(jù)結(jié)構(gòu):線性表和樹(shù)。查找的對(duì)象(一般是由同一類(lèi)型的數(shù)據(jù)元素/記錄構(gòu)成的集合)又可以被稱(chēng)為查找表。查找還分為靜態(tài)查找和動(dòng)態(tài)查找。對(duì)查找表進(jìn)行靜態(tài)查找時(shí),程序只進(jìn)行查找并返回信息;進(jìn)行動(dòng)態(tài)查找時(shí),在靜態(tài)查找的基礎(chǔ)上,還增加了增刪查找表中.https://blog.csdn.net/m0_70372647/article/details/124931956【Python算法系列十】二分查找算法二分查找,也叫折半查找,是一種適用于順序存儲(chǔ)結(jié)構(gòu)的查找方法。它是一種效率較高的查找方法,時(shí)間復(fù)雜度為 O(lgn),但它僅能用于有序表中。也就是說(shuō),表中的元素需按關(guān)鍵字大小有序排列。二分查找用左右兩個(gè)指針來(lái)標(biāo)注查找范圍。程序開(kāi)始時(shí),查找范圍是整個(gè)線性表,左指針指向第一個(gè)元素,右指針指向最后一個(gè)元素;每一次循環(huán)過(guò)后,查找范圍都縮小為原先的一半,直到左右指針重疊或者左指針處于右指針的右側(cè)。因?yàn)槊看慰s小一半的范圍,所以可以得出二分查找的時(shí)間復(fù)雜度為 O(lgn)。...https://blog.csdn.net/m0_70372647/article/details/124936085
【Python算法系列十一】二叉樹(shù)的3種遍歷方式二叉樹(shù)的遍歷是指從根結(jié)點(diǎn)出發(fā),按照某種次序依次訪問(wèn)二叉樹(shù)中所有結(jié)點(diǎn),使得每個(gè)結(jié)點(diǎn)被訪問(wèn)一次且僅被訪問(wèn)一次。遍歷二叉樹(shù)的方法主要分 3 種:先序遍歷、中序遍歷和后序遍歷:先序遍歷指最先遍歷節(jié)點(diǎn)本身,再遍歷節(jié)點(diǎn)的左子樹(shù),最后遍歷右子樹(shù)的遍歷方法;中序遍歷指最先遍歷節(jié)點(diǎn)的左子樹(shù),再遍歷節(jié)點(diǎn)本身,最后遍歷右子樹(shù)的遍歷方法;后序遍歷指最先遍歷節(jié)點(diǎn)的左子樹(shù),再遍歷右子樹(shù),最后遍歷節(jié)點(diǎn)本身的一種遍歷方法。在圖 1 中,L 是左子樹(shù),R 是右子樹(shù),D 當(dāng)前節(jié)點(diǎn)。如果用這三個(gè)字母來(lái)表示 3 種遍歷.https://blog.csdn.net/m0_70372647/article/details/124989895
總結(jié)
以上是生活随笔為你收集整理的【python算法系列二】快速排序算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于mxnet的Regression问题
- 下一篇: python基础案例教程课后答案_Pyt