一、列表排序

  将无序列表变为有序列表

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

二、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. java程序设计课期中考试——数据库的增删改查和简单的js界面

    首先是设计思路,对于数据库的增删改查,我们借助Ecilipse来进行前端和后端的编写.Ecilipse是可以进行java web项目的操作的. 前端,我们选择用使用jsp,所谓的jsp就是可以嵌入其他 ...

  2. 【转】issue management in your test project

    What is Issue Management? Issue Management is the process to make others aware of the problem and th ...

  3. 二 Spring的IOC入门,环境搭建,Spring测试类

    IOC:inversion of Control  控制反转,Spring框架的核心.削减计算机程序的耦合问题,把对象(例如JDBC)的创建权交给Spring. IOC的两种类型: 依赖注入: 依赖查 ...

  4. Python爬取51job实例

    用Python爬取51job里面python相关职业.工作地址和薪资. 51job上的信息 程序代码 from bs4 import BeautifulSoup from urllib.request ...

  5. Linux命令:vi | vim命令

    vim - vi 增强版.文本编辑器 格式:vim [options] [file ..] 说明:如果file存在,文件被打开并显示内容,如果文件不存在,当编辑后第一次存盘时创建它 [options] ...

  6. Educational Codeforces Round 72 (Rated for Div. 2)E(线段树,思维)

    #define HAVE_STRUCT_TIMESPEC#include<bits/stdc++.h>using namespace std;#define BUF_SIZE 100000 ...

  7. Systemverilog for design 笔记(六)

    转载请标明出处 第一章 有限状态机建模(FSM,finite state machine) 1.1.    使用枚举类型建立状态机模型 l  三过程块建模风格:三个过程块分别实现: a.状态转换(al ...

  8. SpringBoot图文教程3—「‘初恋’情结」集成Jsp

    有天上飞的概念,就要有落地的实现 概念+代码实现是本文的特点,教程将涵盖完整的图文教程,代码案例 文章结尾配套自测面试题,学完技术自我测试更扎实 概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例 ...

  9. 浏览器输入URL后HTTP请求返回的完整过程

    图:

  10. Codeforces1301C. Ayoub's function

    本题的收获是,要学会反向思维,正向找包含1的太多,我们就反向找,全排列-只有0的不满足题意的就是答案,一共有n-m个0,m个1,插空法,一共有m+1个地方可以插入0序列,总排列数为(n+1)*n/2, ...