一、列表排序

  将无序列表变为有序列表

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

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

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

1、冒泡排序

思路:

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

代码实现:

  1. # 冒泡排序
  2. @cal_time # 测试执行时间
  3. def bubble_sort(li):
  4. for i in range(len(li)-1): # i表示第i趟
  5. # 第i趟无序区位置【0,n-i-1】
  6. for j in range(len(li)-i-1):
  7. if li[j] > li[j+1]:
  8. li[j], li[j+1] = li[j+1], li[j]
  9.  
  10. # 最好情况 O(n^2)
  11. # 平均情况 O(n^2)
  12. # 最坏情况 O(n^2)
  13.  
  14. # 优化改进>>>
  15. # 思路:如果冒泡排序中执行一趟而没有交换,则列表已经是有序状态,可以直接结束算法。
  16. @cal_time
  17. def bubble_sort2(li):
  18. for i in range(len(li)): # 表示第i趟
  19. exchange = 0
  20. # 第i趟无序区的位置【0,n-i-1】 n是列表长度
  21. for j in range(len(li)-i-1):
  22. if li[j] > li[j+1]:
  23. li[j], li[j+1] = li[j+1], li[j]
  24. exchange = 1
  25. if not exchange: # 如果遍历一遍没有发生交换,则已经有序,直接返回
  26. return
  27.  
  28. # 最好情况 O(n)
  29. # 平均情况 O(n^2)
  30. # 最坏情况 O(n^2)

2、选择排序

思路:

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

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

  ...

问题:

  怎么选出最小的数

  1. # 找最小值
  2. def find_min(li):
  3. min_num = li[0]
  4. for i in range(1,len(li)):
  5. if li[i] < min_num:
  6. min_num = li[i]
  7. return min_num
  8.  
  9. # 找最小值的下标
  10. def find_min_pos(li):
  11. min_pos = 0
  12. for j in range(1,len(li)):
  13. if li[j] < li[min_pos]:
  14. min_pos = j
  15. return min_pos
  16.  
  17. li = [2,5,8,9,11,15,5,1]
  18. print(find_min(li)) #
  19. print(find_min_pos(li)) #

选择排序:

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

比冒泡排序快。

3、插入排序

思路:

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

代码:

  1. def insert_sort(li):
  2. for i in range(1, len(li)): # i表示第i趟,还表示无序区第一个数的位置
  3. tmp = li[i]
  4. j = i - 1 # j 从后往前遍历有序区的指针
  5. while j >= 0 and li[j] > tmp: # j遍历结束或无序区第一位大于j指向的数时跳出循环
  6. li[j + 1] = li[j] # 有序区的第j位往后挪一位
  7. j -= 1 # j 向前指一位
  8. 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要归的位置

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

代码:

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

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

归位思路:

  取最后一个元素r归位,

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

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

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

代码实现:

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

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

思路:

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

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

  然后将三个列表拼起来

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

代码:

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

一行实现快速排序:

  1. 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

代码:

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

3、归并排序

思路:

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

有了归并之后怎么用?

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

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

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

一次归并:

  1. def merge(li, low, mid, high):
  2. '''
  3. 归并
  4. :param li:
  5. :param low:
  6. :param mid:
  7. :param high:
  8. :return:
  9. '''
  10. i = low
  11. j = mid + 1
  12. li_tmp = []
  13. while i <= mid and j <= high: # 两边都有数
  14. if li[i] <= li[j]:
  15. li_tmp.append(li[i])
  16. i += 1
  17. else:
  18. li_tmp.append(li[j])
  19. j += 1
  20. # i<=mid 和 j<=high 两个条件 只能有一个满足
  21. while i <= mid:
  22. li_tmp.append(li[i])
  23. i += 1
  24. while j <= high:
  25. li_tmp.append(li[j])
  26. j += 1
  27. # li_tmp 0~high-low 复制回li low~high
  28. for i in range(len(li_tmp)):
  29. li[low + i] = li_tmp[i]

分解合并:

  1. def merge_sort(li, low, high):
  2. if low < high: # 至少两个元素
  3. # print(li[low:high+1], '->', end=' ')
  4. mid = (low + high) // 2 # 分解
  5. # print(li[low:mid+1], li[mid+1: high+1])
  6. merge_sort(li, low, mid) # 递归排序左边
  7. merge_sort(li, mid + 1, high) # 递归排序右边
  8. # print(li[low:mid+1], li[mid+1: high+1], '->', end=' ')
  9. merge(li, low, mid, high) # 一次归并 合并
  10. # 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. JavaScript 深度遍历对象的两种方式,递归与非递归

    递归遍历: 基本问题: 当前属性值不为对象时,打印键和值 递归过程:当前属性值为对象时,打印键,继续递归 var o = { a: { b: { c: { d: { e: { f: 1, g:{ h: ...

  2. js图片轮换播放器

    <!DOCTYPE html> <html> <head> <title></title> <meta charset="u ...

  3. AngularJS学习:第一个demo

    1. 引入angular.js 相应版本下载地址: https://code.angularjs.org/ 2. 编写html <!DOCTYPE html> <html> & ...

  4. 100w并发产生唯一随机id

    #coding=utf-8 import time import base64 import getopt import sys import threading import random impo ...

  5. Day11 - B - Dice (III) LightOJ - 1248

    设dp_i为已经出现了i面,需要的期望次数,dp_n=0 那么dp_i= i/n*dp_i + (n-i)/n*dp_(i+1) + 1 现在已经i面了,i/n的概率再选择一次i面,(n-i)/n的概 ...

  6. app生命周期之即将关闭

    需求:当软件正在进行任务还未结束时,如果用户强制退出软件,需要将一些数据进行保存等处理. 策略:当用户使用多任务将软件挂起,并滑掉软件时,接下来有5妙钟的时间留给软件做处理.会调用- (void)ap ...

  7. 复盘实战营一期毕业典礼----HHR计划----以太入门课--第一课

    你要永远记住,实事求是. 我好像没能力给大家分享什么.分享点我的专业知识吧.我是做推荐+增长的,在一家D轮 DAU快千万的创业公司做增长优化负责人.一路优化,我把人均时长提高了30多分钟(现在人均时长 ...

  8. ZOJ4102 Array in the Pocket(2019浙江省赛)

    贪心~ #include<bits/stdc++.h> using namespace std; ; int a[maxn]; int b[maxn]; int vis[maxn]; se ...

  9. linux服务器自动备份与删除postgres数据库数据

    1.先创一个back.sh 文件,授权,然后在下面这个文件添加脚本 export PGPASSWORD='123456'        #这是登录服务器密码cur_time=`date +%Y%m%d ...

  10. linux磁盘管理1-分区格式化挂载,swap,df,du,dd

    一些基础 硬盘接口类型 ide 早期家庭电脑 scsi 早期服务器 sata 目前家庭电脑 sas 目前服务器 raid卡--阵列卡 网卡绑定 ABI 应用程序与OS之间的底层接口 API 应用程序调 ...