您好,欢迎来到九壹网。
搜索
您的当前位置:首页python的两种随机数选取key值的快速排序算法的实现

python的两种随机数选取key值的快速排序算法的实现

来源:九壹网
python的两种随机数选取key值的快速排序算法的实现

零:环境python 3.6.5

JetBrains PyCharm 2018.1.4 x⼀:基于百度百科的思路

源地址:https://baike.baidu.com/item/快速排序算法/369842?fromtitle=快速排序&fromid=2084344&fr=aladdin原⽂:

设要排序的是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-完成的时候,此时令循环结束)。  分析:

    这种快速排序的⽅法可以理解成通过⼀轮交换将key值交换到某个正确位置上,该位置的左侧的全部元素都不⾼于key,右侧元素都不低于key

    百度百科选取的左侧的第⼀个元素作为key值,然后⾸先从右侧开始找,使得在右侧的合适的值找到之后,发⽣交换时,key值肯定会随之被来回交换,可以理解成每次寻找都是在找key值对⾯⼀侧的适合元素,key值被动的变化位置,到最后被交换到key所应该在的合适的位置

    ⽽random选取随机值的话因为key位置的不确定性,所以为了在遇到key值时,为了能将key值也像百度百科那样随之来回交换到正确位置,在寻找合适元素时的判断条件不应该添加等号,以下是例⼦。    

[76, 2, 32, 4, 56, 65, 45, 78, 32, 96, 32, 32, 42, 1, 78, 4, 126]

v_数组 = [126, 2, 32, 4, 56, 65, 45, 78, 32, 96, 32, 32, 42, 1, 78, 4, 76]

    

    ⾸先⼀开始假设我们随机到第五个元素为key值,即56为key值,左边界为0,右边界为len(v_数组)-1,即16    1:第⼀轮交换

    假设我们先从左侧开始找

    根据上⾯的快速排序原则,我们从左边找的时候先找第⼀个⽐key值⼤的数,但是为了让我们能够匹配到key值,我们要先找第⼀个⼤于等于key值的数

    根据描述可以看到第⼀个元素就是    所以我们开始进⼊到元素交换环节    

v_数组[v_左边界],v_数组[v_右边界] = v_数组[v_右边界],v_数组[v_左边界]

    此时的数组情况应该为v_左边界:0  v_右边界:16

[76, 2, 32, 4, 56, 65, 45, 78, 32, 96, 32, 32, 42, 1, 78, 4, 126]

    2:第⼆轮交换

    左侧找完了我们该从右侧开始找

    同上⾯的原理,找到第⼀个⼩于等于key的数

    因为126⼤于key值56,所以v_右边界-=1

    此时5[4, 2, 32, 4, 56, 65, 45, 78, 32, 96, 32, 32, 42, 1, 78, 76, 126]

    3:第三轮交换

    这时⼜开始从左侧开始找

    我们左侧的v_左边界在+1过程中遇到了key值56,为了抓住这个变量我们要就这样停⽌下来,开始交换    此时的数组情况应该为v_左边界:4  v_右边界:15

[4, 2, 32, 4, 76, 65, 45, 78, 32, 96, 32, 32, 42, 1, 78, 56, 126]

    ……以此类推

    结束条件应当是v_左边界<=v_old右边界(v_old右边界为⼀开始的右边界,因为左右边界⼀直在变化所以先提取存起来)    结束的时候v_左边界就是key值所在位置

    但是,假如左侧和右侧都找到了和key⼀样的key值的话就陷⼊了死循环

    所以为了避免死循环应该在循环判断时添加⼀个条件,如果左右的值相等且都为key的话,那么从左侧找时那就继续+1,从右侧找时那就继续-1

    其实交换是可以节省的,可以左右都找到或到临界点时才交换,    以下就是节省了⼀次交换的参考代码:

1 def f_第四题_左右交换_随机key(v_数组,t_左边界,t_右边界,t_key位置):

