首页 > 其他分享 >排序实练(2):列表排序-冒泡法/选择排序/快速排序

排序实练(2):列表排序-冒泡法/选择排序/快速排序

时间:2022-11-29 14:08:48浏览次数:65  
标签:sort 实练 65 49 冒泡排序 冒泡 排序 numbers

排序算法有很多,包括​​​插入排序​​​,​​堆排序​​​,​​归并排序​​​,​​选择排序​​​,​​计数排序​​​,​​基数排序​​​,​​桶排序​​​,​​快速排序​​等。插入排序,堆排序,选择排序,归并排序和快速排序都是比较排序,它们通过对数组中的元素进行比较来实现排序,其他排序算法则是利用非比较的其他方法来获得有关输入数组的排序信息。

​http://anony3721.blog.163.com/blog/#m=0&t=1&c=fks_081066081086081067072083087066093081085​

1.2 冒泡排序

​冒泡​​​排序(BubbleSort)的基本概念是:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。至此第一趟结束,将最大的数放到了最后。在第二趟:仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再小于第2个数),将小数放前,大数放后,一直比较到倒数第二个数(倒数第一的位置上已经是最大的),第二趟结束,在倒数第二的位置上得到一个新的最大数(其实在整个数列中是第二大的数)。如此下去,重复以上过程,直至最终完成排序。  由于在排序过程中总是小数往前放,大数往后放,相当于气泡往上升,所以称作冒泡排序。  用​​二重循环​​实现,外循环变量设为i,内循环变量设为j。假如有10个数需要进行排序,则外循环重复9次,内循环依次重复9,8,...,1次。每次进行比较的两个元素都是与内循环j有关的,它们可以分别用a[j]和a[j+1]标识,i的值依次为1,2,...,9,对于每一个i,j的值依次为1,2,...10-i。

  在许多程序设计中,我们需要将一个数列进行排序,以方便统计,而冒泡排序一直由于其简洁的思想方法而倍受青睐。有两个优点:1.“编程复杂度”很低,很容易写出代码;2.具有稳定性,这里的稳定性是指原序列中相同元素的相对顺序仍然保持到排序后的序列,而堆排序、快速排序均不具有稳定性。

        若记录序列的初始状态为"正序",则冒泡排序过程只需进行一趟排序,在排序过程中只需进行n-1次比较,且不移动记录;反之,若记录序列的初始状态为"逆序",则需进行n(n-1)/2次比较和记录移动。因此冒泡排序总的时间复杂度为O(n*n)。

冒泡排序法存在的不足及改进方法:  

        第一,在排序过程中,执行完最后的排序后,虽然数据已全部排序完备,但程序无法判断是否完成排序,为了解决这一不足,可设置一个标志位flag,将其初始值设置为非0,表示被排序的表是一个无序的表,每一次排序开始前设置flag值为0,在进行数据交换时,修改flag为非0。在新一轮排序开始时,检查此标志,若此标志为0,表示上一次没有做过交换数据,则结束排序;否则进行排序;

  第二,当排序的数据比较多时排序的时间会明显延长。改进方法:​​快速排序​​:具体做法:任意选取某一记录(通常取第一个记录),比较其关键字与所有记录的关键字,并将关键字比它小的记录全部放在它的前面,将比它大的记录均存放在它的后面,这样,经过一次排序之后,可将所有记录以该记录所在的分界点分为两部分,然后分别对这两部分进行快速排序,直至排序完  局部冒泡排序算法对冒泡排序的改进:  在冒泡排序中,一趟扫描有可能无数据交换,也有可能有一次或多次数据交换,在传统的冒泡排序算法及近年来的一些改进的算法中,只记录一趟扫描有无数据交换的信息,对数据交换发生的位置信息则不予处理。为了充分利用这一信息,可以在一趟全局扫描中,对每一反序数据对进行局部冒泡排序处理,称之为局部冒泡排序。局部冒泡排序与冒泡排序算法具有相同的时间复杂度,并且在正序和逆序的情况下,所需的关键字的比较次数和移动次数完全相同。由于局部冒泡排序和冒泡排序的数据移动次数总是相同的,而局部冒泡排序所需关键字的比较次数常少于冒泡排序,这意味着局部冒泡排序很可能在平均比较次数上对冒泡排序有所改进,当比较次数较少的优点不足以抵消其程序复杂度所带来的额外开销,而当数据量较大时,局部冒泡排序的时间性能则明显优于冒泡排序。

    #!/usr/bin/python
# -*- coding: UTF-8 -*-

def  bubble_sort(sort_list):
    iter_len = len(sort_list)
    if iter_len < 2:
        return sort_list
    for i in range (iter_len-1):
        for j in range (iter_len-i-1):
            if sort_list[j] > sort_list[j+1]:
                sort_list[j], sort_list[j+1] = sort_list[j+1], sort_list[j]    
    
    return sort_list
    

