快排序算法

承接上文归并排序及小和问题

归并排序扩展-逆序对问题

在一个数组中,左边的数如果比右边的数大,则两个数构成一个逆序对,找到逆序对的数量

比如 3,2,4,5,0

所有的逆序对是 3,2; 3,0; 2,0; 4,0; 5,0

上文的小和问题是 求右边有多少个数比左边的数大

这个题目是求右边有多少个数比左边的数小

所以逆序对的问题和小和问题是等效的

归并排序为什么不会重算和漏算或归并排序的实质

一个数组[a,b,c,d,e,f,g,h,i]

就看数字c 看它经历什么样的心路历程

一开始数组是这么被分的

当a和b整体产生小和变成有序之后 跟c合并的过程

c不产生小和

因为右组所有的数都不会产生小和

然后a,b,c就变成了一个有序的部分

c在哪里不重要

接下来 a,b,c这个整体和d,e所在的组合并

这样合并的时候c产生的小和数量它的范围被扩到右侧的d,e范围上

d,e这个范围上有可能有比c大的(有0个比c大的,有1个比c大的,有2个比c大的)

具体哪种情况不重要,重要的是c已经把自己求有多少个数比自己大的范围扩到d,e上

然后a,b,c,d,e共同构成一个有序的部分,c在哪里不重要

重要的是下面会和f,g,h,i所在的数组

去求这个范围上有多少个数比c大

c先去求de范围有多少个数比c大

再去求f,g,h,i范围有多少个数比c大

a,b,c,d,e,f,g,h,i 这些数成为一个整体

c在哪个位置不重要

如果右侧还有一个范围 则c所在的这个整体会继续求那个右侧的范围有多少个数比c大

c在求有多少个数比它大的范围是右侧依次往外扩的

所以说它不会漏算

为什么说不会重算?

因为c一旦跟某一个右组合并了之后

它们就会变成一个组

而一个组内部是不会产生小和的

只有左组和右组之间才会产生小和

c只是一个普通的一员而已 对于所有的数来说都是这个过程

所以求每一个数的小和都不会漏算 都不会重算

荷兰国旗问题

怎么把一组数做划分 左边都小于它 右边都大于它 中间都等于它

1、问题1

左边都小于等于number,右边都大于number

不要求有序 只要求对数组进行划分

比如 一个数组是 3,5,6,7,4,3,5,8

number是5

所有小于等于5的放左边,大于5的放右边

准备一个变量表示小于等于区域的右边界

一开始在数组的最左侧

当前来到的位置是i位置

当来到i位置就会有两种情况

第一种是小于等于number的

第二种是大于number

1)i所在当前位置的数<=number

把当前数[i]和<=区的下一个数做交换

然后<=区往右扩一个位置

当前数也跳下一个i++

如图第一个数是3

3<5

3和<=区的下一个数也是3 即自己和自己交换

交换完之后 <=区往后扩

当前数跳下一个

当前数是5 也满足 <=number

5和5 自己和自己交换 <=区向右扩

当前数下移

此时当前数6大于5 命中第二种情况

i直接跳下一个

当前数是7 又中了情况2 还继续往下走

当前数是4 ,<=number的

把它跟<=区的下个数做交换

4和6做交换

<=区往右扩

当前数也跳下一个

有中了情况1

把当前数和下个数做交换

<=区往右扩

当前数跳下一个

又中第一种情况 结果是

当前数是8 ,i++ 越界了

此时已经做到了 <=区都是<=5的 ,右侧都是>5的

i是当前来到的位置

右侧是还未看过的位置 待定区域

<=区域里面都是<=已经做到的位置

<=区域和i中间的位置大于区域

荷兰国旗问题🇳🇱

小于区域放左边 中间都是等于number的 右边都是大于number的

小于和大于区域都不要求有序

当然可以没有小于区域或等于区域或大于区域 如果有的话 严格分开

时间复杂度是O(N) 用有限几个变量

定义两个变量 一个是小于区域的左边界

一个是大于区域的右边界

每来一个位置都有三种情况

1)第一种情况

i当前所在的数[i]<num,当前数和小于区域下一个做交换,<区域右扩,i跳下一个

2)第二种情况

[i]=num,i直接跳下一个

3)第三种情况

[i]>num

当前数i大于num

[i]当前数i和大于区域的前一个做交换

大于区域左扩

i原地不变

如图分析

i 位置上3 ,3<5 命中第一条逻辑

i位置上是5 命中第二个规则

直接跳下一个

然后中了情况3,6大于5

i和大于区域的前一个做交换

6和0交换

大于区域向左扩

i原地不动

因为这个0是交换过来的 还没有看过它 所以原地不动

0是小于num的 中了逻辑1

所以和小于区域的下一个数做交换

0和5交换

小于区域向右扩一个位置

i跳下一个

3<5 名中第一个逻辑

4<5 名中第一个逻辑

5=5 直接往下走

2<5 名中第一个逻辑

2和第一个5交换

6>5 名中第三个逻辑

和大于区域的前一个做交换

6和9换

大于区域往左扩一个位置

i原地不动

9>5

名中第三个逻辑

和大于区域的前一个做交换

9和9自己换

大于区域往左扩一个位置

i原地不动

当大于区域和i撞上的时候 交换停止