2 t_old左边界, t_old右边界, t_oldKey = t_左边界, t_右边界, v_数组[t_key位置] 3 while t_左边界 < t_右边界:

4 # ⽤⼩于号或⼤于号⽽不加上等于号是为了把key值能够交换到中间位置⽽省去了移除插⼊操作 5 # 判断左右相等是为了防⽌左右都遇到了和key⼀样的值⽽死循环

6 while (v_数组[t_左边界] < t_oldKey or v_数组[t_左边界] == v_数组[t_右边界] == t_oldKey) and t_左边界 < t_右边界 : 7 t_左边界 += 1

8 while (v_数组[t_右边界] > t_oldKey or v_数组[t_左边界] == v_数组[t_右边界] == t_oldKey) and t_左边界 < t_右边界: 9 t_右边界 -= 1

10 if t_左边界 < t_右边界:

11 v_数组[t_左边界], v_数组[t_右边界] = v_数组[t_右边界], v_数组[t_左边界]12 pass

13 if t_old左边界 < t_左边界 - 1:

14 f_第四题_左右交换_随机key(g_数组,t_old左边界, t_左边界 - 1, random.randint(t_old左边界, t_左边界 - 1))15 if t_左边界 + 1 < t_old右边界:

16 f_第四题_左右交换_随机key(g_数组,t_左边界 + 1, t_old右边界, random.randint(t_左边界 + 1, t_old右边界))17 pass

    其实⾮递归写法也⽐较简单

    可以定义⼀个伪栈v_上下界栈,在栈⾥添加左右边界和key位置,然后在函数退出时再Pop最后⼀个元素    以下是参考代码:

1 def f_第四题_左右交换_随机key(v_数组,t_左边界,t_右边界,t_key位置): 2 \"\"\"

3 百度的快排的⽆递归算法实现 4 :param v_数组: 数组参数

5 :param t_左边界: 想要排序的数组的左边界 6 :param t_右边界: 想要排序的数组的右边界 7 :param t_key位置: key的位置

8 :return: ⽆返回值暂时,可以修改…… 9 \"\"\"

10 v_上下界栈 = [[t_左边界, t_右边界, v_数组[random.randint(t_左边界,t_右边界)]]]11

12 while v_上下界栈:

13 t_左边界,t_右边界,t_oldKey = v_上下界栈[-1][0],v_上下界栈[-1][1],v_上下界栈[-1][2]14 while t_左边界 < t_右边界:

15 while (v_数组[t_左边界] < t_oldKey or v_数组[t_左边界] == v_数组[t_右边界] == t_oldKey) and t_左边界 < t_右边界:16 t_左边界 += 1

17 while (v_数组[t_右边界] > t_oldKey or v_数组[t_左边界] == v_数组[t_右边界] == t_oldKey) and t_左边界 < t_右边界:18 t_右边界 -= 1

19 if t_左边界 < t_右边界:

20 v_数组[t_左边界], v_数组[t_右边界] = v_数组[t_右边界], v_数组[t_左边界]21 pass

22

23 if v_上下界栈[-1][0] < t_左边界 - 1:

24 v_上下界栈.insert(-2, [v_上下界栈[-1][0], t_左边界 - 1, v_数组[random.randint(v_上下界栈[-1][0], t_左边界 - 1)]])25 if t_左边界 + 1 < v_上下界栈[-1][1]:

26 v_上下界栈.insert(-2, [t_左边界 + 1, v_上下界栈[-1][1], v_数组[random.randint(t_左边界 + 1, v_上下界栈[-1][1])]])27 v_上下界栈.pop(-1)28 pass29 pass

