数据结构和算法是什么?答曰:兵法!node
算法是计算机处理信息的本质,由于计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务。通常地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写入输出设备或某个存储地址供之后再调用。python
对于算法而言,实现的语言并不重要,重要的是思想。算法
算法能够有不一样的语言描述实现版本(如C描述、C++描述、Python描述等),咱们如今是在用Python语言进行描述实现。数据结构
算法的五大特性app
需求:已知a+b+c=1000 a2+b2=c2 求a,b,c可能的值数据结构和算法
import time start_time = time.time() # 注意是三重循环 for a in range(0, 1001): for b in range(0, 1001): for c in range(0, 1001): if a**2 + b**2 == c**2 and a+b+c == 1000: print("a, b, c: %d, %d, %d" % (a, b, c)) end_time = time.time() print("elapsed: %f" % (end_time - start_time)) print("complete!") 运行结果: a, b, c: 0, 500, 500 a, b, c: 200, 375, 425 a, b, c: 375, 200, 425 a, b, c: 500, 0, 500 elapsed: 214.583347 complete! 注意运行的时间: 160.913325秒 个人电脑运行了大概26min。。。
import time start_time = time.time() # 注意是两重循环 for a in range(0, 1001): for b in range(0, 1001-a): c = 1000 - a - b if a**2 + b**2 == c**2: print("a, b, c: %d, %d, %d" % (a, b, c)) end_time = time.time() print("elapsed: %f" % (end_time - start_time)) print("complete!") 运行结果: a, b, c: 0, 500, 500 a, b, c: 200, 375, 425 a, b, c: 375, 200, 425 a, b, c: 500, 0, 500 elapsed: 0.182897 complete! 注意运行的时间: 0.609427秒
对于同一问题,咱们给出了两种解决算法,在两种算法的实现中,咱们对程序执行的时间进行了测算,发现两段程序执行的时间相差悬殊(160.913325秒相比于0.609427秒),由此咱们能够得出结论:实现算法程序的执行时间能够反应出算法的效率,即算法的优劣。函数
假设咱们将第二次尝试的算法程序运行在一台配置古老性能低下的计算机中,状况会如何?极可能运行的时间并不会比在咱们的电脑中运行算法一的160.913325秒快多少。
==单纯依靠运行的时间来比较算法的优劣并不必定是客观准确的!==性能
程序的运行离不开计算机环境(包括硬件和操做系统),这些客观缘由会影响程序运行的速度并反应在程序的执行时间上。那么如何才能客观的评判一个算法的优劣呢?测试
咱们假定计算机执行算法每个基本操做的时间是固定的一个时间单位,那么有多少个基本操做就表明会花费多少时间单位。算然对于不一样的机器环境而言,确切的单位时间是不一样的,可是对于算法进行多少个基本操做(即花费多少时间单位)在规模数量级上倒是相同的,由此能够忽略机器环境的影响而客观的反应算法的时间效率。操作系统
对于算法的时间效率,咱们能够用“大O记法”来表示。
“大O记法”:对于单调的整数函数f,若是存在一个整数函数g和实常数c>0,使得对于充分大的n总有f(n)<=c*g(n),就说函数g是f的一个渐近函数(忽略常数),记为f(n)=O(g(n))。也就是说,在趋向无穷的极限意义下,函数f的增加速度受到函数g的约束,亦即函数f与函数g的特征类似。
时间复杂度:假设存在函数g,使得算法A处理规模为n的问题示例所用时间为T(n)=O(g(n)),则称O(g(n))为算法A的渐近时间复杂度,简称时间复杂度,记为T(n)
对于算法进行特别具体的细致分析虽然很好,但在实践中的实际价值有限。对于算法的时间性质和空间性质,最重要的是其数量级和趋势,这些是分析算法效率的主要部分。而计量算法基本操做数量的规模函数中那些常量因子能够忽略不计。例如,能够认为3n2和100n2属于同一个量级,若是两个算法处理一样规模实例的代价分别为这两个函数,就认为它们的效率“差很少”,都为n2级。
分析算法时,存在几种可能的考虑:
对于最优时间复杂度,其价值不大,由于它没有提供什么有用信息,其反映的只是最乐观最理想的状况,没有参考价值。
对于最坏时间复杂度,提供了一种保证,代表算法在此种程度的基本操做中必定能完成工做。
对于平均时间复杂度,是对算法的一个全面评价,所以它完整全面的反映了这个算法的性质。但另外一方面,这种衡量并无保证,不是每一个计算都能在这个基本操做内完成。并且,对于平均状况的计算,也会由于应用算法的实例分布可能并不均匀而难以计算。
因而可知,咱们尝试的第二种算法要比第一种算法的时间复杂度好多的。
所消耗的时间从小到大:
O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)
timeit模块能够用来测试一小段Python代码的执行速度。
timeit.Timer(stmt='pass', setup='pass', timer=<timer function>) Timer是测量小段代码执行速度的类。 stmt参数是要测试的代码语句(statment); setup参数是运行代码时须要的设置; timer参数是一个定时器函数,与平台有关。
timeit.Timer.timeit(number=1000000) Timer类中测试语句执行速度的对象方法。 number参数是测试代码时的测试次数,默认为1000000次。 方法返回执行代码的平均耗时,一个float类型的秒数。
list的操做测试 def test1(): ls= [] for i in range(1000): ls = ls + [i] def test2(): ls = [] for i in range(1000): ls.append(i) def test3(): ls = [] for i in range(1000): ls.insert(0,i) def test4(): ls = [i for i in range(1000)] def test5(): ls = list(range(1000)) from timeit import Timer t1 = Timer(stmt='test1()',setup='from __main__ import test1') print('concat',t1.timeit(number=10000),'second') t2 = Timer(stmt='test2()',setup='from __main__ import test2') print('append',t2.timeit(number=10000),'second') t3 = Timer(stmt='test3()',setup='from __main__ import test3') print('insert',t3.timeit(number=10000),'second') t4 = Timer(stmt='test4()',setup='from __main__ import test4') print('list gen',t4.timeit(number=10000),'second') t5 = Timer(stmt='test5()',setup='from __main__ import test5') print('list',t5.timeit(number=10000),'second') 运行结果 concat 18.341660484899588 second append 1.0099177848925684 second insert 5.361922586350083 second list gen 0.45405794180203074 second list 0.18035762155434298 second
pop操做测试 from timeit import Timer x1 = [x for x in range(2000000)] t = Timer('x1.pop(0)','from __main__ import x1') print('pop(0)',t.timeit(number=1000),'second') x2 = [x for x in range(2000000)] t = Timer('x2.pop()','from __main__ import x2') print('pop()',t.timeit(number=1000),'second') 运行结果 pop(0) 2.033923430381945 second pop() 0.00011084076000633658 second
测试pop操做:从结果能够看出,pop最后一个元素的效率远远高于pop第一个元素
数据是一个抽象的概念,将其进行分类后获得程序设计语言中的基本类型。如:int,float,char等。数据元素之间不是独立的,存在特定的关系,这些关系即是结构。数据结构指数据对象中数据元素之间的关系。
Python给咱们提供了不少现成的数据结构类型,这些系统本身定义好的,不须要咱们本身去定义的数据结构叫作Python的内置数据结构,好比列表、元组、字典。而有些数据组织方式,Python系统里面没有直接定义,须要咱们本身去定义实现这些数据的组织方式,这些数据组织方式称之为Python的扩展数据结构,好比栈,队列等。
抽象数据类型(ADT)的含义是指一个数学模型以及定义在此数学模型上的一组操做。即把数据类型和数据类型上的运算捆在一块儿,进行封装。
引入抽象数据类型的目的是把数据类型的表示和数据类型上运算的实现与这些数据类型和运算在程序中的引用隔开,使它们相互独立。
最经常使用的数据运算有五种:
在程序中,常常须要将一组(一般是同为某个类型的)数据元素做为总体管理和使用,须要建立这种元素组,用变量记录它们,传进传出函数等。一组数据中包含的元素个数可能发生变化(能够增长或删除元素)。
对于这种需求,最简单的解决方案即是将这样一组元素当作一个序列,用元素在序列里的位置和顺序,表示实际应用中的某种有意义的信息,或者表示数据之间的某种关系。
这样的一组序列元素的组织形式,咱们能够将其抽象为线性表。一个线性表是某类元素的一个集合,还记录着元素之间的一种顺序关系。线性表是最基本的数据结构之一,在实际程序中应用很是普遍,它还常常被用做更复杂的数据结构的实现基础。
根据线性表的实际存储方式,分为两种实现模型:
一个顺序表的完整信息包括两部分,一部分是表中的元素集合,另外一部分是为实现正确操做而需记录的信息,即有关表的总体状况的信息,这部分信息主要包括元素存储区的容量和当前表中已有的元素个数两项。
元素存储区替换:
元素存储区扩充
采用分离式结构的顺序表,若将数据区更换为存储空间更大的区域,则能够在不改变表对象的前提下对其数据存储区进行了扩充,全部使用这个表的地方都没必要修改。只要程序的运行环境(计算机系统)还有空闲存储,这种表结构就不会由于满了而致使操做没法进行。人们把采用这种技术实现的顺序表称为动态顺序表,由于其容量能够在使用中动态变化。
扩充的两种策略
每次扩充增长固定数目的存储位置,如每次扩充增长10个元素位置,这种策略可称为线性增加。
特色:节省空间,可是扩充操做频繁,操做次数多。
每次扩充容量加倍,如每次扩充增长一倍存储空间。
特色:减小了扩充操做的执行次数,但可能会浪费空间资源。以空间换时间,推荐的方式。
增长元素,删除元素
Python中的list和tuple两种类型采用了顺序表的实现技术,具备前面讨论的顺序表的全部性质。
tuple是不可变类型,即不变的顺序表,所以不支持改变其内部状态的任何操做,而其余方面,则与list的性质相似。
链表(Linked list)是一种常见的基础数据结构,是一种线性表,可是不像顺序表同样连续存储数据,而是在每个节点(数据存储单元)里存放下一个节点的位置信息(即地址)。
单向链表也叫单链表,是链表中最简单的一种形式,它的每一个节点包含两个域,一个信息域(元素域)和一个连接域。这个连接指向链表中的下一个节点,而最后一个节点的连接域则指向一个空值。
单链表的实现 #定义节点类 class SingleNode(object): def __init__(self,item): #信息域:存储节点数据的 self.item = item #连接域:连接下一个节点的 self.next = None #定义单向链表 class SingleLinkList(object): def __init__(self): #head:头节点的引用 self._head = None def is_empty(self): #链表是否为空 return self._head == None def length(self): #链表长度 count = 0 #cur指向链表的首节点 cur = self._head #判断cur是否为None while cur != None: #cur不等于None,就表示一个节点存在 #给计数器加1 count += 1 #cur指向当前节点的下一个节点 cur = cur.next return count def travel(self): # 遍历整个链表 # cur指向链表的首节点 cur = self._head # 判断cur是否为None while cur != None: #打印当前节点的数据 print(cur.item) #cur指向下一个节点 cur = cur.next def add(self,item): #链表头部添加元素 #生成新的节点对象 node = SingleNode(item) #设置node节点的next指向原来的头节点 node.next = self._head #把node节点设置成了新的头节点 self._head = node def append(self,item): #链表尾部添加元素 #定义新的节点 node = SingleNode(item) if self.is_empty(): self._head = node else: #cur指向链表的开头 cur = self._head while cur.next != None: cur = cur.next #cur是链表的最后一个节点 cur.next = node def insert(self,pos, item): #指定位置添加元素 if pos<=0: #在链表的头部添加节点 self.add(item) elif pos>=self.length(): #在链表的尾部添加节点 self.append(item) else: node = SingleNode(item) #计数器,用来肯定插入位置 count = 0 cur = self._head while count < pos - 1: count+=1 cur = cur.next; node.next = cur.next cur.next = node def remove(self,item): #删除节点 cur = self._head #pre:保存cur的上一个节点 pre = None while cur != None: if cur.item == item: #肯定要删除的节点 if not pre: #删除的是第一个节点 #把当前节点的下一个节点看成首节点 self._head = cur.next else: # 删除的不是第一个节点 pre.next = cur.next break else: #不相等,不是要删除的节点 #遍历下一个节点 pre = cur cur = cur.next def search(self,item): #查找节点是否存在 cur = self._head while cur != None: #断定是否是要查找的节点 if cur.item == item: return True else: cur = cur.next return False if __name__ == '__main__': sl = SingleLinkList() sl.add(1) sl.add(2) sl.append(3) sl.insert(2,6) print('length:',sl.length()) sl.travel() print(sl.search(2)) sl.remove(2) print('length:', sl.length())
链表与顺序表的对比
链表失去了顺序表随机读取的优势,同时链表因为增长告终点的指针域,空间开销比较大,但对存储空间的使用要相对灵活。
单链表的一个变形是单向循环链表,链表中最后一个节点的next域再也不为None,而是指向链表的头节点。
单向循环列表的实现 class Node(object): """节点""" def __init__(self, item): self.item = item self.next = None class SinCycLinkedlist(object): """单向循环链表""" def __init__(self): self._head = None def is_empty(self): """判断链表是否为空""" return self._head == None def length(self): """返回链表的长度""" # 若是链表为空,返回长度0 if self.is_empty(): return 0 count = 1 cur = self._head while cur.next != self._head: count += 1 cur = cur.next return count def travel(self): """遍历链表""" if self.is_empty(): return cur = self._head print(cur.item) while cur.next != self._head: cur = cur.next print(cur.item) def add(self, item): """头部添加节点""" node = Node(item) if self.is_empty(): self._head = node node.next = self._head else: #添加的节点指向_head node.next = self._head # 移到链表尾部,将尾部节点的next指向node cur = self._head while cur.next != self._head: cur = cur.next cur.next = node #_head指向添加node的 self._head = node def append(self, item): """尾部添加节点""" node = Node(item) if self.is_empty(): self._head = node node.next = self._head else: # 移到链表尾部 cur = self._head while cur.next != self._head: cur = cur.next # 将尾节点指向node cur.next = node # 将node指向头节点_head node.next = self._head def insert(self, pos, item): """在指定位置添加节点""" if pos <= 0: self.add(item) elif pos > (self.length()-1): self.append(item) else: node = Node(item) cur = self._head count = 0 # 移动到指定位置的前一个位置 while count < (pos-1): count += 1 cur = cur.next node.next = cur.next cur.next = node def remove(self, item): """删除一个节点""" # 若链表为空,则直接返回 if self.is_empty(): return # 将cur指向头节点 cur = self._head pre = None # 若头节点的元素就是要查找的元素item if cur.item == item: # 若是链表不止一个节点 if cur.next != self._head: # 先找到尾节点,将尾节点的next指向第二个节点 while cur.next != self._head: cur = cur.next # cur指向了尾节点 cur.next = self._head.next self._head = self._head.next else: # 链表只有一个节点 self._head = None else: pre = self._head # 第一个节点不是要删除的 while cur.next != self._head: # 找到了要删除的元素 if cur.item == item: # 删除 pre.next = cur.next return else: pre = cur cur = cur.next # cur 指向尾节点 if cur.item == item: # 尾部删除 pre.next = cur.next def search(self, item): """查找节点是否存在""" if self.is_empty(): return False cur = self._head if cur.item == item: return True while cur.next != self._head: cur = cur.next if cur.item == item: return True return False if __name__ == "__main__": ll = SinCycLinkedlist() ll.add(1) ll.add(2) ll.append(3) ll.insert(2, 4) ll.insert(4, 5) ll.insert(0, 6) print("length:",ll.length()) ll.travel() print(ll.search(3)) print(ll.search(7)) ll.remove(1) print("length:",ll.length()) ll.travel()
一种更复杂的链表是“双向链表”或“双面链表”。每一个节点有两个连接:一个指向前一个节点,当此节点为第一个节点时,指向空值;而另外一个指向下一个节点,当此节点为最后一个节点时,指向空值。
class Node(object): #节点的类 def __init__(self,item): self.item = item self.prev = None self.next = None class DLinkList(object): #双向链表的类 def __init__(self): #指向链表的头节点 self._head = None def is_empty(self): #链表是否为空 return self._head == None def length(self): #链表长度 cur = self._head #计数器 count = 0 while cur != None: count += 1 cur = cur.next return count def travel(self): #遍历链表 cur = self._head while cur != None: print(cur.item) cur = cur.next def add(self,item): #链表头部添加 node = Node(item) if self.is_empty(): #若是是空链表,将_head指向node #给链表添加第一个元素 self._head = node else: #若是链表不为空,在新的节点和原来的首节点之间创建双向连接 node.next = self._head self._head.prev = node #让_head指向链表的新的首节点 self._head = node def append(self,item): #链表尾部添加 #建立新的节点 node = Node(item) if self.is_empty(): #空链表, self._head = node else: #链表不为空 cur = self._head while cur.next != None: cur = cur.next #cur的下一个节点是node cur.next = node #node的上一个节点是 node.prev = cur def insert(self,pos,item): #指定位置添加 if pos <=0: self.add(item) elif pos > self.length()-1: self.append() else: node = Node(item) cur = self._head count = 0 #把cur移动到指定位置的前一个位置 while count < (pos - 1): count+=1 cur = cur.next #node的prev指向cur node.prev = cur #node的next指向cur的next node.next = cur.next cur.next.prev = node cur.next = node def remove(self,item): #删除节点 if self.is_empty(): return else: cur = self._head if cur.item == item: #首节点是要删除的节点 if cur.next == None: #说明链表中只有一个节点 self._head = None else: #链表多于一个节点的状况 cur.next.prev = None self._head = cur.next else: # 首节点不是要删除的节点 while cur != None: if cur.item == item: cur.prev.next = cur.next cur.next.prev = cur.prev break cur = cur.next def search(self,item): #查找节点是否存在 cur = self._head while cur != None: if cur.item == item: return True cur = cur.next return False if __name__ == '__main__': dls = DLinkList() dls.add(10) dls.add(12) dls.append(15) dls.append(16) dls.insert(2,32) dls.insert(3,36) print('dls lenght:',dls.length()) dls.travel() print(dls.search(15)) dls.remove(32) print('dls length:',dls.length()) dls.travel()
栈(stack),有些地方称为堆栈,是一种容器,可存入数据元素、访问元素、删除元素,它的特色在于只能容许在容器的一端(称为栈顶端指标,英语:top)进行加入数据(英语:push)和输出数据(英语:pop)的运算。没有了位置概念,保证任什么时候候能够访问、删除的元素都是此前最后存入的那个元素,肯定了一种默认的访问顺序。
因为栈数据结构只容许在一端进行操做,于是按照后进先出(LIFO, Last In First Out)的原理运做。
栈能够用顺序表实现,也能够用链表实现。
class Stack(object): #栈的实现 def __init__(self): self.items = [] def is_empty(self): #判断栈是否为空 return self.items == [] def push(self,item): #压栈:在栈中加入数据元素 self.items.append(item) def pop(self): #弹栈操做:从栈中弹出元素 return self.items.pop() def peek(self): #返回栈顶的数据元素 return self.items[len(self.items)-1] def size(self): #返回栈的尺寸 return len(self.items) if __name__ == '__main__': stack = Stack() print(stack.is_empty()) stack.push('hello') stack.push('python') stack.push('qiku') stack.push('zhengzhou') print(stack.is_empty()) print(stack.size()) print(stack.peek()) print(stack.pop()) print(stack.pop()) print(stack.pop()) print(stack.pop())
队列(queue)是只容许在一端进行插入操做,而在另外一端进行删除操做的线性表。
队列是一种先进先出的(First In First Out)的线性表,简称FIFO。
容许插入的一端为队尾,容许删除的一端为队头。队列不容许在中间部位进行操做!假设队列是q=(a1,a2,……,an),那么a1就是队头元素,而an是队尾元素。这样咱们就能够删除时,老是从a1开始,而插入时,老是在队列最后。这也比较符合咱们一般生活中的习惯,排在第一个的优先出列,最后来的固然排在队伍最后。
同栈同样,队列也能够用顺序表或者链表实现。
class Queue(object): #队列的实现 def __init__(self): self.items = [] def is_empty(self): #判断队列是否为空 return self.items == [] def enqueue(self,item): #插入到队列的头部 self.items.insert(0,item) def dequeue(self): #删除数据 return self.items.pop() def size(self): #返回队列的大小 return len(self.items) if __name__ == '__main__': q = Queue() q.enqueue("hello") q.enqueue("sssss") q.enqueue("aaaaa") print(q.size()) print(q.is_empty()) print(q.dequeue()) print(q.size()) print(q.dequeue()) print(q.size())
双端队列(deque,全名double-ended queue),是一种具备队列和栈的性质的数据结构。
双端队列中的元素能够从两端弹出,其限定插入和删除操做在表的两端进行。双端队列能够在队列任意一端入队和出队。
#双端列表的定义 class Deque(object): def __init__(self): self.items = [] def add_front(self,item): #从队头加入一个item元素 self.items.insert(0,item) def add_rear(self,item): #从队尾加入一个item元素 self.items.append(item) def remove_front(self): #从队头删除一个item元素 return self.items.pop(0) def remove_rear(self): #从队尾删除一个item元素 return self.items.pop() def is_empty(self): #判断双端队列是否为空 return self.items == [] def size(self): # 返回队列的大小 return len(self.items) if __name__ == '__main__': deqeue = Deque() print(deqeue.is_empty()) deqeue.add_front(22) deqeue.add_front(33) deqeue.add_rear(44) deqeue.add_rear(55) print(deqeue.is_empty()) print(deqeue.size()) print(deqeue.remove_front()) print('size:',deqeue.size()) print(deqeue.remove_rear()) print('size:',deqeue.size())
排序算法(英语:Sorting algorithm)是一种能将一串数据依照特定顺序进行排列的一种算法。
稳定性:稳定排序算法会让本来有相等键值的纪录维持相对次序。也就是若是一个排序算法是稳定的,当有两个相等键值的纪录R和S,且在本来的列表中R出如今S以前,在排序过的列表中R也将会是在S以前。
当相等的元素是没法分辨的,好比像是整数,稳定性并非一个问题。然而,假设如下的数对将要以他们的第一个数字来排序。
(4, 1) (3, 1) (3, 7)(5, 6)
在这个情况下,有可能产生两种不一样的结果,一个是让相等键值的纪录维持相对的次序,而另一个则没有:
(3, 1) (3, 7) (4, 1) (5, 6) (维持次序)
(3, 7) (3, 1) (4, 1) (5, 6) (次序被改变)
不稳定排序算法可能会在相等的键值中改变纪录的相对次序,可是稳定排序算法历来不会如此。不稳定排序算法能够被特别地实现为稳定。做这件事情的一个方式是人工扩充键值的比较,如此在其余方面相同键值的两个对象间之比较,(好比上面的比较中加入第二个标准:第二个键值的大小)就会被决定使用在原先数据次序中的条目,看成一个同分决赛。然而,要记住这种次序一般牵涉到额外的空间负担。
冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,若是他们的顺序错误就把他们交换过来。遍历数列的工做是重复地进行直到没有再须要交换,也就是说该数列已经排序完成。这个算法的名字由来是由于越小的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序算法的运做以下:
def bubble_sort(alist): for j in range(len(alist)-1,0,-1): # j表示每次遍历须要比较的次数,是逐渐减少的 for i in range(j): if alist[i] > alist[i+1]: alist[i], alist[i+1] = alist[i+1], alist[i] li = [54,26,93,17,77,31,44,55,20] bubble_sort(li) print(li)
时间复杂度
选择排序(Selection sort)是一种简单直观的排序算法。
它的工做原理以下:
选择排序的主要优势与数据移动有关。若是某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,所以对n个元素的表进行排序总共进行至多n-1次交换。在全部的彻底依靠交换去移动元素的排序方法中,选择排序属于很是好的一种。
def selection_sort(alist): n = len(alist) # 须要进行n-1次选择操做 for i in range(n-1): # 记录最小位置 min_index = i # 从i+1位置到末尾选择出最小数据 for j in range(i+1, n): if alist[j] < alist[min_index]: min_index = j # 若是选择出的数据不在正确位置,进行交换 if min_index != i: alist[i], alist[min_index] = alist[min_index], alist[i] alist = [54,226,93,17,77,31,44,55,20] selection_sort(alist) print(alist)
时间复杂度
插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工做原理是经过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程当中,须要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
def insert_sort(alist): # 从第二个位置,即下标为1的元素开始向前插入 for i in range(1, len(alist)): # 从第i个元素开始向前比较,若是小于前一个元素,交换位置 for j in range(i, 0, -1): if alist[j] < alist[j-1]: alist[j], alist[j-1] = alist[j-1], alist[j] alist = [54,26,93,17,77,31,44,55,20] insert_sort(alist) print(alist)
时间复杂度