Python学习笔记:4.2.2 排序算法

这篇博客详细介绍了Python中的五种排序算法:选择排序、冒泡排序、插入排序、快速排序和合并排序。通过对每种排序算法的原理、示例和时间复杂度分析,帮助读者深入理解这些基本排序方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文是学习陆老师的《python全栈工程师 - 数据结构与算法》课程的笔记,欢迎学习交流。同时感谢陆老师的精彩传授!

一、课程目标
  • 选择排序
  • 冒泡排序
  • 插入排序
  • 快速排序
  • 合并排序
二、详情解读
01选择排序:

举例: [5, 2, 4 , 3, 1]

1.排序目的是在一个排好序的序列里面,从左到右是从小到大排序的,比如[1,2,3,4,5]
2.选择一个元素,然后与所有其他尚未排序元素比较,如果最小与左侧交换位置
3.第一轮:[1,2,4,3,5]
4.第二轮:[1,2,4,3,5]
5.第三轮:[1,2,3,4,5]

在这里插入图片描述

def select_sort(lists):
  '''
  选择排序
  '''
  max_index = len(lists)
  for i in range(max_index):
    min_index = i
    for j in range(i+1, max_index):
      if lists[min_index] > lists[j]:
        min_index = j
    if min_index != i:
       print('min_index=', min_index, ' ', lists[min_index], '<=>', lists[i])
       lists[i], lists[min_index] = lists[min_index], lists[i]
       print(lists)
       print('-'*100)
  return lists

lists = list(range(1, 6))
from random import shuffle

shuffle(lists)
print(lists)
print('end=>', select_sort(lists))

运行结果:
在这里插入图片描述
时间复杂度

1.选择排序包含了两个循环
2.总的次数是(n-1)+ (n - 2)+ … + 1
3.因此时间复杂度为n(n-1)/2, 即O(n^2)

02.冒泡排序:

举例:[5,2,4,3,1]

1.从第一个元素开始,每个元素与后面的一个元素比较,较大的排到后面
2.第一轮: [2,4,3,1,5]
3.第二轮:[2,3,1,4,5]
4.第三轮:[2,1,3,4,5]
5.第四轮:[1,2,3,4,5]

在这里插入图片描述

代码实现:

from random import shuffle

def bubble_sort(lists):
    '''
    冒泡排序
    '''
    max_index = len(lists) - 1
    for i in range(max_index):
        for j in range(max_index-i):
            if lists[j] > lists[j+1]:
                lists[j], lists[j+1] = lists[j+1], lists[j]
                print('i=', i, ':', 'j=', j, '=>', lists)
    return lists

lists = list(range(1, 6))
shuffle(lists)
print(lists)
print('end=>', bubble_sort(lists))

运行结果:
在这里插入图片描述
时间复杂度

1.冒泡排序包含了两个循环
2.总的次数是(n-1)+ (n - 2)+ … + 1
3.因此时间复杂度为n(n-1)/2, 即O(n^2)

03插入排序

1.插入排序从第2个元素开始排序,与该元素之前的元素比较
2.该元素之前的顺序已经排好
3.如果该元素顺序可以插入的话,将该元素按照顺序插入

举例: [5,2,4,3,1]

1.从4开始排,4之前的顺序片段:[5],4如果加入到这个片段,需要插入到5之前
i=1:j => 0 [5,5,3,1,2] - 将5移后一位,让出位置
i=1:j => -1 [4,5,3,1,2] - 将4插入到5之前
2.排3的时候,3之前的顺序片段:[4,5],3如果加入到这个片段,需要插入到4之前
3.将[4,5]后移
i = 2: j => 1 [4, 5, 6, 1, 2]
i = 2: j => 0 [4,4,5,1,2]
i = 2: j => -1 [3,4,5,1,2]

在这里插入图片描述
代码实现:

from random import shuffle

def insert_sort(lists):
    '''
    插入排序
    '''
    for i in range(1, len(lists)):
      # 准备被插入排序的元素
        item_to_insert = lists[i]
        # 将这个元素与它之前排好顺序的元素比较
        for j in range(i-1, -2, -1):
          # 如果这个元素比它前面的元素小,就把它插入进去
          # 插入过程就是前面的元素一个个后移,直到大于其中的某个元素
            if item_to_insert < lists[j]:
                lists[j+1] = lists[j]
            else:
                break
        # 插入空出来的位置
        # 在 j 位置的元素比等待插入的元素要小,所以插入位置为 j+1
        lists[j+1] = item_to_insert
    return lists

l = [i for i in range(1, 20)]
shuffle(l)
print(l)
print('end=>', insert_sort(l))

运行结果:
在这里插入图片描述
时间复杂度:

1.插入排序包含了两个循环
2.总的次数是(n - 1) + (n - 2 )+ … + 1
3.因此时间复杂度为n(n-1)/2, 即 O(n^2)