if __name__ == "__main__":

    #num = raw_input("enter the numbers : ").split(' ')
    num = [12, 1, 65, 11, 24, 22, 98, 10]
    print num
    print "the sorted nums are :"
    print bubble_sort(num)
    #x = num
    #x.sort(key=lambda x:int(x))       #通过对输入的数排序时用此法
    #print x
>>>
[12, 1, 65, 11, 24, 22, 98, 10]
the sorted nums are :
[1, 10, 11, 12, 22, 24, 65, 98]
>>>


​说明:
sort_list[i], sort_list[location]​
​​ ​​​=​​​ ​​​ sort_list[location], sort_list[i]​


这是交换两个数的做法,通常在其他语言中如果要交换a与b的值,常常需要一个中间变量temp,首先把a赋给temp,然后把b赋给a,最后再把temp赋给b。但是在python中你就可以这么写:a, b = b, a,其实这是因为赋值符号的左右两边都是元组(这里需要强调的是,在python中,元组其实是由逗号“,”来界定的,而不是括号)。


1.3 选择排序

每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。

【示例】:

  初始关键字 [49 38 65 97 76 13 27 49]

  第一趟排序后 13 [38 65 97 76 49 27 49]

  第二趟排序后 13 27 [65 97 76 49 38 49]

  第三趟排序后 13 27 38 [97 76 49 65 49]

  第四趟排序后 13 27 38 49 [76 97 65 49 ]

  第五趟排序后 13 27 38 49 49 [97 65 76]

  第六趟排序后 13 27 38 49 49 65 [97 76]

  第七趟排序后 13 27 38 49 49 65 76 [97]

  最后排序结果 13 27 38 49 49 65 76 97

​选择排序法​​的第一层循环从起始元素开始选到倒数第二个元素,主要是在每次进入的第二层循环之前,将外层循环的下标赋值给临时变量,接下来的第二层循环中,如果发现有比这个最小位置处的元素更小的元素,则将那个更小的元素的下标赋给临时变量,最后,在二层循环退出后,如果临时变量改变,则说明,有比当前外层循环位置更小的元素,需要将这两个元素交换.

n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果:

  ①初始状态:无序区为R[1..n],有序区为空。

  ②第1趟排序  在无序区R[1..n]中选出关键字最小的记录R[k],将它与无序区的第1个记录R[1]交换,使R[1..1]和R[2..n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。  ……

  ③第i趟排序  第i趟排序开始时,当前有序区和无序区分别为R[1..i-1]和R(i..n)。该趟排序从当前无序区中选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1..i]和R分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区。  这样,n个记录的文件的直接选择排序可经过n-1趟直接选择排序得到有序结果。

  常见的选择排序细分为简单选择排序、树形选择排序(​​锦标赛​​排序)、堆排序。上述算法仅是简单选择排序的步骤。

       选择排序的交换操作介于 0 和 (n - 1) 次之间。选择排序的比较操作为 n (n - 1) / 2 次之间。选择排序的赋值操作介于 0 和 3 (n - 1) 次之间。

  比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2。交换次数O(n),最好情况是,已经有序,交换0次;最坏情况是,逆序,交换n-1次。交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快。

!/usr/bin/python
# -*- coding: UTF-8 -*-

def  selection_sort(sort_list):
    iter_len = len(sort_list)
    if iter_len < 2:
        return sort_list
    for i in range (iter_len-1):
        smallest = sort_list[i]
        location = i
        for j in range (i,iter_len):
            if sort_list[j] < smallest:
                smallest = sort_list[j]
                location = j
        if i != location:
            sort_list[i], sort_list[location] = sort_list[location], sort_list[i]    
    
    return sort_list
    

if __name__ == "__main__":

    #num = raw_input("enter the numbers : ").split(' ')
    num = [12, 1, 65, 11, 24, 22, 98, 10]            #逗号间隔
    print num
    print "the sorted nums are :"
    print selection_sort(num)
    #x = num
    #x.sort(key=lambda x:int(x))       #通过对输入的数排序时用此法
    #print x

>>>
[12, 1, 65, 11, 24, 22, 98, 10]
the sorted nums are :
[1, 10, 11, 12, 22, 24, 65, 98]
>>>
1.4 快速排序

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

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

        一趟快速排序的算法是:

  1)设置两个变量I、J,排序开始的时候:I=0,J=N-1;

  2)以第一个数组元素作为关键数据,赋值给key,即 key=A[0];

  3)从J开始向前搜索,即由后开始向前搜索(J=J-1即J--),找到第一个小于key的值A[j],A[j]与A[i]交换;

  4)从I开始向后搜索,即由前开始向后搜索(I=I+1即I++),找到第一个大于key的A[i],A[i]与A[j]交换;

  5)重复第3、4、5步,直到 I=J; (3,4步是在程序中没找到时候j=j-1,i=i+1,直至找到为止。找到并交换的时候i, j指针位置不变。另外当i=j这过程一定正好是i+或j-完成的最后令循环结束。)

  示例:待排序的数组A的值分别是:(初始关键数据:key=49) 注意关键key永远不变,永远是和key进行比较,无论在什么位置,最后的目的就是把key放在中间,小的放前面大的放后面。   

