一、列表排序

  将无序列表变为有序列表

  应用场景: 榜单,表格, 给二分查找用,给其他算法用

二、python实现三种简单排序算法

时间复杂度O(n^2), 空间O(1)

1、冒泡排序

思路:

  列表每两个相邻的数,如果前面的比后面的大,那么交换这两个数

代码实现:

# 冒泡排序
@cal_time # 测试执行时间
def bubble_sort(li):
for i in range(len(li)-1): # i表示第i趟
# 第i趟无序区位置【0,n-i-1】
for j in range(len(li)-i-1):
if li[j] > li[j+1]:
li[j], li[j+1] = li[j+1], li[j] # 最好情况 O(n^2)
# 平均情况 O(n^2)
# 最坏情况 O(n^2) # 优化改进>>>
# 思路:如果冒泡排序中执行一趟而没有交换,则列表已经是有序状态,可以直接结束算法。
@cal_time
def bubble_sort2(li):
for i in range(len(li)): # 表示第i趟
exchange = 0
# 第i趟无序区的位置【0,n-i-1】 n是列表长度
for j in range(len(li)-i-1):
if li[j] > li[j+1]:
li[j], li[j+1] = li[j+1], li[j]
exchange = 1
if not exchange: # 如果遍历一遍没有发生交换,则已经有序,直接返回
return # 最好情况 O(n)
# 平均情况 O(n^2)
# 最坏情况 O(n^2)

2、选择排序

思路:

  一趟遍历记录最小的数,放到第一个位置;

  再一趟遍历记录剩余列表中最小的数,继续放置;

  ...

问题:

  怎么选出最小的数

# 找最小值
def find_min(li):
min_num = li[0]
for i in range(1,len(li)):
if li[i] < min_num:
min_num = li[i]
return min_num # 找最小值的下标
def find_min_pos(li):
min_pos = 0
for j in range(1,len(li)):
if li[j] < li[min_pos]:
min_pos = j
return min_pos li = [2,5,8,9,11,15,5,1]
print(find_min(li)) #
print(find_min_pos(li)) #

选择排序:

def select_sort(li):
for i in range(len(li)-1): # 第i趟遍历,从0开始
# 第i趟 无序区【i, len(li)-1】
# 找无序区最小数位置,和无序区第一个数交换
min_pos = i
for j in range(i+1, len(li)): # 从无序区第二个开始找
if li[j] < li[min_pos]:
min_pos = j
li[min_pos], li[i] = li[i], li[min_pos] li = list(range(100))
random.shuffle(li) # 打乱列表次序
print(li)
select_sort(li)
print(li)

比冒泡排序快。

3、插入排序

思路:

  列表被分为有序区和无序区两个部分。最初有序区只有一个元素。每次从无序区选择一个元素,插入到有序区的位置,直到无序区变空。

代码:

def insert_sort(li):
for i in range(1, len(li)): # i表示第i趟,还表示无序区第一个数的位置
tmp = li[i]
j = i - 1 # j 从后往前遍历有序区的指针
while j >= 0 and li[j] > tmp: # j遍历结束或无序区第一位大于j指向的数时跳出循环
li[j + 1] = li[j] # 有序区的第j位往后挪一位
j -= 1 # j 向前指一位
li[j + 1] = tmp # 将tmp插入到有序区j后面一位

三、python实现三种较复杂排序算法

1、快速排序

时间复杂度: O(nlogn)

思路:

  取一个元素p(第一个元素),使元素p归位;

  列表被p分成两部分,左边都比p小,右边都比p大;

  左右两边递归完成排序。

方法一(经典方法):

归位思路:

  将要归位的数p存起来,此时左游标left指向空

  将游标指向的数与p比较,大于放右边,小于放左边(详细操作:

  先将右游标right指向的数与p比较,大于p,位置不变,右游标往左移一位,继续比较;小于p,放到left指向的空位置,此时right指向空,然后移left

  再将left游标往右移一位指向的数与p比较,小于p,位置不变,left往右移一位,继续比较;大于p,放到right指向的空位置,此时left指向空,然后移right)

  当左游标等于右游标时,游标指向的位置就是p要归的位置

  注意处理最坏情况(列表倒序)导致递归达到最大深度,从列表中随机取一个与第一个交换位置

