python实现排序算法 时间复杂度、稳定性分析 冒泡排序、选择排序、插入排序、希尔排序
- 说到排序算法,就不得不提时间复杂度和稳定性!
- 其实一直对稳定性不是很理解,今天研究python实现排序算法的时候突然有了新的体会,一定要记录下来
- 稳定性:
稳定性指的是 当排序碰到两个相等数的时候,他们的顺序会不会发生交换。其实对于一个整数数列的排序,是否交换元素没有任何影响。
但是: 如果有这样一串二元组: ( 2, 5) (1 ,6 ) ( 2 , 8 )
我们要优先按照第一个元素排序,如果相同再按照第二个元素排序
我们先按照第二个元素进行排序之后再按照第一个元素进行排序,
里面有两个元组第一个数据都是2 如果是稳定的,那么第二个元素顺序不会发生改变,,如果不稳定,,那可能就悲剧了,,会让我们的数据排出不是我们想要的结果。- 时间复杂度:
教科书和网络中流传的概念我觉着实在是有些难理解。我来谈一谈自己的想法。
对于一个算法程序,我们可以把每进行一次操作的时间记为一个时间单元,即 一次加法操作是一个时间单元,一次逻辑判断是一个时间单元
当我们一次算法的执行时间以时间单元为单位,就能得到一个时间函数 T(n)= balbala(n) ,其中n一般代表一个传入的数据,决定了循环的次数或者递归次数,T是关于n的函数。balbala是我想用它来指代一个函数的算式
比如在c++语言中一个循环: count = 0; for( int i = 0; i<n ;i++ ){ count++; }
1 count = 0是一个赋值操作,算作一个单元时间
2 for循环一开始会执行int i = 0,算作一个单元时间,
3 之后显然会执行n次循环,在每次循环当中 count++、i++和i<n 三个时间单元操作,
所以对于这个for循环 T(n)= 1 + 1 + n*(3) = 3*n+2
比如可能某个函数 T(n) = n^2 + n + 1
类似这样的时间函数,都是 总时间T 和 不确定的决定循环或者递归次数的n 的关系
时间复杂度则是保留T(n)的最高阶,把其他低阶都忽略掉,然后用大O来表示
比如 T(n) = n^2 + n + 1 的时间复杂度是 O(n^2)
T(n) = n + 20 的时间复杂度是O(n)
为什么要只保留最高阶忽略低阶呢??有过初中高中数学基础的伙伴们你们会明白,画出函数图像,当n从0增长到正无穷的过程中,对增长速度影响最大的是最高阶,低阶其实基本可以忽略的。- 此时我们要对时间复杂度有更深一步的理解,时间复杂度体现的是随着n增大的过程中算法消耗时间增加的速率,也就是随着n增大,算法时间增大的速度
时间复杂度大的算法,函数图像一定比时间复杂度小的算法图像更陡
但是!!!!!时间复杂度大的算法并不一定比时间复杂度小的算法执行时间长!
当我们忽略低阶的时候,可以理解成,我们忽略了循环当中的好多个单元操作的时间,只保留了循环主导了时间。
但是如果一个两层循环里面有1个时间单元操作 时间复杂度正常情况是O(n^2)
另一个一层循环元里面10个单元操作 时间复杂度是O(n)
这两个算法耗时完全要看循环次数了,如果都是只循环一次,很显然里面只有1个单元操作的家伙耗时更短(它的时间复杂度是较大的)。- 再次总结:时间复杂度比较的是随着n增大,耗时增加的速度,而不是单纯比较两个算法谁耗时更大。
- 怎么样!!!好像非常有道理的样子!!!有木有被这帅气的妖风吓一大跳!!我听到大神伙伴给我讲这些的时候真的是感觉学到好多知识!!
谢谢我身边的大神伙伴咯~
嘻嘻 希望对大家有所帮助吧- 接下来分享一下我利用python实现的排序算法
- 冒泡排序:
对于一个n个数的列表,从第一个数开始,一直到第n-1个数,每个数和它挨着的下一个数比较,如果他下一个数更小,他们就交换数值,否则不交换。以上叫做一趟排序。经历一趟排序之后,可以保证最后一个数一定是整个数列中最大的。
之后 从第一个数开始 到第n-2个数,每个数和下一个数比较(因为第n个数经历第一趟之后一定是最大的,所以不用再用第n-1个数跟第n个数比),如果下一个数大,他们就交换数值,否则不交换。经过第二趟比较,数列的倒数两个数一定满足最终的条件,是倒数第二大的和最大的。
这样一直一趟一趟的比,n个数的列表,经过n-1趟排序之后一定能保证正确的顺序。- 一个数列有n个数,
第一趟排序需要比较前n-1个数每个数和后面一个数,使最大的数到最后一个位置,发生n-1次比较
第二趟排序需要比较前n-2个数,每个数后面的一个数,使第二大的数到倒数第二个位置,发生n-2次比较
。。。
第i趟排序需要比较前n-i个数,每个数和后面的一个数,使第i大的数到倒数第i的位置上,发生n-i次比较
最后一次就是第一个数和第二个数比较。- 经历n-1次比较,数列一定会变成正确的顺序。
- 最优时间复杂度: O(n)
最坏时间复杂度: O(n^2)
稳定性:稳定的排序
- #传入li列表
- def bubble_sort( li ):
- n = len(li) #数列里有n个数,下标从0到n-1
- #一共会发生n-1趟排序过程,限制排序次数,i 从0到n -1 ,记录当前是第i+1次排序
- for i in range( n-1 ):
- # 每次排序都会确定一个前面最大的数放在最后,
- # i从0到n-1,所以经历第i+1次排序之后最后的i+1个数是正确的,只需要把前n-(i+1) 个数进行比较挑换就可以
- for j in range( n - i - 1):
- #如果我后面的哥们比我数小,我俩就换数值
- if li[j]>li[j+1]:
- li[j] , li[j+1] = li[j+1], li[j]
- if __name__ == '__main__':
- li = [5,4,3,2,1]
- bubble_sort(li)
- print(li)
- 选择排序:
对于一个有n个数的数列,
第一次我们从第一个数开始选出所有数中最小的,和第一个数交换数值,这样保证第一个数是最小的
第二次我们从第二个数开始选出第一个数之后最小的数和第二个数交换数值,这样前两个数都在正确位置
。。。
最后一次 我们拿倒数第二个数跟最后一个数比较,把小的放在前面- 第一次我们从第1个数开始一直到最后找最小的数
第二次我们从第2个数开始到最后找最小的数
。。。
第i次我们从第i个数开始向后找最小的数- 最优时间复杂度:O(n^2)
最坏时间复杂度:O(n^2)
稳定性:不稳定的排序 例如: 5 8 5 2 第一趟排序5和2互换,那么两个5顺序就改变了
- def select_sort(li):
- n = len(li) #li列表中有n个数,下标从0到n-1
- # i从0到n-1 ,我们每次拿下标为i的数跟后面数比较 标记最小的数
- for i in range( n ):
- temp = i #用temp做临时标记,没遇见比下标temp更小的数,就用temp标记更小的数的下表
- # 从temp开始向后找到最后 找最小的数
- for j in range( temp , n ):
- #如果我们遇到比temp标记的数更小的,tamp就标记更小的数的下标
- if li[temp] > li[j] :
- li[temp] ,li[j] = li[j] , li[temp]
- #这次for循环之后 temp一定标记了i之后的最小的数的下标,我们把最小的数和i位置进行呼唤
- li[i] , li[temp] = li[temp] , li[i]
- if __name__ == '__main__':
- li = [5,4,3,2,1]
- select_sort(li)
- print(li)
- 插入排序:
对于一个n个数的数列:
拿出第二个数,跟第一个比较,如果第二个大,第二个就放在第一个后面,否则放在第一个前面,这样前两个数就是正确顺序了
拿出第三个数,跟第二个数比较,如果比第二个数小,就放在第二个数前面,再跟第一个数比,比第一个小就放在第一个前面,这样前三个数就是正确顺序
....
拿出最后一个数,跟之前的正确顺序进行比较,插入到合适的位置当中。- 可以理解成: 每次拿到一个数,他前面都是一个有序的数列,然后,找到我何时的位置,我插入进去
- 最坏时间复杂度: O(n^2)
最优时间复杂度: O(n)
稳定性:稳定的排序
- def select_sort(li):
- n = len(li) #li列表中有n个数,下标从0到n-1
- # i从0到n-1 ,我们每次拿下标为i的数跟后面数比较 标记最小的数
- for i in range( n ):
- temp = i #用temp做临时标记,没遇见比下标temp更小的数,就用temp标记更小的数的下表
- # 从temp开始向后找到最后 找最小的数
- for j in range( temp , n ):
- #如果我们遇到比temp标记的数更小的,tamp就标记更小的数的下标
- if li[temp] > li[j] :
- temp = j
- #这次for循环之后 temp一定标记了i之后的最小的数的下标,我们把最小的数和i位置进行互换
- li[i] , li[temp] = li[temp] , li[i]
- if __name__ == '__main__':
- li = [5,4,3,2,1]
- select_sort(li)
- print(li)
- 希尔排序:
是对插入排序的升级的版本。(插入排序: 每次拿一个数在前面有序的数列中找到位置进行插入 )
设置一个步长,一般为数列长度的一半。
把数列按照步长分组,每组相同位置的数据进行选择排序,
然后再把步长减小后分组,每组的相同位置元素进行选择排序
一直步长减小到1位置,进行选择排序
此时,数列顺序就排好了- 举个例子: 对于数列 8 7 6 5 4 3 2 1
8个元素,我们按4为步长进行分组 分成了 8 7 6 5 4 3 2 1
之后 每组相同位置元素进行插入排序,即把
第一趟 8 4 (每组第一个位置)插入排序 整个数列变成: 4 7 6 5 8 3 2 1
第二趟 7 3 (每组第二个位置)插入排序 整个数列变成: 4 3 6 5 8 7 2 1
第三趟 6 2 (每组第三个位置) 进行插入排序后: 4 3 2 5 8 7 6 1
第四趟 5 1 (每组第四个位置) 进行插入排序后: 4 3 2 1 8 7 6 5- 之后步长改为2 进行分组 4 3 2 1 8 7 6 5
第一趟 4 2 8 6(每组第一个数)进行插入排序后整个数列: 2 3 4 1 6 7 8 5
第二趟 3 1 7 5(每组第二个数)进行插入排序后整个数列: 2 1 4 3 6 5 8 7- 上述过程是便于理解,但是对于步长为2分组后,实际上的过程是: 4 3 2 1 8 7 6 5
第一次 4 2 比 结果是 2 3 4 1 8 7 6 5
第二次 3 1 比 结果是 2 1 4 3 8 7 6 5
第三次 4 8 比 结果是 2 1 4 3 8 7 6 5
。。。。持续下去
实际上是按下标递增的方式进行的,但是每次只与它相邻组同位置元素进行比较,
分组之后,每组相同位置元素不是一次进行插入排序结束再进行第二个相同位置排序
比较顺序 是按照下表递增的方式进行的,但是比较的数据不是自己相邻的,而是相邻组相同位置的数据- 之后 步长改为1 进行分组 每个元素一组 进行插入排序: 1 2 3 4 5 6 7 8
- 优点:设置步长,似的后边较大的元素能快速移动到前面来,前面大元素能快速移动到后面,省去了很多次比较的过程。
- 时间复杂度: O(n)< x < O(n^2)
稳定性: 不稳定的排序 按步长分组的时候,如果存在两个相等的数分在不同组里,插入排序后有可能本来在前面的数到后面去了
- def shell_sort(li ):
- n = len(li) #li列表中有n个数 下标从0到n-1
- gep = n//2 #设置初始步长进行分组
- #当步长大于等于1的时候 一直进行分组的插入排序
- while gep >= 1 :
- # 从gep的元素往后 一个一个元素对前面所有组跟自己处于相同位置的数进行插入排序
- for j in range( gep , n ):
- i = j #标记下当前j的位置给i
- # 这层循环是为了回跳i所在位置,
- # 当一个新元素对前组同位置元素交换后,当前元素还需要跟再往前组的同位置元素比较决定是不是要交换
- # i - gep 能看我当前是不是第一组位置,如果是第一组位置,我前面没有组了,就不循环了,
- # 如果我是第三组,我会跳到前二组再跟第一组的相同位置元素进行比较
- while i - gep >= 0 :
- #如果当前数比前一组同位置数小,就交换数值
- if li[i] < li[i-gep] :
- li[i] , li[i-gep] = li[i-gep] , li[i]
- #如果我和前一组同位置元素交换了,我需要继续与再往前一组同位置元素比较是否需要交换
- #这个操作是把我当前位置回跳到上一组的同位置
- i -= gep
- #如果没有发生数据交换,说明前面的不用再比较了,前面的一定都比我当前数小,跳出回跳位置的循环
- else :
- break
- #修改步长 缩小步长
- gep//=2
- if __name__ == '__main__':
- li = [5,4,3,2,1]
- shell_sort(li)
- print(li)
python实现排序算法 时间复杂度、稳定性分析 冒泡排序、选择排序、插入排序、希尔排序的更多相关文章
- 常见排序算法总结与分析之交换排序与插入排序-C#实现
前言 每每遇到关于排序算法的问题总是不能很好的解决,对一些概念,思想以及具体实现的认识也是模棱两可.归根结底,还是掌握不够熟练.以前只是看别人写,看了就忘.现在打算自己写,写些自己的东西,做个总结.本 ...
- Python之排序算法:快速排序与冒泡排序
Python之排序算法:快速排序与冒泡排序 转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/7828610.html 入坑(简称IT)这一行也有些年头了,但自老师 ...
- 排序算法总结第二弹----冒泡排序---javascript描述
上篇博文总结了选择排序,这篇来看冒泡排序,接上篇. 冒泡排序思想:若是正再将一组数据升序排序, 第一趟:比较相邻的数据,当左侧值大于右侧值将他们进行交换,将较小值向前浮动,大值向后冒泡,直至比较到最后 ...
- python数据结构与算法第八天【冒泡排序】
1.排序算法的稳定性 稳定排序算法会让原本有相同键值的记录维持相对次序 例如:对以下元组按照元组的第一个元素升序排列,元组如下: (4,1) (3,1) (3,7) (5,6) 若要满足条件,则可能的 ...
- python排序算法实现(冒泡、选择、插入)
python排序算法实现(冒泡.选择.插入) python 从小到大排序 1.冒泡排序: O(n2) s=[3,4,2,5,1,9] #count = 0 for i in range(len(s)) ...
- 十大排序算法时间复杂度 All In One
十大排序算法时间复杂度 All In One 排序算法时间复杂度 排序算法对比 Big O O(n) O(n*log(n)) O(n^2) 冒泡排序 选择排序 插入排序 快速排序 归并排序 基数排序 ...
- Shell数组以及排序算法(冒泡、直接选择、反转)
Shell数组以及排序算法(冒泡.直接选择.反转) 目录 Shell数组以及排序算法(冒泡.直接选择.反转) 一.数组概述 1. 数组的定义 2. 下标的定义 3. 数组的特点 4. 数组定义的方法 ...
- 学习C#之旅 冒泡排序,选择排序,插入排序,希尔排序[资料收集]
关于冒泡排序,选择排序,插入排序,希尔排序[资料收集] 以下资料来源与网络 冒泡排序:从后到前(或者从前到后)相邻的两个两两进行比较,不满足要求就位置进行交换,一轮下来选择出一个最小(或最大)的放到 ...
- 排序算法Java代码实现(三)—— 插入排序 和 希尔排序
因为希尔排序的核心思想是插入排序,所以本篇将两篇排序一起记录 本篇内容: 插入排序 希尔排序 (一)插入排序 算法思想: 把n个待排序的元素看成一个有序表和一个无序表,开始时有序表中只有一个元素,无序 ...
- 插入排序:直接插入排序&希尔排序
一.直接插入排序 1. 思想 直接排序法, 可以分为两个部分, 一部分是有序的, 一部分是无序的. 从这个图上, 应该是能看清楚直接插入排序的思想了. 将无序部分的第一个与有序部分进行比较. 从有序部 ...
随机推荐
- SpringtMVC中配置 <mvc:annotation-driven/> 与 <mvc:default-servlet-handler/> 的作用与源码解析
基于 Spring4.X 来学习 SpringtMVC, 在学习过程中,被"告知"在 XML 配置文件中建议设置如下两项: 一直不明白为什么,但又甘心.于是,花了一点时间来调试源码 ...
- 最小生成数(并查集)Kruskal算法
并查集:使用并查集可以把每个连通分量看作一个集合,该集合包含连通分量的所有点.这两两连通而具体的连通方式无关紧要,就好比集合中的元素没有先后顺序之分,只有属于和不属于的区别.#define N 100 ...
- 单例模式、简单工厂模式、XML解析
单例模式: 什么是单例模式? 针对特定问题提出的特定解决方案 为什么使用设计模式? 让程序有更好的可扩展性 在哪里使用? 一般情况下,开发中真正使用设计模式的地方,JVM(虚拟机)底层机制模式 usi ...
- Git忽略规则.gitignore梳理
对于经常使用Git的朋友来说,.gitignore配置一定不会陌生.废话不说多了,接下来就来说说这个.gitignore的使用. 首先要强调一点,这个文件的完整文件名就是".gitignor ...
- JavaWeb学习笔记总结 目录篇
JavaWeb学习笔记一: XML解析 JavaWeb学习笔记二 Http协议和Tomcat服务器 JavaWeb学习笔记三 Servlet JavaWeb学习笔记四 request&resp ...
- alpha-咸鱼冲刺day8-紫仪
总汇链接 一,合照 emmmmm.自然还是没有的. 二,项目燃尽图 三,项目进展 正在进行页面整合.然后还有注册跟登陆的功能完善-- 四,问题困难 数据流程大概是搞定了.不过语法不是很熟悉,然后还有各 ...
- 需求分析&原型设计
需求分析&原型设计 需求分析 访问软件项目真实用户 首先本项目的用户是这个需要做简单四则运算的用户(我们团队通过对家里有三四年级小学生(需要做简单四则运算)的简单采访):反映了几个主要的问题: ...
- EasyUI 中easyui-textbox和easyui-searchbox文本框的点击事件。
html: <input id="txtsearch" class="easyui-textbox" data-options="buttonT ...
- volt问题
1./表示当前目录:/college/detail/{{ item.sid }}表示这个路径超链接,url实在不好写就不写,作为开发人员想怎么弄就怎么弄最后发布是项目主管的事 2.不需要服务器给值,直 ...
- 第四章 Ajax与jQuery
第四章 Ajax与jQuery 一.Ajax简介 在传统的Web应用中,每次请求服务器都会生成新的页面,用户在提交请求后,总是要等待服务器的响应.如果前一个请求没有响应,则后一个请求就不能发送,在 ...