04.快速排序

举例:[5,2,4,3,1]

1.找一个基准点,比如4,
2.以4交换到最后一项[5,2,3,1,4]
3.在列表开始处设立一个边界标志,比如现在[:5,2,3,1,4]的5左侧:为边界标志
4.小于4的项放到边界左侧,直到所有小于4的放到边界左侧,每在边界左侧放置一个,边界位置右移一次,边界右侧的都比4大
5.[2,3,1,:5,4]
6.然后将4放置边界标志:的左侧 [2,3,1,4,:5]
7.在边界两侧重复以上过程,直到分组里只有一个元素

举例:[5,2,4,3,1]

1.第二轮:[2, 3, 1][4][5]
通过第一轮,将列表分成了左右两部分,左边的是[2,3,1,4],右边是[5],右边只剩下一个,就不用排了
2.在[2,3,1]中选3,那么[2,3,1],2就是边界标志
3.将3移到最右边[2,1,3]
4.[:2,1,3],然后将1放置于2的左侧,[1,:2,3],2也移到边界左边,边界移到[1,2,:3],[1,:2,3]就排好了

在这里插入图片描述
代码实现:

def quicksort(lists):
  # 初始左边界为0,右边界为列表上界
  quick_sort_helper(lists, 0, len(lists)-1)
  return lists

def quick_sort_helper(lists, left, right):
  # 边界左边不能超越边界右边
  # print('left=', left, 'right=', right)
  if left < right:
    # 分割列表设置分界点
    boundary = partition(lists, left, right)
    # 分界点左侧接着分割
    quick_sort_helper(lists, left, boundary-1)
    # 分界点右侧接着分割
    quick_sort_helper(lists, boundary+1, right)

def partition(lists, left, right):
  # 根据left, right寻找分界点
  middle = (left + right) // 2
  # 将分界点放置于该段最右侧
  lists[right], lists[middle] = lists[middle], lists[right]
  boundary = left
  # 将小于分界点的排到左边,边界右移+1
  for i in range(left, right):
    print(lists)
    if lists[i] < lists[right]:
      lists[i], lists[boundary] = lists[boundary], lists[i]
      boundary += 1
  # 再将分界点移回
  lists[right], lists[boundary] = lists[boundary], lists[right]
  return boundary

lists = list(range(1, 6))
from random import shuffle
shuffle(lists)
print(lists)
print(quicksort(lists))

运行结果:
在这里插入图片描述
时间复杂度:

1.第一次是O(n)
2.由于每次是分割成2部分,大概log2n次可以得到一个元素
3.最好的水平是O(nlog2n)
4.最坏的是每个元素都作为一次分割点,O(n^2)

05.合并排序

举例:[5, 2, 4, 3, 1]

1.选一个点将列表分成左右3部分[2, 3, 1] + [4] + [5]
2.左边的列表所有的元素< 分割点
3.右边的列表所有的元素> 分割点
4.将左边的部分[2, 3, 1]重复1~3步骤[2, 1] + [3] + []
5.将[2, 1]重复1~3步骤[] + [1] + [2]
6.通过递归将他们合并起来[] + [1] + [2] + [3] + [] + [4] + [5]

在这里插入图片描述

在这里插入图片描述
代码实现:

def merge_sort(lists):
  if len(lists) <= 1:
    return lists
  
  middle = len(lists) // 2
  left_lists = []
  right_lists = []

  for i in lists[:middle]:
    if i < lists[middle]:
      left_lists.append(i)
    else:
      right_lists.append(i)
  
  for i in lists[middle+1:]:
    if i < lists[middle]:
      left_lists.append(i)
    else:
      right_lists.append(i)
  print(left_lists, right_lists)
  return merge_sort(left_lists) + [lists[middle]] + merge_sort(right_lists)

lists = list(range(1, 10))
from random import shuffle
shuffle(lists)
print(lists)
print('end=>', merge_sort(lists))

运行结果:
在这里插入图片描述

时间复杂度

1.由于每次是分割成2部分,大概log2n次可以得到一个元素
2.合并的次数是O(n)
3.平均水平是O(nlog2n)
4.由于使用了递归与两个列表,所以空间复杂度增大O(logn) + O(n)

06.各排序方法的对比
排序方法平均时间最坏时间辅助空间稳定性
选择排序O(n^2)O(n^2)O(1)不稳定
冒泡排序O(n^2)O(n^2)O(1)稳定
插入排序O(n^2)O(n^2)O(1)稳定
快速排序O(nlogn)O(n^2)O(logn)不稳定
合并排序O(nlogn)O(nlogn)O(n)稳定
三、课程小结
  • 学习了排序算法
  • 学习了排序代码实现
  • 学习了如何进行排序时间复杂度分析
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值