代码:

def quick_sort(li, left, right):
if left < right: # 递归区域至少有两个元素
mid = partition(li, left, right) # 归位
quick_sort(li, left, mid-1) # 左边
quick_sort(li, mid+1, right) # 右边 def partition(li, left, right):
i = random.randint(left, right) # 防止最坏情况(列表有序或倒序)导致递归达到最大深度,从列表中随机取一个与第一个交换位置
li[i], li[left] = li[left], li[i]
tmp = li[left] # 将要归位的数存起来
while left < right:
while left < right and li[right] >= tmp:
right -= 1 # 右边的数大于等于tmp就不动,right游标往左走
li[left] = li[right] # 右边的数小于tmp就往左放
while left < right and li[left] <= tmp:
left += 1 # 左边的数小于等于tmp就不动,left游标往右走
li[right] = li[left] # 左边的数大于tmp就往右放
li[left] = tmp # left=right 将tmp归位
return left

方法二(算法导论中的归位方法):

归位思路:

  取最后一个元素r归位,

  分两个区域,将小于r的数都放到区域一, 剩下的就是大于r的区域二

  然后将r与区域二的第一个数交换,就归位成功了

  与方法1一样也有python递归最大深度的问题

代码实现:

def partition2(li, left, right):
# 区域1:[left, i] 区域2:[i+1, j-1]
i = left - 1 # 初始区域1和区域2都空,i指向区域1最后一个数
for j in range(left, right):
if li[j] < li[right]: # 放到区域1,i往后移一位
i += 1
li[i], li[j] = li[j], li[i] # 与区域2的第一个数(i+1)交换归为区域1,
li[right], li[i+1] = li[i+1], li[right] # 归位
return i+1 # 返回mid

方法三(占用空间的方法):

思路:

  每次都取中间的数为归位,尽可能的避免最坏情况导致python递归达最大深度的问题

  开三个列表,一个放大于归位数的,一个放小于归位数的,一个放等于归位数的

  然后将三个列表拼起来

  递归结束条件,列表长度小于等于1, 

代码:

