快速排序算法_基于位运算的快速排序算法
前言
如果你準(zhǔn)備看這篇文章,我就當(dāng)你是懂快速排序算法原理的。
下面是我在2018年10月3日想到的基于二進(jìn)制位運(yùn)算對(duì)正整數(shù)進(jìn)行的一種快速排序算法,目前的代碼只能對(duì)正整數(shù)進(jìn)行有效的排序,當(dāng)然,稍微修改一下就可以支持負(fù)數(shù)的排序了。后面會(huì)給出我用Julia語言實(shí)現(xiàn)的沒有經(jīng)過優(yōu)化的代碼和一些測(cè)試結(jié)果。如果您對(duì)此能有什么改進(jìn)或者能幫嗎優(yōu)化一下代碼,請(qǐng)告知我一下,我會(huì)非常感謝您。下面是此算法的思想和原理。
2是一個(gè)神奇的數(shù)字,可以用來做很多東西,二叉樹,二分搜索,快速冪算法以及很多分治算法都是在2這個(gè)神奇的數(shù)字的幫助才能達(dá)到他們的優(yōu)雅和高效率。
利用二進(jìn)制,我們也可以進(jìn)行快速排序。
如果要對(duì)一列數(shù)字進(jìn)行排序,你會(huì)怎樣去做呢?學(xué)過快速排序的同學(xué)應(yīng)該會(huì)立即想到快速排序。先選一個(gè)數(shù)字,然后從兩邊開始,分別把小于它的數(shù)字交換到它的左邊,把大于它的數(shù)字交換到它右邊,完成這一過程,其右邊的數(shù)字全體大于等于它,左邊的數(shù)字全部小于等于它。然后分別對(duì)其左邊和右邊遞歸地運(yùn)用此方法(快速排序)進(jìn)行排序,當(dāng)排序的長(zhǎng)度小于2時(shí),遞歸過程完成了,這一列數(shù)字也就變成有序的了。
這種最基本的快排算法是不是也散發(fā)著二分的思想?如果它再二一點(diǎn)會(huì)怎樣?那就是我下面想要談?wù)摰幕诙M(jìn)制的快速排序算法了。哈哈,我給它加入了二進(jìn)制。
其實(shí)除開二進(jìn)制的位運(yùn)算,這個(gè)算法跟快速排序算法基本上是一樣的,所以我暫且稱之為BQuickSort(Binary or Bit)。
算法的基本思想
對(duì)每一個(gè)二進(jìn)制位,把該位是1的數(shù)字劃分到右邊,是0的劃分到左邊。高位優(yōu)先,通過交換來進(jìn)行。
實(shí)現(xiàn)
在算法的實(shí)現(xiàn)上我采用了按位與運(yùn)算,先用
的時(shí)間復(fù)雜度求出其中最大的數(shù)字,然后對(duì)最大的數(shù)字進(jìn)行一次簡(jiǎn)單的flp2運(yùn)算(把其二進(jìn)制最高位之外的bit全部置0,這里的最高位并非64或32等,而是左邊第一個(gè)不為0的bit),獲得一個(gè)為2的n次冪的整數(shù)變量,我們暫且把它命名為shift。每一次所做的就是用這個(gè)shift與一個(gè)要排序的區(qū)間段進(jìn)行按位與運(yùn)算運(yùn)算,將右邊運(yùn)算結(jié)果為0的數(shù)字同左邊運(yùn)算結(jié)果不為0的數(shù)字進(jìn)行交換,并返回交換之后的分割位置。然后遞歸地將同樣的算法運(yùn)用于分割位置的左邊和右邊,只需將shift右移一位即可。遞歸結(jié)束的條件是區(qū)間寬度小于1或者shift等于0。
對(duì)于一次部分排序,我寫了兩個(gè)函數(shù),一個(gè)像快速排序那樣從兩邊向中間行進(jìn),是不穩(wěn)定的算法,另一個(gè)只從左邊行進(jìn),應(yīng)該是穩(wěn)定的算法。
演示
接下來我將演示對(duì)下列所示的整數(shù)序列進(jìn)行的BQuickSort。
在這里最大的數(shù)字是10(二進(jìn)制為1010),通過對(duì)10進(jìn)行flp2運(yùn)算,我們將獲得一個(gè)數(shù)字8(二進(jìn)制為1000)。
將shift(這里為8)同每一個(gè)數(shù)字進(jìn)行按位與運(yùn)算并把運(yùn)算結(jié)果不為0的數(shù)字交換到右邊,最后我們返回一個(gè)整數(shù)索引10,它記錄了一次排序之后的分割線(分割線左邊的數(shù)字都小于8,同8進(jìn)行按位與運(yùn)算為0;右邊的數(shù)字都大于等于8,同8進(jìn)行按位與運(yùn)算不為0)。
鑒于分割線及其右邊的數(shù)字都大于左邊的數(shù)字,下面值演示對(duì)左邊的數(shù)字進(jìn)行的排序,同樣的方法適用于右邊。
將shift右移一位(結(jié)果為4),然后與分割線左邊的數(shù)字(前9個(gè))進(jìn)行與上面一樣的部分排序操作,我們將得到下面這張圖所示的序列。
再對(duì)前3個(gè)數(shù)字進(jìn)行shift為2的部分排序,1將被交換到左邊,此時(shí)前3個(gè)數(shù)已經(jīng)有序,但是遞歸其實(shí)尚未結(jié)束,下一次遞歸是shift為1的一次部分排序。繼續(xù)對(duì)第4~9個(gè)數(shù)字執(zhí)行同樣的部分排序,最后我們將會(huì)得到前九個(gè)數(shù)字的有序排列。之后再對(duì)第10~12個(gè)數(shù)字進(jìn)行同樣的部分排序,我們就會(huì)得到一個(gè)所有數(shù)字的有序排列。
更新:以前隨便寫的文章,用語有點(diǎn)輕佻。我以前以為利用被注釋了的那個(gè)bqpartition函數(shù),寫出來的快速排序是穩(wěn)定的,現(xiàn)在我不這么認(rèn)為。我現(xiàn)在認(rèn)為它是不穩(wěn)定的,形式化證明應(yīng)該不難,用計(jì)算機(jī)也很好驗(yàn)證,用那個(gè)bqpartition來做基數(shù)排序,以0,1為基數(shù),排序結(jié)果不正確,所以它是不穩(wěn)定的。其次,可以輕易舉出這么一個(gè)例子:對(duì)于這樣的序列[1, 1, 2],一次bqpartition將會(huì)得到這樣的結(jié)果,[2, 1, 1],兩個(gè)1的位置被交換了,所以它是不穩(wěn)定的。沒有被注釋的那個(gè)bqpartition要快一點(diǎn),因?yàn)楸蛔⑨尩袅说哪莻€(gè)bqpartition對(duì)于一個(gè)數(shù),可能要交換不止一次才能到達(dá)最后位置。(這篇文章的代碼,以前是用Julia語言實(shí)現(xiàn)的,沒有改變語義,僅僅改變語法,Julia實(shí)現(xiàn)的比C語言實(shí)現(xiàn)(未經(jīng)優(yōu)化)的略快一些,現(xiàn)在為了讀者方便,改用C語言實(shí)現(xiàn)的。除了這個(gè)引用塊里面的更新說明和代碼更改,其他一切保持原樣(縱有不足之處))。
算法實(shí)現(xiàn)代碼
BqSort排序算法的C語言實(shí)現(xiàn)
// bqsort.c各種排序算法的測(cè)試代碼及測(cè)試結(jié)果(經(jīng)過gcc -O3編譯優(yōu)化)見
各種排序算法效率簡(jiǎn)單比較?github.com。可見,對(duì)于正整數(shù)的排序,BqSort的速度基本上算是最快的了,尤其是有大量重復(fù)數(shù)字的時(shí)候,雖然這時(shí)Quick3Way(三路快排)也很快。不過BqSort可以保證O(nlogn)的最壞情況,而三路快排很容易會(huì)陷入O(N^2)的時(shí)間復(fù)雜度,比如N個(gè)不同的數(shù)正序或逆序排列。
。
總結(jié)
以上是生活随笔為你收集整理的快速排序算法_基于位运算的快速排序算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓虚拟机_安卓虚拟机(*New*)v1
- 下一篇: 学电脑从新手到高手_小白如何学手绘插画?