第十四章 Caché 算法與數(shù)據(jù)結(jié)構(gòu) 快速排序

第十四章 Caché 算法與數(shù)據(jù)結(jié)構(gòu) 快速排序

定義

  • 同冒泡排序一樣蹂匹,快速排序也屬于交換排序次洼,通過元素之間的比較和交換位置來達(dá)到排序的目的戚揭。
  • 不同的是雕旨,冒泡排序在每一輪中只把1個元素冒泡到數(shù)列的一端搓译,而快速排序則在每一輪挑選一個基準(zhǔn)元素悲柱,并讓其他比它大的元素移動到數(shù)列一邊,比它小的元素移動到數(shù)列的另一邊些己,從而把數(shù)列拆解成兩個部分豌鸡。
  • 這種思路就叫作分治法。
image.png

流程

image.png
  • 如圖所示段标,在分治法的思想下涯冠,原數(shù)列在每一輪都被拆分成兩部分,每一部分在下一輪又分別被拆分成兩部分逼庞,直到不可再分為止蛇更。
  • 每一輪的比較和交換,需要把數(shù)組全部元素都遍歷一遍,時間復(fù)雜度是O(n)派任。假如元素個數(shù)是n砸逊,那么平均情況下需要logn輪,因此快速排序算法總體的平均時間復(fù)雜度是O(nlogn)掌逛。

選擇基準(zhǔn)元素

  • 基準(zhǔn)元素师逸,英文是pivot,在分治過程中颤诀,以基準(zhǔn)元素為中心字旭,把其他元素移動到它的左右兩邊对湃。
  • 最簡單的方式是選擇數(shù)列的第1個元素崖叫。
  • 加入有原本逆數(shù)序列
    • 在這種極端情況下,快速排序需要進(jìn)行n輪拍柒,時間復(fù)雜度退化成了O(n2)心傀。
image.png
  • 我們隨機挑選一個基準(zhǔn)元素
image.png
  • 這樣一來,即使在數(shù)列完全逆序的情況下拆讯,也可以有效地將數(shù)列分成兩部分脂男。
  • 所以,雖然快速排序的平均時間復(fù)雜度是O(nlogn)种呐,但最壞情況下的時間復(fù)雜度是O(n2)宰翅。

元素的交換(遞歸實現(xiàn))

雙邊循環(huán)

  • 首先,選定基準(zhǔn)元素pivot爽室,并且設(shè)置兩個指針left和right汁讼,指向數(shù)列的最左和最右兩個元素。


    image.png
  • 接下來進(jìn)行第1次循環(huán)阔墩,從right指針開始嘿架,讓指針?biāo)赶虻脑睾突鶞?zhǔn)元素做比較。如果大于或等于pivot啸箫,則指針向左移動耸彪;如果小于pivot,則right指針停止移動忘苛,切換到left指針蝉娜。


    image.png

完整實例

快速排序類