⼆:⽼师所讲的思路

    ⽼师的思路是都从⼀侧开始寻找

    1:先找第⼀个⽐key⼤的数,我们⽤v_下标来遍历,找到之后那个位置我们就认为他是key值可能会插⼊的位置,并将v_下标赋值给v_插⼊位置。

      从左往右找的效果会造成v_插⼊位置处的值的左侧必定都不⼤于key,但是右侧并不保证

    2:在找到可能的插⼊位置之后再从v_插⼊位置处开始定义v_下标,v_下标负责寻找第⼀个⽐key⼩的值,然后找到之后将v_插⼊位置处的值与v_下标处的值交换,此时v_插⼊位置处的值是⽐key⼩的,所以v_插⼊位置要+=1    3:此时v_下标继续往后查找直到结尾    以下是例⼦

    我们还以那个数组为例     

v_数组 = [126, 2, 32, 4, 56, 65, 45, 78, 32, 96, 32, 32, 42, 1, 78, 4, 76]

    我们还是假设56是key值    1:第⼀步

    从左侧的第⼀个元素开始寻找第⼀个⽐key⼤的数,我们可以看到第⼀个元素就是    所以定义v_插⼊位置 = 0    2:第⼆步

    定义v_下标 = v_插⼊位置

    开始寻找第⼀个⽐key⼤的数,此时v_下标为1    所以我们要开始交换了    结果如下

[2, 126, 32, 4, 56, 65, 45, 78, 32, 96, 32, 32, 42, 1, 78, 4, 76]

    3:第三步

    v_插⼊位置处的值变成了⼩于key的值,所以v_插⼊位置需要加⼀

    但是有⼀种特殊情况,当加⼀后遇到了key值之后我们要跳过这个key,所以还要继续+1    4:重复第⼆三步

    因为我们忽略了key,所以key的位置必定不变,交换到最后v_插⼊位置的左侧必定都⼩于等于key,右侧都⼤于等于key,但是key还在原位,我们需要最后将key插⼊到v_插⼊位置然后再删除原来位置的key    以下是参考代码,关于特殊情况会在代码⾥注释

1 def f_随机数版本(p_列表,p_左边界,p_右边界,p_key位置): 2 v_插⼊位置,v_下标 = p_左边界,p_左边界 3 v_key = p_列表[p_key位置] 4 # 跳过key位置

5 # 因为我们这⾥最后是先insert再pop,所以插⼊到p_右边界+1即为插⼊到最后,不会出错 6 while v_下标<=p_右边界 and (p_列表[v_下标] < v_key or v_下标==p_key位置): 7 v_下标+=1

8 # 插⼊位置更新为下标位置

9 v_插⼊位置 = v_下标

10 # 带等于号是为了能够判断到最后⼀个元素11 while v_下标<=p_右边界:

12 while v_下标 <= p_右边界 and (p_列表[v_下标] >= v_key or v_下标==p_key位置):13 v_下标+=1

14 # 当v_下标有意义时,才交换15 if v_下标<=p_右边界:

16 p_列表[v_下标],p_列表[v_插⼊位置] = p_列表[v_插⼊位置],p_列表[v_下标]17 v_插⼊位置+=118 # 跳过key位置

19 if v_插⼊位置 == p_key位置:20 v_插⼊位置+=121 # 将Key值插⼊到指定位置

22 p_列表.insert(v_插⼊位置,v_key)

23 # 如果插⼊位置在key之前,则key位置就会往后推迟⼀位,所以需要pop +1 24 if v_插⼊位置 < p_key位置:25 p_列表.pop(p_key位置 + 1)26 else:

27 p_列表.pop(p_key位置)

28 # 插⼊位置在key之后的话,因为pop了key,所以插⼊位置处前⾯的元素会少,插⼊位置会指向下⼀个元素,所以为了正确指向key要-129 v_插⼊位置-=1

30 if v_插⼊位置-1>p_左边界:

31 f_随机数版本(p_列表,p_左边界,v_插⼊位置-1,random.randint(p_左边界,v_插⼊位置-1))32 if v_插⼊位置+133 f_随机数版本(p_列表,v_插⼊位置+1,p_右边界,random.randint(v_插⼊位置+1,p_右边界))34

35 pass

    以上皆为⾃⼰的思考    转载请注明出处

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务