此时已经做到了 左边都是小于5的,中间都是等于5的,右边都是大于5的

i根据自己现在来到的数

如果是等于num的 就在等于区域直接扩充

然后跳下一个

如果i是小于区域的 就把i放到小于区域的下一个

相当于小于区域推着等于区域往前走

如果i是大于区域的

和大于区域的下一个交换并左扩

要么i往右走压缩待定区域

让小于区域推着等于区域奔向大于区域

要么i位置直接发货到大于区域

让大于区域往左扩 压缩待定位置

当小于区域推着等于区域和大于区域撞上的时候 就结束了

快排1.0版本

在整个数组中 拿最后一个数做划分值

最后一个数认为是num

让最后一个位置前面的这一段 小于等于num的放到左边

大于num的放在右边

这个数和大于区域的第一个数做交换

就可以做到小于等于区域被扩充了 而且最后一个数一定是num

然后剩下的都是大于区域的

让num的左侧和右侧重复这个行为

比如

最后一个数是5

大于5的区域的第一个数是6

6和5交换

5就不用动了 在数组中固定下来

这一个过程就是partition分层过程

再在小于等于5的区域上拿最后一个数重复此过程

大于5区域上拿最后一个数重复此过程

左侧递归下去

右侧递归下去

总能让左右2个区域都有序

在一个范围上总拿这个范围的最后一个数做划分

然后把最后一个数放到小于等于区域的最后一个位置

然后让小于等于区域做递归

让大于区域也做递归

因为每次都能排好一个数

所以总有整体都有序的时候

每个区域的最后一个值可能都不一样 上面是以5为例做的说明

快排2.0基于荷兰国旗

拿最后一个数做划分 比如是5

让前面的分三个区域 小于5 等于5 大于5区域

把5和大于5区域的第一个数做交换

等于5的区域就靠在一起了

整个数组就变成 等于5的区域在中间

大于5的区域在右边

等于5的区域就不用动了

在小于5的区域上做递归

在大于5的区域上做递归

每一次递归搞定的是一批等于划分值的数

所以总有有序的时候

快排2.0版本比1.0版本快一些

因为它一次搞定一批数

举例说明

原数组

最后一个是5

将前面的数据分为3个区域

小于5区域

等于5区域

大于5区域

最后一个元素5和大于区域的第一个元素6做交换

等于5的区域在整个数组中的位置固定了

接下来在左侧区域 4301上以最后一个元素1做划分重复该行为

0继续做递归是0

4 3区域 以3做划分

总有都变成有序的时候

在右侧区域 786上 以6做划分重复该行为

左侧和右侧做递归总有都排好的时候

快排1.0和2.0时间复杂度都是O(N^2)

拿最差的例子说明

1,2,3,4,5,6,7,8,9

以9做划分 没有右侧区域

只有左侧区域

划分的过程就是partition

partition过了9个数

在左侧区域拿8做划分 没有右侧区域

只有左侧区域

partion过了8个数

每次只搞定一个

所以等差数列

所以是O(N^2)的算法

最差情况原因只有一个 划分值打的很偏

先看什么时候是好情况

好情况是划分值打在几乎中间的位置

左侧递归的规模和右侧递归的规模都是差不多的

此时的master公式是

T(N)=2 T(N/2) + O(N)

除了调用递归之外(就是partition过程)的时间复杂度是O(N)

整体的时间复杂度是 O(N*LogN)

这种是最好的情况

划分值打偏就会逐渐退化成N^2的算法

左侧很小 右侧规模很大 最差情况就是没有左侧部分 只有右侧部分

或者 右侧很小 左侧规模很大 最差情况就是没有右侧部分 只有左侧部分

不管哪一种都是O(N^2)的算法

因为总是拿数组的最后一个位置做划分

所以差情况没法避免

可以人为构建差的例子

快排3.0

在数组L~R范围上

拿谁做划分

随机选择一个数

随机选了一个数之后 拿它和最后位置上的数做交换

然后拿这个新的最后位置的数做划分

既然是随机选的

好情况和差情况就是概率事件

随机选的可能是最坏的情况 事件复杂度是O(N^2)

也可能是1/5处

master公式是

T(N)=T(N/5)+T((4/5) * N)+O(N)

可能是1/3处

master公式是

T(N)=T(N/3)+T((2/3) * N)+O(N)

可能是1/2处

master公式是

T(N)=2*T(N/2)+O(N)

可能是4/5

master公式是

T(N)=T(4/5)+T((1/5) * N)+O(N)

每一位置都是等概率事件

每一种情况都是1/N的权重

把所有的master公式做概率累加

再求数学期望 得到的结果是O(N logN)的算法

代码

第一步等概率随机选择一个位置

把它跟最右侧位置做交换

在L~R这个范围上 拿最后位置的数即选出来的这个随机数

做partition

返回一个数组 长度一定为2

指的是划分值等于区域的左右边界

原数组最后一个数是5

按照5进行partition划分

等于5的区域是下标12和13的位置

返回的这个数组就是[12,13]

表示划分值等于区域的左右边界

p[0]就是等于区域的左边界

p[0]-1 为小于区域的右边界

然后在小于区域上递归

p[1]是等于区域的右边界

p[1]+1是大于区域的第一个数的位置

在右侧范围上递归

partition就是荷兰国旗问题

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值