def quick_sort3(li):
if len(li) <= 1:
return li
m = li[len(li)//2] # 防止列表本来就是有序或倒序的,导致递归达到最大深度,不取li[0]
left = [item for item in li if item < m]
right = [item for item in li if item > m]
x = [i for i in li if i == m]
return quick_sort3(left) + x + quick_sort3(right)

一行实现快速排序:

quick_sort4 = lambda li: li if len(li) <= 1 else quick_sort4([item for item in li[1:] if item <= li[0]]) + [li[0]] + quick_sort4([item for item in li[1:] if item > li[0]])

2、堆排序

堆的概念:

  堆是完全二叉树,完全二叉树可以用列表来存储,通过规律可以从父亲找到孩子或从孩子找到父亲,堆中某个节点的值总是不大于或不小于其父节点的值

  大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大

  小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小

堆排序利用了堆向下调整的特征:节点的左右子树都是堆,但自身不是堆。

向下调整,挨个出数;

通过父节点找子节点:父节点下标为i

  则:孩子节点为,2i+1 和 2i+2

通过孩子节点找父节点:孩子节点为j

  子节点为左节点,父节点为:(j-1) / 2

  子节点为右节点,父节点为:(j-2) / 2

  不知道为左子节点还是右子节点: (j-1) // 2

代码:

def sift(li, low, high):
'''
向下调整
:param li:
:param low: 堆顶下标
:param high: 堆中最后一个元素下标
:return:
'''
tmp = li[low]
i = low
j = 2 * i + 1 # i, j 两个游标,初始i指向堆顶,j指向堆顶的左孩子
while j <= high: # 第二个结束循环的条件,没有孩子和tmp竞争i这个位置
if j+1 <= high and li[j+1] > li[j]: # 如果右孩子存在并且比左孩子大 j指向右孩子
j += 1
if li[j] > tmp:
li[i] = li[j] # 大的数往上调整
i = j # i指向下一个要调整的堆的堆顶
j = 2 * i + 1 # j指向调整堆的堆顶的左孩子
else:
break # 第一种循环退出情况,tmp比目前两个孩子都大
li[i] = tmp # i就是tmp要调整到的位置 def heap_sort(li):
'''
堆排序
:param li:
:return:
'''
# 1. 从列表构造堆,low的值和high的值
n = len(li)
# 子节点找父节点: low = (i-1)//2 --> (n-2)//2 --> n//2-1
for low in range(n//2-1, -1, -1):
sift(li, low, n-1)
# 2. 挨个出数 利用原来的空间存储下来的值,但是这些值不属于堆
for high in range(n-1, -1, -1): # 或range(n-1, 0, -1)
li[high], li[0] = li[0], li[high] # 1.把最大的调下来 2.high对应的数调上去
sift(li, 0, high - 1) # 3.调整,high 往前移一个,low为0

3、归并排序

思路:

  假设现在的列表分两段有序,如何将其合成一个有序列表, 这个操作称为一次归并。

有了归并之后怎么用?

  分解 :将列表越分越小,直至分成一个元素

    终止条件:一个元素是有序的。

  合并:将两个有序列表归并,列表越来越大。

一次归并:

def merge(li, low, mid, high):
'''
归并
:param li:
:param low:
:param mid:
:param high:
:return:
'''
i = low
j = mid + 1
li_tmp = []
while i <= mid and j <= high: # 两边都有数
if li[i] <= li[j]:
li_tmp.append(li[i])
i += 1
else:
li_tmp.append(li[j])
j += 1
# i<=mid 和 j<=high 两个条件 只能有一个满足
while i <= mid:
li_tmp.append(li[i])
i += 1
while j <= high:
li_tmp.append(li[j])
j += 1
# li_tmp 0~high-low 复制回li low~high
for i in range(len(li_tmp)):
li[low + i] = li_tmp[i]

分解合并:

def merge_sort(li, low, high):
if low < high: # 至少两个元素
# print(li[low:high+1], '->', end=' ')
mid = (low + high) // 2 # 分解
# print(li[low:mid+1], li[mid+1: high+1])
merge_sort(li, low, mid) # 递归排序左边
merge_sort(li, mid + 1, high) # 递归排序右边
# print(li[low:mid+1], li[mid+1: high+1], '->', end=' ')
merge(li, low, mid, high) # 一次归并 合并
# print(li[low:high+1])

小结:

02.python实现排序算法的更多相关文章

  1. Python之排序算法:快速排序与冒泡排序

    Python之排序算法:快速排序与冒泡排序 转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/7828610.html 入坑(简称IT)这一行也有些年头了,但自老师 ...

  2. python实现排序算法 时间复杂度、稳定性分析 冒泡排序、选择排序、插入排序、希尔排序

    说到排序算法,就不得不提时间复杂度和稳定性! 其实一直对稳定性不是很理解,今天研究python实现排序算法的时候突然有了新的体会,一定要记录下来 稳定性: 稳定性指的是 当排序碰到两个相等数的时候,他 ...

  3. python常见排序算法解析

    python——常见排序算法解析   算法是程序员的灵魂. 下面的博文是我整理的感觉还不错的算法实现 原理的理解是最重要的,我会常回来看看,并坚持每天刷leetcode 本篇主要实现九(八)大排序算法 ...

  4. 第四百一十五节,python常用排序算法学习

    第四百一十五节,python常用排序算法学习 常用排序 名称 复杂度 说明 备注 冒泡排序Bubble Sort O(N*N) 将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮 ...

  5. Python实现排序算法之快速排序

    Python实现排序算法:快速排序.冒泡排序.插入排序.选择排序.堆排序.归并排序和希尔排序 Python实现快速排序 原理 首先选取任意一个数据(通常选取数组的第一个数)作为关键数据,然后将所有比它 ...

  6. python 经典排序算法

    python 经典排序算法 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存.常见的内部排序算 ...

  7. Python 实现排序算法

    排序算法 下面算法均是使用Python实现: 插入排序 原理:循环一次就移动一次元素到数组中正确的位置,通常使用在长度较小的数组的情况以及作为其它复杂排序算法的一部分,比如mergesort或quic ...

  8. python——常见排序算法解析

    算法是程序员的灵魂. 下面的博文是我整理的感觉还不错的算法实现 原理的理解是最重要的,我会常回来看看,并坚持每天刷leetcode 本篇主要实现九(八)大排序算法,分别是冒泡排序,插入排序,选择排序, ...

  9. python之排序算法

    排序是每个语言都需要学会的,不管是c++.java还是python,套路都是类似的 python中也有自带的排序函数sort,直接使用也可 闲来无事写了几个排序算法,各不相同 1.每次遇到最小的数都交 ...

随机推荐

  1. Docker 安装 Kibana

    使用和 elasticsearch 相同版本镜像 7.4.1 (不一样可能会出现问题) 1.下载Kibana镜像  # 下载Kibana镜像 docker pull kibana: # 查看镜像 do ...

  2. 蓝桥杯-铺瓷砖(dfs)

    问题描述 有一长度为N(1< =N< =10)的地板,给定两种不同瓷砖:一种长度为1,另一种长度为2,数目不限.要将这个长度为N的地板铺满,一共有多少种不同的铺法? 例如,长度为4的地面一 ...

  3. Django 学习之Rest Framework 视图集与Routers与扩展功能

    一.视图集使用 使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中: list() 提供一组数据 retrieve() 提供单个数据 create() 创建数据 update() 保存数 ...

  4. SpringBoot常用注解解析

    @RestController 将返回的对象数据直接以 JSON 或 XML 形式写入 HTTP 响应(Response)中.绝大部分情况下都是直接以 JSON 形式返回给客户端,很少的情况下才会以 ...

  5. 「学习笔记」FFT 快速傅里叶变换

    目录 「学习笔记」FFT 快速傅里叶变换 啥是 FFT 呀?它可以干什么? 必备芝士 点值表示 复数 傅立叶正变换 傅里叶逆变换 FFT 的代码实现 还会有的 NTT 和三模数 NTT... 「学习笔 ...

  6. hdoj6703 2019 CCPC网络选拔赛 1002 array

    题意 description You are given an array a1,a2,...,an(∀i∈[1,n],1≤ai≤n). Initially, each element of the ...

  7. PTA的Python练习题(六)

    从 第3章-8 字符串逆序 开始 1. n = str(input()) n1=n[::-1] print(n1) 2. 不是很好做这道题,自己还是C语言的思维,网上几乎也找不到什么答案 s = in ...

  8. Python数据类型-7 bytes

    bytes 在Python3以后,字符串和bytes类型彻底分开了.字符串是以字符为单位进行处理的,bytes类型是以字节为单位处理的. bytes数据类型在所有的操作和使用甚至内置方法上和字符串数据 ...

  9. c++对象初始化(翁恺c++公开课[10])

    c++对象初始化 就是去调用构造函数来完成初始化操作: 构造函数有无参数的构造函数.有参数构造函数.默认构造函数(编译器给我们实现的)...(拷贝构造函数之后说) 注意:默认构造函数只有在我们自己没有 ...

  10. shell脚本基础及重定向!

    重定向: 补充:/dev/null(名叫黑洞)就是把输出的文件混合重定向到黑洞从而不显示在屏幕 yum -y install http &> /dev/null 重定向输入: 重定向输出 ...