5快速排序

'''
快速排序
分类 算法
    快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。
    在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn)
    算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。
    快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
    快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。
    快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高!
    它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀,
    在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。
    好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案:
        快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn)
        记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。
        所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。

1. 算法步骤
    1从数列中挑出一个元素,称为 "基准"(pivot);
    2重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
    在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
    3递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

快速排序的基本思想
    快速排序是对冒泡排序的一种改进。
    它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,
    然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

快速排序的算法分析
快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
    1首先设定一个分界值,通过该分界值将数组分成左右两部分。
    2将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时左边部分中各元素都小于或等于分界值,
    而右边部分中各元素都大于或等于分界值。
    3然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边
    放置较小的值,右边放置较大值。右侧的数组数据也可以做类似处理。
    4重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两部分各
    数据排序完成后,整个数组的排序也就完成了。

快速排序的排序步骤
    设要排序的数组是A[0]....A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数
    都放到它的左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序
    值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。

一趟快速排序的算法是:
    1设置二个变量i,j,排序开始的时候:i=0,j=n-1;
    2以第一个数组元素作为关键数据,赋值给key,即key=A[0];
    3从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]的值交换;
    4从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的值A[i],将A[i]和A[j]的值交换;
    5重复3,4步,直到i=j;(3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中的A[i]不大于key的时候改变j,i的值,
    使得j=j-1,i=i+1,直到找到为止。找到符合条件的值,进行交换的时候i,j指针位置不变。另外,i==j这一过程一定正好是
    i+或j-完成的时候,此时循环结束)。

例:
    假设一开始序列a = [5,3,7,6,4,1,0,2,9,10,8]
    1 ref = 5,i=0,j=10,从后向前找,a[7]=2 < 5,此时i=0,j=7,得到a=[2,3,7,6,4,1,0,5,9,10,8]
    2 ref = 5,i=0,j=7,从前向后找,a[2]=7 > 5,此时i=2,j=7,得到a=[2,3,5,6,4,1,0,7,9,10,8]
    3 ref = 5,i=2,j=7,从后向前找,a[6]=0 < 5,此时i=2,j=6,得到a=[2,3,0,6,4,1,5,7,9,10,8]
    4 ref = 5,i=2,j=6,从前向后找,a[3]=6 > 5,此时i=3,j=6,得到a=[2,3,0,5,4,1,6,7,9,10,8]
    5 ref = 5,i=3,j=6,从后向前找,a[5]=1 < 5,此时i=3,j=5,得到a=[2,3,0,1,4,5,6,7,9,10,8]
    6 ref = 5,i=3,j=5,从前向后找,a[6]=6 > 5,(正常区间已经无法满足的数),这时,i=j=6,ref成为
    一条分界线,它之前的数都比它小,之后的数都比它大,对于前后两部分数,可以采用同样的方法来排序。

方法1:
def quickSort(arr, left=None, right=None):
    left = 0 if not isinstance(left,(int, float)) else left
    right = len(arr)-1 if not isinstance(right,(int, float)) else right
    if left < right:
        partitionIndex = partition(arr, left, right)
        quickSort(arr, left, partitionIndex-1)
        quickSort(arr, partitionIndex+1, right)
    return arr

def partition(arr, left, right):
    pivot = left
    index = pivot+1
    i = index
    while  i <= right:
        if arr[i] < arr[pivot]:
            swap(arr, i, index)
            index+=1
        i+=1
    swap(arr,pivot,index-1)
    return index-1

def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]

方法2:
# def quickSort(l, p, r):         #p为最最左边的索引号即i=0,r为最右边的索引号即j=n-1
#     if p < r:                   #当p<r,表示没有超出区间范围
#         q = partion(l,p,r)
#         print(q)
#         quickSort(l,p,q)
#         quickSort(l,q+1,r)
#
# def partion(l,p,r):
#     i=p-1                           #初始值设为-1
#     for j in range(p,r):            #p为最左边的
#         if l[j]<=l[r]:              #如果这个列表中有值小于等于最后一个值
#             i+=1                    #i向右移一位
#             print(i,j)
#             l[i],l[j]=l[j],l[i]     #
#     print('i:',i)
#     l[i+1], l[r] = l[r], l[i+1]
#     return i
#
# l = [5,3,7,6,4,1,11,0,2,9,10,13,12,8]
# quickSort(l,0,len(l)-1)
# print(l)

方法3:
    假设有n项数据,数据值用k1,k2,k3,......kn来表示。
    1先在数据中假设一个虚拟中间值K(为了方便,一般取第一个位置上的数)。
    2从左向右查数据ki,使得ki>K,ki的位置数记为i。
    3从右向左查数据kj,使得kj<K,kj的位置数记为j。
    4若i<j,那么数据ki,kj交换位置,并回到2,3步骤。
    5若i>=j,那么数据kj,K交换位置,并以j为基准点分割成左右两部分,然后针对左右两部分再
    进行1-5步骤,直到左半边数据等于右半边数据为止。
    快速排序的结果有两种形式,递增和递减数列。
例:    位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
        6   1  2   7   9  3   4  5  10   8
步骤1:取原始值的第一个数6为虚拟中间值,K=6,然后从左向右找大于6的值,即k4=7,再从右向左
      找小于6的值,即K8=5.
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       6   1  2   7   9  3   4  5  10   8
       K          i             j
      根据第4条法则,i<j时,交换ki,kj,得到如下列数:
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       6   1  2   5   9  3   4  7  10   8

步骤2:重复2,3法则,继续从左向右找大于6的值,即k5=9,再从右向左
      找小于6的值,即K7=4.
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       6   1  2   5   9  3   4  7  10   8
       k              i      j
      根据第4条法则,i<j时,交换ki,kj,得到如下列数:
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       6   1  2   5   4  3   9  7  10   8

步骤3,重复2,3法则,继续从左向右找大于6的值,即k7=9,再从右向左
      找小于6的值,即K6=3.
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       6   1  2   5   4  3   9  7  10   8
       k                 j   i
      根据第5条法则,i>=j时,交换kj,k,得到如下列数:
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       3   1  2   5   4  6   9  7  10   8

步骤4,K=6是真正的中间值,左边的部分都小于6,右边的部分都大于6.

步骤5,对中间值6左边的数据进行排序,按照快速度排序法则,在左半边部分取K=3,
      从左边到右找大于3的值,i=4,k4=5,从右边到左找小于3的值,j=3,K3=2
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       3   1  2   5   4       不处理
       K      j   i
      根据第5条法则,i>=j时,交换kj,k,得到如下列数:
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       2   1  3   5   4       不处理

步骤6,交换K值后,k=3成了新的中间值,把左边的部分分成新的左右二部分,小于3的都在左边
      大于3的都在右边。
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       2   1  3   5   4       不处理

步骤7,接下来对中间值3的左,右两侧排序,经过排序之后,最终排序结果如下
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       1  2   3  4   5       不处理

步骤8:此时,左边已经排好,再排右边,k=9,从左到右找大于9的值,i=9,k9=10,从右到
      到左找小于9的值,j=10,k10=8,
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
          已经处理好           9  7  10   8
                             k      i   j
      根据第4条法则,i<j时,交换ki,kj,得到如下列数:
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
          已经处理好           9  7  8   10

步骤9,k=9,从左到右找大于k的值,i=10,k10=10,从右到左找比9小的值,
      j=9,k9=8,
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
          已经处理好           9  7  8   10
                             k     j    i
      根据第5条法则,i>=j时,交换kj,k,得到如下列数:
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
          已经处理好           8  7  9   10

步骤10,k=8,从左到右找大于k的值,i=9,k9=9,从右到左找比8小的值,
      j=8,k8=7,
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
          已经处理好           8  7  9   10
                             k  j  i
      根据第5条法则,i>=j时,交换kj,k,得到如下列数:
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
          已经处理好           7  8  9   10

步骤11,左右二边都已排好,最后得到:
      位1 位2 位3 位4 位5 位6 位7 位8 位9 位10
       1  2   3   4  5   6   7  8   9  10
至此快速排序结束。

'''
def kspx(l,start,end):
    if start > end:
        return
    k = l[start]
    i,j=start,end
    while True:
        while j > i and l[j]>=k:
            j -= 1
        while i<j and l[i]<=k:
            i += 1
        if i < j:
            l[i],l[j]=l[j],l[i]
        elif i >= j:
            l[start],l[j]=l[j],l[start]
            break
    kspx(l,start,j-1)
    kspx(l,j+1,end)

import random as r
a = []
for i in range(50):
    v = r.randint(0,100)
    a.append(v)
v = kspx(a,0,len(a)-1)
print(a)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值