Class PHA.YX.Arithmetic.QuickSort Extends %RegisteredObject
{

Method quickSortBilateral(arr As PHA.YX.Arithmetic.Array, startIndex As %Integer, endIndex As %Integer)
{
    
    /*  遞歸結(jié)束條件:startIndex大等于endIndex的時候 */
    q:(startIndex >= endIndex) arr
    
    /*  得到基準(zhǔn)元素位置 */
    #dim pivotIndex as %Integer = ..partitionBilateral(arr, startIndex, endIndex)
    w "startIndex:" _ startIndex _" endIndex:" _ endIndex_" pivotIndex:" _ pivotIndex,!
    
    /*  根據(jù)基準(zhǔn)元素,分成兩部分遞歸排序 */
    d ..quickSortBilateral(arr, startIndex, pivotIndex - 1)
    d ..quickSortBilateral(arr, pivotIndex + 1, endIndex)
    
    q arr
}

Method partitionBilateral(arr As PHA.YX.Arithmetic.Array, startIndex As %Integer, endIndex As %Integer)
{
    /*  取第一個位置的元素作為基準(zhǔn)元素(也可以選擇隨機位置) */
    #dim pivot as %Integer = arr.get(startIndex)
    #dim left as %Integer = startIndex
    #dim right as %Integer = endIndex
    while (left '= right){
        
        /*  控制right指針比較并左移 */
        while ((left < right) && (arr.get(right) > pivot)){
            w "first left:" _ left _" right:" _ right _ " arr.get(right):" _ arr.get(right) _" pivot:" _ pivot,!
            s right = right -1
        } 
        
        /*  控制left指針比較并右移 */
        while ((left < right) && (arr.get(left) <= pivot)){
            w "second left:" _ left _" right:" _ right _" arr.get(left):" _ arr.get(left) _" pivot:" _ pivot,!
            s left = left + 1
        }
        /*  交換left和right指向的元素 */
        if (left < right){
            #dim p as %Integer = arr.get(left)
            d arr.set(left, arr.get(right))
            d arr.set(right, p)
        }
    }
    d arr.set(startIndex, arr.get(left))
    d arr.set(left, pivot)
    q left
}

}

調(diào)用


/// w ##class(PHA.YX.Arithmetic).QuickSortBilateral()
ClassMethod QuickSortBilateral()
{
    s $zt = "ErrQuickSort"
    s array = ##class(PHA.YX.Arithmetic.Array).%New()
    d array.init(8)
    d array.insert(0,41)
    d array.insert(1,73)
    d array.insert(2,64)
    d array.insert(3,55)
    d array.insert(4,36)
    d array.insert(5,27)
    d array.insert(6,88)
    d array.insert(7,19)
    
    #dim mQuickSort as PHA.YX.Arithmetic.QuickSort = ##class(PHA.YX.Arithmetic.QuickSort).%New()
    s array = mQuickSort.quickSortBilateral(array, 0 ,array.length - 1)
    d array.output()
    q ""
ErrQuickSort
    q $ze
}
DHC-APP>w ##class(PHA.YX.Arithmetic).QuickSortBilateral()

19
27
36
41
55
64
73
88
 

單邊循環(huán)

而單邊循環(huán)法則簡單得多扎唾,只從數(shù)組的一邊對元素進(jìn)行遍歷和交換蜀肘。

  1. 給出原始數(shù)列如下,要求對其從小到大進(jìn)行排序稽屏。


    image.png
  2. 開始和雙邊循環(huán)法相似扮宠,首先選定基準(zhǔn)元素pivot。同時,設(shè)置一個mark指針指向數(shù)列起始位置坛增,這個mark指針代表小于基準(zhǔn)元素的區(qū)域邊界获雕。


    image.png
  3. 如果遍歷到的元素大于基準(zhǔn)元素,就繼續(xù)往后遍歷收捣。如果遍歷到的元素小于基準(zhǔn)元素届案,則需要做兩件事:第一,把mark指針右移1位罢艾,因為小于pivot的區(qū)域邊界增大了1楣颠;第二,讓最新遍歷到的元素和mark指針?biāo)谖恢玫脑亟粨Q位置咐蚯,因為最新遍歷的元素歸屬于小于pivot的區(qū)域童漩。首先遍歷到元素7,7>4春锋,所以繼續(xù)遍歷矫膨。


    image.png
  4. 接下來遍歷到的元素是3,3<4期奔,所以mark指針右移1位侧馅。


    image.png
  5. 隨后,讓元素3和mark指針?biāo)谖恢玫脑亟粨Q呐萌,因為元素3歸屬于小于pivot的區(qū)域馁痴。


    image.png
  6. 剩余遍歷


    image.png

完整實例

快速排序類

