第十四章 Caché 算法与数据结构 快速排序

第十四章 Caché 算法与数据结构 快速排序

定义

  • 同冒泡排序一样,快速排序也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的。
  • 不同的是,冒泡排序在每一轮中只把1个元素冒泡到数列的一端,而快速排序则在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分。
  • 这种思路就叫作分治法。
image.png

流程

image.png
  • 如图所示,在分治法的思想下,原数列在每一轮都被拆分成两部分,每一部分在下一轮又分别被拆分成两部分,直到不可再分为止。
  • 每一轮的比较和交换,需要把数组全部元素都遍历一遍,时间复杂度是O(n)。假如元素个数是n,那么平均情况下需要logn轮,因此快速排序算法总体的平均时间复杂度是O(nlogn)。

选择基准元素

  • 基准元素,英文是pivot,在分治过程中,以基准元素为中心,把其他元素移动到它的左右两边。
  • 最简单的方式是选择数列的第1个元素。
  • 加入有原本逆数序列
    • 在这种极端情况下,快速排序需要进行n轮,时间复杂度退化成了O(n2)。
image.png
  • 我们随机挑选一个基准元素
image.png
  • 这样一来,即使在数列完全逆序的情况下,也可以有效地将数列分成两部分。
  • 所以,虽然快速排序的平均时间复杂度是O(nlogn),但最坏情况下的时间复杂度是O(n2)。

元素的交换(递归实现)

双边循环

  • 首先,选定基准元素pivot,并且设置两个指针left和right,指向数列的最左和最右两个元素。


    image.png
  • 接下来进行第1次循环,从right指针开始,让指针所指向的元素和基准元素做比较。如果大于或等于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)
{
    
    /*  递归结束条件:startIndex大等于endIndex的时候 */
    q:(startIndex >= endIndex) arr
    
    /*  得到基准元素位置 */
    #dim pivotIndex as %Integer = ..partitionBilateral(arr, startIndex, endIndex)
    w "startIndex:" _ startIndex _" endIndex:" _ endIndex_" pivotIndex:" _ pivotIndex,!
    
    /*  根据基准元素,分成两部分递归排序 */
    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)
{
    /*  取第一个位置的元素作为基准元素(也可以选择随机位置) */
    #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
}

}

调用


/// 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
 

单边循环

而单边循环法则简单得多,只从数组的一边对元素进行遍历和交换。

  1. 给出原始数列如下,要求对其从小到大进行排序。


    image.png
  2. 开始和双边循环法相似,首先选定基准元素pivot。同时,设置一个mark指针指向数列起始位置,这个mark指针代表小于基准元素的区域边界。


    image.png
  3. 如果遍历到的元素大于基准元素,就继续往后遍历。如果遍历到的元素小于基准元素,则需要做两件事:第一,把mark指针右移1位,因为小于pivot的区域边界增大了1;第二,让最新遍历到的元素和mark指针所在位置的元素交换位置,因为最新遍历的元素归属于小于pivot的区域。首先遍历到元素7,7>4,所以继续遍历。


    image.png
  4. 接下来遍历到的元素是3,3<4,所以mark指针右移1位。


    image.png
  5. 随后,让元素3和mark指针所在位置的元素交换,因为元素3归属于小于pivot的区域。


    image.png
  6. 剩余遍历


    image.png

完整实例

快速排序类

Method quickSortUnilateral(arr As PHA.YX.Arithmetic.Array, startIndex As %Integer, endIndex As %Integer)
{
    
    /*  递归结束条件:startIndex大等于endIndex的时候 */
    q:(startIndex >= endIndex) arr
    
    /*  得到基准元素位置 */
    #dim pivotIndex as %Integer = ..partitionUnilateral(arr, startIndex, endIndex)
    w "startIndex:" _ startIndex _" endIndex:" _ endIndex_" pivotIndex:" _ pivotIndex,!
    
    /*  根据基准元素,分成两部分递归排序 */
    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)
{
    /*  取第一个位置的元素作为基准元素(也可以选择随机位置) */
    #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
}

调用

/// 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

元素的交换(非递归实现)

快速排序类


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)
{
    /*  取第一个位置的元素作为基准元素(也可以选择随机位置) */
    #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
}

调用

/// 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352