排序是计算机程序设计中的一种重要操作,它的功能是将一个数据元素的任意序列,重新排列成一个关键字有序的序列。

一、冒泡排序 BubbleSort

介绍:

冒泡排序的原理非常简单,它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。
步骤:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对第0个到第n-1个数据做同样的工作。这时,最大的数就“浮”到了数组最后的位置上。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

    稳定性:

    冒泡排序在相邻元素相等不会交换位置,所以他是稳定排序

    适用场景:

    适合小数据的排序

    排序演示:

    冒泡.gif
    源代码:
    1. def bubble(nums):
    2. count = 0
    3. for i in range(1, len(nums)):
    4. fl = True
    5. for j in range(len(nums) - i):
    6. if nums[j] > nums[j + 1]:
    7. nums[j], nums[j + 1] = nums[j + 1], nums[j]
    8. count += 1
    9. print('第%s轮排序结果' % (count), nums)
    10. return nums

    不过针对上述代码还有优化方案。
    优化1:某一趟遍历如果没有数据交换,则说明已经排好序了,因此不用再进行迭代了。用一个标记记录这个状态即可。
    1. def bubble(nums):
    2. count = 0
    3. for i in range(1, len(nums)):
    4. flag = True
    5. for j in range(len(nums) - i):
    6. if nums[j] > nums[j + 1]:
    7. nums[j], nums[j + 1] = nums[j + 1], nums[j]
    8. flag=False
    9. count += 1
    10. print('第%s轮排序结果' % (count), nums)
    11. if flag:
    12. break
    13. return nums

二、选择排序 SelectionSort

介绍:
选择排序是一种最简单直观的排序,和冒泡有一定的相似度.可以说是冒泡排序的改进
步骤:

  1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 以此类推,直到所有元素均排序完毕。

    稳定性:

    他是一种不稳定的排序方法

    适用场景:

    适用于简单数据排序,优于冒泡,但大数据面前有些力不从心.

    排序演示:

    选择.jpg
    源代码:
    1. def selection_sort(nums):
    2. count=0
    3. for i in range(len(nums)-1):
    4. for j in range(i+1,len(nums)):
    5. if nums[i]>nums[j]:
    6. count+=1
    7. nums[i],nums[j]=nums[j],nums[i]
    8. print('第%s次结果为'%(count),nums)
    9. print(nums)

三、插入排序 InsertionSort

介绍:
插入排序的工作原理是,通过构建有序序列,对于每个未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
步骤:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果被扫描的元素(已排序)大于新元素,将该元素后移一位
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5步

    稳定性:

    由于只需要找到不大于当前数的位置而不需要交换,因此,插入排序是稳定的

    适用场景:

    适用于数据比较少的时候
    排序演示:
    插入.jpg
    源代码:
    1. def insert_sort(nums:List):
    2. for r in range(1,len(nums)):
    3. target=nums[r]
    4. for l in range(r):
    5. if nums[l]>nums[r]:
    6. nums[l+1:r+1]=nums[l:r]
    7. nums[l]=target
    8. break
    9. return nums

四、计数排序 Counting sort

介绍:
计数排序不是比较的排序算法,核心是把输入的数据转化伪建存在额外开辟的数组空间中.

步骤:

1.找出待排数组中最大的值,开辟一份空间
2.放到新开辟的空间中
3.按顺利依次取出

稳定性:

计数排序是稳定的排序方式

适合场景:

计数排序是一种牺牲空间换取时间的排序方式,它仅适用于数据比较集中的情况

排序演示:

计数.gif

源代码:

  1. def countSort(arr:List):
  2. max_value=max(arr)
  3. count_arr=[0]*(max_value+1)
  4. for i in range(len(arr)):
  5. count_arr[arr[i]]+=1
  6. sort_arr=[]
  7. for i in range(len(count_arr)):
  8. for j in range(count_arr[i]):
  9. sort_arr.append(i)
  10. return sort_arr

五、归并排序 MergeSort

介绍:
归并排序是采用分治法的一个非常典型的应用。归并排序的思想就是先递分解数组,再并数组。
先考虑合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
再考虑递归分解,基本思路是将数组分解成leftright,如果这两个数组内部数据是有序的,那么就可以用上面合并数组的方法将这两个数组合并排序。如何让这两个数组内部是有序的?可以再二分,直至分解出的小组只含有一个元素时为止,此时认为该小组内部已有序。然后合并排序相邻二个小组即可。

稳定性:

归并排序严格遵循从左到右或从右到左的顺序合并子数据序列, 它不会改变相同数据之间的相对顺序, 因此归并排序是一种稳定的排序算法.

应用场景:

内存空间不足的时候使用归并排序
排序演示:
排序 - 图5
源代码:

  1. def mergeSort(nums):
  2. if len(nums)<=1:
  3. return nums
  4. middle=len(nums)//2
  5. left,right=nums[0:middle],nums[middle:]
  6. return merge(mergeSort(left),mergeSort(right))
  7. def merge(left,right):
  8. list=[]
  9. while left and right:
  10. if left[0]>=right[0]:
  11. list.append(right.pop(0))
  12. else:
  13. list.append(left.pop(0))
  14. if left:
  15. list.extend(left)
  16. if right:
  17. list.extend(right)
  18. return list

六、快速排序 QuickSort

介绍:
快速排序通常明显比同为Ο(n log n)的其他算法更快,因此常被采用,而且快排采用了分治法的思想,所以在很多笔试面试中能经常看到快排的影子。可见掌握快排的重要性。
步骤:

  1. 从数列中挑出一个元素作为基准数。
  2. 分区过程,将比基准数大的放到右边,小于或等于它的数都放到左边。
  3. 再对左右区间递归执行第二步,直至各区间只有一个数。

    稳定性:

    快速排序是一种不稳定的排序

    应用场景:

    适合大多数情况下适用
    排序演示:
    排序 - 图6
    源代码:
    1. def quickSort(array):
    2. if len(array)<2:
    3. return array
    4. else:
    5. pivot=array[0]
    6. less=[i for i in array[1:] if i <= pivot]
    7. greater=[i for i in array[1:] if i > pivot]
    8. return quickSort(less)+[pivot]+quickSort(greater)

七、桶排序 Bucket Sort

介绍:

它是将数组划分到一定数量的有序的桶里,然后再对每个桶中的数据进行排序,最后再将各个桶里的数据有序的合并到一起。

排序图示:

桶.jpg
源代码:

  1. def bucketSort(nums: list):
  2. maxnum = max(nums)
  3. minnum = min(nums)
  4. d = maxnum - minnum
  5. bucket_num = len(nums)
  6. count_list = []
  7. for i in range(bucket_num):
  8. count_list.append([])
  9. for i in range(len(nums)):
  10. num = int((nums[i] - minnum) * (bucket_num - 1) / d)
  11. bucket = count_list[num]
  12. bucket.append(nums[i])
  13. for i in range(len(count_list)):
  14. count_list[i].sort()
  15. sort_nums = []
  16. for i in count_list:
  17. for j in i:
  18. sort_nums.append(j)
  19. return sort_nums

总结

下面为七种经典排序算法指标对比情况:

排序法 平均时间 最差情形 稳定度
冒泡排序 O(n) O(n) 稳定
选择排序 O(n) O(n) 不稳定
快速排序 O(nlogn) O(n) 不稳定
插入排序 O(n) O(n) 稳定
计数排序 O(n+k) O(n+k) 稳定
归并排序 O(nlogn) O(nlogn) 稳定
桶排序 O(n+k) O(n+k) 不稳定