美文网首页
快速排序 --- 基础实践篇

快速排序 --- 基础实践篇

作者: 张虾米试错 | 来源:发表于2019-03-05 21:23 被阅读0次

本篇主要介绍快速排序的基本代码。

大纲

  1. 普通版的快速排序
  2. 改进版的快速排序
  3. 快速排序应用----求前K个最大的数

1. 普通版的快速排序

# -*- coding:utf-8 -*-

def partition(nums, left, right):
    low = left
    high = right
    privot = nums[low]
    while low < high:
        while low < high and nums[high] >= privot:
            high -= 1
        if low < high:
            nums[low], nums[high] = nums[high], nums[low]
        while low < high and nums[low] <= privot:
            low += 1
        if low < high:
            nums[high], nums[low] = nums[low], nums[high]
    #nums[low] = privot
    return low


def partition1(nums, left, right):
    low = left
    high = right
    privot = nums[low]
    while low < high:
        while low < high and nums[high] >= privot:
            high -= 1
        while low < high and nums[low] <= privot:
            low += 1

        if low < high:
            nums[low], nums[high] = nums[high], nums[low]

    nums[left], nums[low] = nums[low], nums[left]
    return low


def quick_sort(nums, left, right):
    if nums is None or len(nums) == 0:
        return 
    # 这里的判断条件已经是判断是否调出递归
    if left < right: 
        ind = partition(nums, left, right)
        quick_sort(nums, left, ind-1)#ind-1可能会更好
        quick_sort(nums, ind+1, right)

2. 改进版的快速排序

普通版本存在的问题:

Q1: 数组长度比较短

研究表明长度在5~25的数组,快排不如插入排序。所以可以对数组长度进行判断,然后再选择用哪一种排序方法。

Q2: pivot选择

当pivot选择不当,左右两边不平衡,这样导致快排的时间复杂度为o(n^2)。有一种方法是选取随机数作为枢轴,但是随机数的生成本身是一种代价,根本减少不了算法其余部分的平均运行时间。因此,我们可以使用左端,右端和中心的中值做为枢轴元。经验得知,选取左端,右端,中心元素的中值会减少了快排大约 14%的比较。

Q3: 数组中重复元素太多

大致的思想就是将数组分为三段:小于pviot,等于pviot,大于pviot

方案1
def partition(nums, left, right):
    q = left
    pivot = nums[right]
    # 这一段代码是找出第一个大于pviot的index
    # 右界范围也可以是right+1,不过因为pviot是nums[right]所以该代码上没有意义;但是如果pviot不是nums[right]则应该换成right+1
    for i in xrange(left, right):
        if nums[i] < pivot:
            nums[q], nums[i] = nums[i], nums[q]
            q += 1
    # q表示第一个大于等于pviot的index,因此一定会小于或等于right
    # 这一段代码跳过等于pviot的数
    t = q
    while t < right and nums[t] == pivot:
        t += 1
    # 这一段代码将大于pviot的数移到后面
    i = right
    while i > t - 1:
        if nums[i] == pivot:
            nums[t], nums[i] = nums[i], nums[t]
            t += 1
        i -= 1
    return q, t

def quick_sort(nums, left, right):
    if left < right:
        q, t = partition(nums, left, right)
        # q是第一个等于pivot的index,而t是第一个大于pivot的index
        quick_sort(nums, left, q-1)
        # 当q-1变成q后,竟然会陷入死循环???
        quick_sort(nums, t, right)
        
if __name__ == "__main__":
    nums = [6, 4, 2, 6, 8, 9, 5, 6, 7, 6]
    quick_sort(nums, 0, len(nums)-1)
    print nums

由于可能存在重复的元素,因此无法像普通的快速排序一样进行值对换;而这种遍历的方法与普通值对换的方法相比,可能就是多了一些交换的代价。其本质是一样的,都是把小于pviot的值换到左边,大于pviot的值换到右边。

好像确实如此哦,因为pviot是当前right的值,如果一直是q的话,那么pviot一直是最开始的pviot,那么前半段就会陷入死循环。

