快速排序的优化

1、改进办法

  • 对于小数组,插入排序比快速排序快,并且切分到小数组的时候,快速的排序的递归深度很大
  • 因为递归,快速排序在小数组中也会调用自己
  • 调整pivot的值,(可使用三数取中,或者随机数等)
  • 最后一种最优的排序:三向切分快速排序
    因此在排序小数组是应该使用应该切换为插入排序,
//这是golang中的源码,也是这种做法,当只有12(这个值在5~15之间都能获得不错的性能)的时候就是用下面的插入,并且在大于十二的时候,递归深度为0的时候用堆排序(也可以使用希尔插入排序),
func quickSort(data Interface, a, b, maxDepth int) {
    for b-a > 12 { // Use ShellSort for slices <= 12 elements
        //递归深度为0的时候才能使用堆排序
        if maxDepth == 0 {
            heapSort(data, a, b)
            return
        }
        maxDepth--
        mlo, mhi := doPivot(data, a, b)
        // Avoiding recursion on the larger subproblem guarantees
        // a stack depth of at most lg(b-a).
        if mlo-a < b-mhi {
            quickSort(data, a, mlo, maxDepth)
            a = mhi // i.e., quickSort(data, mhi, b)
        } else {
            quickSort(data, mhi, b, maxDepth)
            b = mlo // i.e., quickSort(data, a, mlo)
        }
    }
    //只有是个元素的时候使用插入排序
    if b-a > 1 {
        // Do ShellSort pass with gap 6
        // It could be written in this simplified form cause b-a <= 12
        for i := a + 6; i < b; i++ {
            if data.Less(i, i-6) {
                data.Swap(i, i-6)
            }
        }
        insertionSort(data, a, b)
    }
}

2、调整pivot的值

1、使用取中的pivot的值
比如:需排序数组为:[8,5,6,4,2,3,9,0]
1、第一步取出第一个,最后一个,中间的三个元素,8,0 ,2
2、选取中间值并和第一个元素交换

//快速排序
func QuickSort(data Interface) {
    n := data.Len()
    low, row := 0, n-1
    quickSort(data, low, row)
}

func quickSort(data Interface, low, row int) {
    if low < row {
        pivotOnMedilOfThree(data, low, row) //三个数取中间数
        i, j, x, last := low, row, low, 0   //0就是使用第一个作为基准值
        for i < j {
            for i < j && data.Less(x, j) { //比x小的放在前面出现的坑中
                j--
            }
            if i < j {
                data.Swap(i, j)
                i++
                x = j
                last = 1
            }
            for i < j && data.Less(i, x) { //比x大的放在后面出现的坑中
                i++
            }
            if i < j {
                data.Swap(i, j)
                j--
                x = i
                last = -1
            }
        }
        if last == 1 {
            data.Swap(j, x)
        } else if last == -1 {
            data.Swap(i, x)
        }
        quickSort(data, low, i-1)
        quickSort(data, i+1, row)
    }
}

//三个数取中间数
func pivotOnMedilOfThree(data Interface, low, height int) {
    medil := low + (height-low)/2 //取中间元素的值
    if data.Less(low, medil) {
        if data.Less(medil, height) {
            medil = medil
        } else {
            medil = height
        }
    } else if data.Less(height, medil) {
        medil = medil
    } else {
        medil = height
    }
    data.Swap(medil, low)
}

3、三向切分快速排序

三向切分排序是为了解决数组中有大量重复的元素使用原有的快速排序性能低下的问题
三向切分的快速排序从左到右遍历数组一次,维护一个指针lt使得a[lo….lt-1]中的元素都小于v,一个指针gt使得a[gt+1……hi]中的元素都大于v,一个指针i使得a[lt….i-1]中的元素都等于v,a[i,…gt]中元素还不确定

  • a[i]小于v,将a[lt]和a[i]交换,将lt和i加1
  • a[i]大于v,将a[gt] 和a[i] 交换,将gt–
  • a[i]等于v,将i加1
//golang源码中没有使用这种排序,我想是因为这里会是这个排序的比较方法变的不够简洁
//定义三常量
const (
    ThanLess = -1  //小 于
    Equel    = 0  //等于
    Greate   = 1 //大于
)
type Interface interface {
    Len() int //长度
    Less(i, j int) int //改进了这里,
    Swap(i, j int)
}
//写一个排序
func ThreeSyncopateQuickSort(data Interface) {
    n := data.Len()
    lo, ho := 0, n-1
    quickSort(data, lo, ho)
}

func quickSort(data Interface, lo, ho int) {
    if lo < ho {
        lt, gt, pivot, i := lo, ho, lo, lo+1
        for i <= gt {
            cmp := data.Less(i, pivot)
            if cmp > 0 { //lt 大于基准,那么
                data.Swap(gt, i)
                gt--
            } else if cmp < 0 { //lt小于基准,那么进行交换
                data.Swap(lt, i)
                pivot = i
                i++
                lt++
            } else if cmp == 0 {
                i++
            }
        }
        quickSort(data, lo, lt-1)
        quickSort(data, gt+1, ho)
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值