Method quickSortUnilateral(arr As PHA.YX.Arithmetic.Array, startIndex As %Integer, endIndex As %Integer)
{
    
    /*  遞歸結(jié)束條件:startIndex大等于endIndex的時候 */
    q:(startIndex >= endIndex) arr
    
    /*  得到基準(zhǔn)元素位置 */
    #dim pivotIndex as %Integer = ..partitionUnilateral(arr, startIndex, endIndex)
    w "startIndex:" _ startIndex _" endIndex:" _ endIndex_" pivotIndex:" _ pivotIndex,!
    
    /*  根據(jù)基準(zhǔn)元素,分成兩部分遞歸排序 */
    d ..quickSortUnilateral(arr, startIndex, pivotIndex - 1)
    d ..quickSortUnilateral(arr, pivotIndex + 1, endIndex)
    
    q arr
}

Method partitionUnilateral(arr As PHA.YX.Arithmetic.Array, startIndex As %Integer, endIndex As %Integer)
{
    /*  取第一個位置的元素作為基準(zhǔn)元素(也可以選擇隨機位置) */
    #dim pivot as %Integer = arr.get(startIndex)
    #dim mark as %Integer = startIndex
    
    for i = (startIndex + 1) : 1 : endIndex {
        if (arr.get(i) < pivot){
            s mark = mark + 1
            #dim p as %Integer = arr.get(mark)
            d arr.set(mark, arr.get(i))
            d arr.set(i, p)
        }
    }
    d arr.set(startIndex, arr.get(mark))
    d arr.set(mark, pivot)
    q mark
}

調(diào)用

/// w ##class(PHA.YX.Arithmetic).QuickSortUnilateral()
ClassMethod QuickSortUnilateral()
{
    s $zt = "ErrQuickSort"
    s array = ##class(PHA.YX.Arithmetic.Array).%New()
    d array.init(8)
    d array.insert(0,41)
    d array.insert(1,73)
    d array.insert(2,64)
    d array.insert(3,55)
    d array.insert(4,36)
    d array.insert(5,27)
    d array.insert(6,88)
    d array.insert(7,19)
    
    #dim mQuickSort as PHA.YX.Arithmetic.QuickSort = ##class(PHA.YX.Arithmetic.QuickSort).%New()
    s array = mQuickSort.quickSortUnilateral(array, 0 ,array.length - 1)
    d array.output()
    q ""
ErrQuickSort
    q $ze
}
DHC-APP>w ##class(PHA.YX.Arithmetic).QuickSortUnilateral()
startIndex:0 endIndex:7 pivotIndex:3
startIndex:0 endIndex:2 pivotIndex:0
startIndex:1 endIndex:2 pivotIndex:2
startIndex:4 endIndex:7 pivotIndex:6
startIndex:4 endIndex:5 pivotIndex:4
19
27
36
41
55
64
73
88

元素的交換(非遞歸實現(xiàn))

快速排序類


Method quickSortStack(arr As PHA.YX.Arithmetic.Array, startIndex As %Integer, endIndex As %Integer)
{
    #dim quickSortStack as PHA.YX.Arithmetic.Stack = ##class(PHA.YX.Arithmetic.Stack).%New()
    #dim rootParam as %ArrayOfDataTypes = ##class(%ArrayOfDataTypes).%New()
    d rootParam.SetAt(startIndex, "startIndex")
    d rootParam.SetAt(endIndex, "endIndex")
    d quickSortStack.push(rootParam)

    while ('quickSortStack.isEmpty()){
        #dim param as %ArrayOfDataTypes = quickSortStack.pop()
        w "param.GetAt(startIndex):" _ param.GetAt("startIndex") _ " param.GetAt(endIndex):" _ param.GetAt("endIndex") ,!
        #dim pivotIndex as %Integer = ..partitionStack(arr, param.GetAt("startIndex"), param.GetAt("endIndex"))
        if ((param.GetAt("startIndex")) < (pivotIndex - 1) ){
            #dim leftParam as %ArrayOfDataTypes = ##class(%ArrayOfDataTypes).%New()
            d leftParam.SetAt(param.GetAt("startIndex"), "startIndex")
            d leftParam.SetAt(pivotIndex - 1, "endIndex")
            d quickSortStack.push(leftParam)
        }
        if ((pivotIndex + 1) < (param.GetAt("endIndex"))){
            #dim rightParam as %ArrayOfDataTypes = ##class(%ArrayOfDataTypes).%New()
            d rightParam.SetAt(pivotIndex + 1, "startIndex")
            d rightParam.SetAt(param.GetAt("endIndex"), "endIndex")
            d quickSortStack.push(rightParam)
        }
    }
    q arr
}