A[0]

A[1]

A[2]

A[3]

A[4]

A[5]

A[6]

49

38

65

97

76

13

27

 进行第一次交换后:27 38 65 97 76 13 49  ( 按照算法的第三步从后面开始找,此时:J=6)

  进行第二次交换后:27 38 49 97 76 13 65   ( 按照算法的第四步从前面开始找>key的值,65>49,两者交换,此时:I=2 )

  进行第三次交换后:27 38 13 97 76 49 65   ( 按照算法的第五步将又一次执行算法的第三步从后开始找

  进行第四次交换后:27 38 13 49 76 97 65   ( 按照算法的第四步从前面开始找大于key的值,97>49,两者交换,此时:I=3,J=5 )

  此时再执行第三步的时候就发现I=J=3,从而结束一趟快速排序,那么经过一趟快速排序之后的结果是:27 38 13 49 76 97 65,即所有大于key49的数全部在49的后面,所有小于key(49)的数全部在key(49)的前面。

快速排序(Quicksort)有几个值得一提的变种算法,这里进行一些简要介绍:

  随机化快排:快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。一位前辈做出了一个精辟的总结:“随机化快速排序可以满足一个人一辈子的人品需求。”  随机化快速排序的唯一缺点在于,一旦输入数据中有很多的相同数据,随机化的效果将直接减弱。对于极限情况,即对于n个相同的数排序,随机化快速排序的时间复杂度将毫无疑问的降低到O(n^2)。解决方法是用一种方法进行扫描,使没有交换的情况下主元保留在原位置。

  平衡快排(Balanced Quicksort):每次尽可能地选择一个能够代表中值的元素作为关键数据,然后遵循普通快排的原则进行比较、替换和递归。通常来说,选择这个数据的方法是取开头、结尾、中间3个数据,通过比较选出其中的中值。取这3个值的好处是在实际问题(例如信息学竞赛……)中,出现近似顺序数据或逆序数据的概率较大,此时中间数据必然成为中值,而也是事实上的近似中值。万一遇到正好中间大两边小(或反之)的数据,取的值都接近最值,那么由于至少能将两部分分开,实际效率也会有2倍左右的增加,而且利于将数据略微打乱,破坏退化的结构。

  外部快排(External Quicksort):与普通快排不同的是,关键数据是一段buffer,首先将之前和之后的M/2个元素读入buffer并对该buffer中的这些元素进行排序,然后从被排序数组的开头(或者结尾)读入下一个元素,假如这个元素小于buffer中最小的元素,把它写到最开头的空位上;假如这个元素大于buffer中最大的元素,则写到最后的空位上;否则把buffer中最大或者最小的元素写入数组,并把这个元素放在buffer里。保持最大值低于这些关键数据,最小值高于这些关键数据,从而避免对已经有序的中间的数据进行重排。完成后,数组的中间空位必然空出,把这个buffer写入数组中间空位。然后递归地对外部更小的部分,循环地对其他部分进行排序。

  三路基数快排(Three-way Radix Quicksort,也称作Multikey Quicksort、Multi-key Quicksort):结合了​​基数排序​​​(radix sort,如一般的​​字符串​​比较排序就是基数排序)和快排的特点,是字符串排序中比较高效的算法。该算法被排序数组的元素具有一个特点,即multikey,如一个字符串,每个字母可以看作是一个key。算法每次在被排序数组中任意选择一个元素作为关键数据,首先仅考虑这个元素的第一个key(字母),然后把其他元素通过key的比较分成小于、等于、大于关键数据的三个部分。然后递归地基于这一个key位置对“小于”和“大于”部分进行排序,基于下一个key对“等于”部分进行排序。

#!/usr/bin/python
# -*- coding: UTF-8 -*-

def quick_sort(numbers, left, right, partion_func = None):
    if left < right:          #只有left小于right的时候才有必要排序
        if partion_func:
            middle = partion_func(numbers, left, right)#确定基准位置
        else:
            middle = partion(numbers, left, right)
        quick_sort(numbers, left, middle - 1)#基准位置左边的元素
        quick_sort(numbers, middle + 1, right)#基准位置右边的元素

    
def partion(numbers, left, right):#确定基准位置,比它小的移到它左边,大的移到它右边
    i = left
    for j in range(left, right):
        if numbers[j] <= numbers[right]:
            numbers[i], numbers[j] = numbers[j], numbers[i]
            i = i + 1
    numbers[i], numbers[right] = numbers[right], numbers[i]
    return i

def main():
    numbers1 = [12, 1, 65, 11, 24, 22, 98, 10]
    print numbers1
    print "the sorted nums are :"
    quick_sort(numbers1, 0, len(numbers1) - 1)
    print numbers1

    
      

if __name__ == "__main__":
    main()

    #num = raw_input("enter the numbers : ").split(' ')

        
    #x = num
    #x.sort(key=lambda x:int(x))       #通过对输入的数排序时用此法
    #print x

>>>
[12, 1, 65, 11, 24, 22, 98, 10]
the sorted nums are :
[1, 10, 11, 12, 22, 24, 65, 98]
>>>

法二:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

def quick_sort(numbers, left, right, partion_func = None):
    if left < right:          #只有left小于right的时候才有必要排序
        if partion_func:
            middle = partion_func(numbers, left, right)#确定基准位置
        else:
            middle = partion(numbers, left, right)
        quick_sort(numbers, left, middle - 1)#基准位置左边的元素
        quick_sort(numbers, middle + 1, right)#基准位置右边的元素

    
def partion(numbers, left, right):#确定基准位置,比它小的移到它左边,大的移到它右边
    i = left
    for j in range(left, right):
        if numbers[j] <= numbers[right]:
            numbers[i], numbers[j] = numbers[j], numbers[i]
            i = i + 1
    numbers[i], numbers[right] = numbers[right], numbers[i]
    return i

def main():
    numbers2 = [12, 1, 65, 11, 24, 22, 98, 10]
    print numbers2
    print "the sorted nums are :"
    quick_sort(numbers2, 0, len(numbers2) - 1, partion)  
    print numbers2
    
  
if __name__ == "__main__":
    main()


>>>
[12, 1, 65, 11, 24, 22, 98, 10]
the sorted nums are :
[1, 10, 11, 12, 22, 24, 65, 98]
>>>











标签:sort,实练,65,49,冒泡排序,冒泡,排序,numbers
From: https://blog.51cto.com/u_15797945/5894893

相关文章

  • acwing113. 特殊排序
    记录交互题这个东西 classSolution{public:vector<int>specialSort(intN){vector<int>res;res.push_back(1);for(inti=2;i<......
  • 5.2.5 快速排序——代码解说
    需思考一个巧妙的办法,在这个数组里头,原地进行这个数组元素倒换,实现参照元素在它该到达的位置去存放,左边的元素都比它小,右边的元素都比它大,不分配动态数组。保证整体左边小,......
  • 5.2.1 归并排序——总体思路
    折半查找快速是因为每次只查一半,另一半不管把一个任务拆成两个部分只完成其中一部分,是一个很有效的办法当元素多了,运算、时间消耗等会比较复杂数组前一半让它有序,后一半......
  • 与堆和堆排序相关的问题
    与堆和堆排序相关的问题作者:Grey原文地址:博客园:与堆和堆排序相关的问题CSDN:与堆和堆排序相关的问题堆结构说明堆结构就是用数组实现的完全二叉树结构,什么是完全二叉......
  • PTA 21级数据结构与算法实验8—排序
    目录7-1统计工龄7-2寻找大富翁7-3点赞狂魔7-4插入排序还是归并排序7-5插入排序还是堆排序7-6逆序对7-7堆排序7-8石子合并7-9第k小7-10快速排序的过程7-1统计工......
  • DataGridView绑定BindingList 中的 DataGridViewCheckBoxColumn 无法点击排序问题
    参考文档DataGridView绑定BindingList<T>带数据排序的类-腾讯云开发者社区-腾讯云(tencent.com) DataGridView使用技巧十三:点击列头实现升序和降序排序-.NET开发......
  • 自然排序
    自然排序      如果数组中部分元素已按自然数顺序排放,例如,数组,则初期自然排好序的子数组段显然有4段,分别为,,和。请充分利用上述特点设计并实现一个自然合并排序算法......
  • 堆排序算法
    堆排序算法堆排序定义:堆排序是将一组无序数组(二叉树)构建成一个堆,分为大顶堆和小顶堆大顶堆:父节点的值永远大于其左子树和右子树的值堆排序思路:将堆顶元素与末尾元......
  • Java数组排序
       今天,巩固教大家数组排序方法,我将介绍以下这几种方式:快速排序,冒泡排序,选择排序。1、快速排序这就是各位学Java的福利了,Java提供sort()方法,咱们只......
  • 07#Web 实战:实现 GitHub 个人主页项目拖拽排序
    实现效果图GitHub和Gitee个人主页中可以对自己的项目进行拖拽排序,于是我就想自己实现一个。本随笔只是记录一下大概的实现思路,如果感兴趣的小伙伴可以通过代码和本随......