方案2
import random
class Solution:
    def quickSortArray(self, nums):
        def partition(nums, start, end):
            rand = random.randint(start, end)
            nums[rand], nums[start] = nums[start], nums[rand]
            key = nums[start]
            left, right = start, end 
            while left < right:
                while left < right and nums[right] >= key:
                    right -= 1
                nums[right], nums[left] = nums[left], nums[right]
                while left < right and nums[left]<=key:
                    left += 1
                nums[right], nums[left] = nums[left], nums[right]
            return left
            
        def sort(nums, left, right):
            if left < right:
                mid = partition(nums, left, right)
                sort(nums, left, mid-1)
                sort(nums, mid+1, right)
            
        n = len(nums)
        if n <=1:
            return nums
        #random.shuffle(nums)
        sort(nums, 0, n-1)
        return nums

if __name__ == "__main__":
    s = Solution()
    nums = [5,2,1,1,2,3,3,2,1]
    ans = s.quickSortArray(nums)
    print (ans)           

3. 快速排序应用----求前K个最大的数

def partition(nums, left, right):
    pviot = nums[right]
    p = left
    for i in xrange(left, right):
        # 由于本题是找第K大的数,因此这里是>
        if nums[i] > pviot:
            nums[i], nums[p] = nums[p], nums[i]
            p += 1
    nums[p], nums[right] = nums[right], nums[p]
    return p

def findKthLargest2(nums, k):
    if len(nums) == 1:
        return nums[0]
    left = 0
    right = len(nums) - 1
    p = partition(nums, left, right)
    while left < right:
        if k == p + 1:
            return nums[p]
        elif k < p + 1:
            return findKthLargest2(nums[left:p], k)
        else:
            return findKthLargest2(nums[p+1:right+1], k-p-1)

def findKthLargest3(nums, k):
    if len(nums) == 1:
        return nums[0]
    size = len(nums)
    left = 0
    right = len(nums) - 1
    while left <= right:
        p = partition(nums, left, right)
        if k == p + 1
            return nums[p]
        elif k > p + 1:
            left = p + 1
        else:
            right = p - 1


if __name__ == "__main__":
    nums = [3, 2, 1, 5, 6, 4]
    k = 2
    print findKthLargest2(nums, k)

参考资料

相关文章

  • 快速排序 --- 基础实践篇

    本篇主要介绍快速排序的基本代码。 大纲 普通版的快速排序 改进版的快速排序 快速排序应用----求前K个最大的数 ...

  • 快速排序代码的常见问题整理

    快速排序为什么快 很多书上都说快速排序是实践中排序速度最快的排序。原因有很多,从计算机角度来说,快速排序中...

  • 开发者应该掌握的几种排序算法

    该篇文章主要介绍了算法基础以及几种常见的排序算法:选择排序、插入排序、冒泡排序、快速排序、堆排序。 一、算法基础 ...

  • Java常见排序基础 - 中

    在Java常见排序基础 - 上中主要介绍了冒泡排序、选择排序、插入排序三种基础排序,本篇文章主要介绍的是 快速排序...

  • 基础排序:快速排序

    一、什么是快速排序? 把一个无序的数组,第一趟排序后将数组分隔成两部分,若把前半部分和后半部分的相交元素称为中间元...

  • Partition 快排核心函数

    快速排序介绍 快速排序是目前在实践中非常高效的一种排序算法,它不是稳定的排序算法,平均时间复杂度为O(nlogn)...

  • 排序算法

    概述 一般排序算法(以元素比较为基础) => 快速排序、归并排序、插入排序、冒泡排序、堆排序 特殊排序算法 => ...

  • 快速排序算法及其测试算法的原理与实现

    快速排序简介 快速排序是一种分治的排序算法,是实践中最快的排序算法,理论上的时间复杂度为O(N*lgN),最差情况...

  • 快速排序优化详解

    原文地址:快速排序优化详解 正如它的名字所体现,快速排序是在实践中最快的已知排序算法,平均运行时间为O(NlogN...

  • 《python算法教程》Day9 - 快速排序法

    这是《python算法教程》第9篇读书笔记,笔记的主要内容为快速排序法。 快速排序法简介 快速排序法运用分治法的方...

网友评论

      本文标题:快速排序 --- 基础实践篇

      本文链接:https://www.haomeiwen.com/subject/jrdauqtx.html