Method partitionStack(arr As PHA.YX.Arithmetic.Array, startIndex As %Integer, endIndex As %Integer)
{
    /*  取第一個位置的元素作為基準(zhǔn)元素(也可以選擇隨機位置) */
    #dim pivot as %Integer = arr.get(startIndex)
    #dim mark as %Integer = startIndex
    
    for i = (startIndex + 1) : 1 : endIndex {
        if (arr.get(i) < pivot){
            s mark = mark + 1
            #dim p as %Integer = arr.get(mark)
            d arr.set(mark, arr.get(i))
            d arr.set(i, p)
        }
    }
    d arr.set(startIndex, arr.get(mark))
    d arr.set(mark, pivot)
    q mark
}

調(diào)用

/// w ##class(PHA.YX.Arithmetic).QuickSortStack()
ClassMethod QuickSortStack()
{
    s $zt = "QuickSortStack"
    s array = ##class(PHA.YX.Arithmetic.Array).%New()
    d array.init(8)
    d array.insert(0,41)
    d array.insert(1,73)
    d array.insert(2,64)
    d array.insert(3,55)
    d array.insert(4,36)
    d array.insert(5,27)
    d array.insert(6,88)
    d array.insert(7,19)
    
    #dim mQuickSort as PHA.YX.Arithmetic.QuickSort = ##class(PHA.YX.Arithmetic.QuickSort).%New()
    s array = mQuickSort.quickSortStack(array, 0 ,array.length - 1)
    d array.output()
    q ""
QuickSortStack
    q $ze
}
DHC-APP>w ##class(PHA.YX.Arithmetic).QuickSortStack()
param.GetAt(startIndex):0 param.GetAt(endIndex):7
param.GetAt(startIndex):4 param.GetAt(endIndex):7
param.GetAt(startIndex):4 param.GetAt(endIndex):5
param.GetAt(startIndex):0 param.GetAt(endIndex):2
param.GetAt(startIndex):1 param.GetAt(endIndex):2
19
27
36
41
55
64
73
88
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肺孤,一起剝皮案震驚了整個濱河市罗晕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌渠旁,老刑警劉巖攀例,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異顾腊,居然都是意外死亡粤铭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門杂靶,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梆惯,“玉大人,你說我怎么就攤上這事吗垮《饴穑” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵烁登,是天一觀的道長怯屉。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么锨络? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任赌躺,我火速辦了婚禮,結(jié)果婚禮上羡儿,老公的妹妹穿的比我還像新娘礼患。我一直安慰自己,他們只是感情好掠归,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布缅叠。 她就那樣靜靜地躺著,像睡著了一般虏冻。 火紅的嫁衣襯著肌膚如雪肤粱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天兄旬,我揣著相機與錄音狼犯,去河邊找鬼余寥。 笑死领铐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宋舷。 我是一名探鬼主播绪撵,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼祝蝠!你這毒婦竟也來了音诈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤绎狭,失蹤者是張志新(化名)和其女友劉穎细溅,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體儡嘶,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡喇聊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹦狂。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誓篱。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖邦鲫,靈堂內(nèi)的尸體忽然破棺而出抄罕,到底是詐尸還是另有隱情餐曹,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布邻遏,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏准验。R本人自食惡果不足惜削解,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望沟娱。 院中可真熱鬧氛驮,春花似錦、人聲如沸济似。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽砰蠢。三九已至蓖扑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間台舱,已是汗流浹背律杠。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留竞惋,地道東北人柜去。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像拆宛,于是被迫代替她去往敵國和親嗓奢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355