文章目录
常见的算法 & 数据结构
时间复杂度
- 评判规则:量化算法执行步骤的数量
- 最重要的项 :时间复杂度表达式中最有意义的项(如果最高阶项存在,则省去最高阶项前面的系数。)
- 大O记法:就是使用时间复杂度衡量算法的好坏的表现形式
分析算法时,需要考虑的
-
算法完成工作最少需要多少基本操作,即最优时间复杂度
-
算法完成工作最多需要多少基本操作,即最坏时间复杂度
-
算法完成工作平均需要多少基本操作,即平均时间复杂度
对于最坏时间复杂度,提供了一种保证,表明算法在此种程度的基本操作中一定能完成工作。
因此,我们主要关注算法的最坏情况,亦即最坏时间复杂度。
时间复杂度的基本计算规则
- 基本操作,即只有常数项,认为其时间复杂度为O(1)
- 顺序结构,时间复杂度按加法进行计算
- 循环结构,时间复杂度按乘法进行计算
- 分支结构,时间复杂度取最大值
- 判断一个算法的效率时,往往只需要关注操作数量的最高次项,其它次要项和常数项可以忽略
- 在没有特殊说明时,我们所分析的算法的时间复杂度都是指最坏时间复杂度
算法是解决实际问题而设计的,数据结构是算法处理问题的载体。
- timeit 模块︰该模块可以用来测试一段python代码的执行速度/时长。
- Timer类该类是 timeit 模块中专门用于测量python代码的执行速度/时长的。原型为: class
timeit.Timer(stmt=‘pass’, setup=‘pass’)- stmt 参数:表示即将进行测试的代码块语句。
- setup:运行代码块语句时所需要的设置。
- timeit函数: timeit.Timer.timeit(number=100000),该函数返回代码块语句执行number次的平均耗时。
from timeit import Timer
def text1():
list = []
for i in range(100):
list.append(i)
return list
def text2():
list = [i for i in range(100)]
return list
if __name__ == '__main__':
timer1 = Timer(stmt='text1', setup='from __main__ import text1')
t1 = timer1.timeit(100)
timer2 = Timer(stmt='text2', setup='from __main__ import text2')
t2 = timer2.timeit(100)
print(t1, t2)
栈
特性:栈(stacks)是一种只能通过访问其一端来实现数据存储与检索的线性数据结构,具有”后进先出“的特征
应用︰每个web浏览器都有一个返回按钮。当你浏览网页时,这些网页被放置在一个栈中(实际是网页的网址)。你现在查看的网页在顶部,你第一个查看的网页在底部。如果按 “返回” 按钮,将按相反的顺序浏览刚才的页面。
- Stack() 创建一个空的新栈。它不需要参数,并返回一个空栈。
- push(item) 将一个新项添加到栈的顶部。它需要item做参数并不返回任何内容。
- pop() 从栈中删除顶部项。它不需要参数并返回item。栈被修改。
- peek() 从栈返回顶部项,但不会删除它。不需要参数。不修改栈。
- isEmpty() 测试栈是否为空。不需要参数,并返回布尔值。
- size() 返回栈中的item数量。不需要参数,并返回一个整数。
# 栈 先进后出
class Stack():
def __init__(self):
self.items = []
def push(self, item): # 向栈里添加元素
self.items.append(item)
def pop(self): # 从栈底取元素
return self.items.pop()
def peek(self): # 返回栈顶元素下标
return len(self.items) - 1
def isEmpty(self): # 判断是否为空
return self.items == []
def size(self): # 大小
return len(self.items)
s = Stack()
s.push(1)
s.push(2)
s.push(3)
print(s.pop())
print(s.pop())
print(s.pop())
队列
概念:队列是项的有序结合,其中添加新项的一端称为队尾,移除项的一端称为队首。当一个元素从队尾进入队列时,一直向队首移动,直到它成为下一个需要移除的元素为止。最近添加的元素必须在队尾等待。集合中存活时间最长的元素在队首,这种排序成为 FIFO,先进先出,也被成为先到先得。。python库from collections import deque
可以实现。
- Queue() 创建一个空的新队列。它不需要参数,并返回一个空队列。
- enqueue(item) 将新项添加到队尾。它需要item作为参数,并不返回任何内容。
- dequeue() 从队首移除项。它不需要参数并返回item。队列被修改。
- isEmpty () 查看队列是否为空。它不需要参数,并返回布尔值。
- size() 返回队列中的项数。它不需要参数,并返回一个整数。
# 队列 先进先出
class Queue():
def __init__(self):
self.items = []
def enqueue(self, item): # 进去
self.items.insert(0, item)
def dequeue(self): # 取出
return self.items.pop()
def isEmpty(self): # 是否为空
return self.items == []
def size(self): # 大小
return len(self.items)
k = Queue()
k.enqueue(1)
k.enqueue(2)
k.enqueue(3)
print(k.dequeue())
print(k.dequeue())
print(k.dequeue())
两个队列生成一个栈
q1 = Queue()
q2 = Queue()
alist = [1,2,3,4,5]
#将数据加入队列
for i in alist:
q1.enqueue(i)
while True:
while q1.size() > 1:
item = q1.dequeue()
q2.enqueue(item)
print(q1.dequeue())
q1,q2 = q2,q1
if q1.size() == 0:
break
烫手的山芋
-
案例:烫手的山芋
-
烫手山芋游戏介绍:6个孩子围城一个圈,排列顺序孩子们自己指定。第一个孩子手里有一个烫手的山芋,需要在计时器计时 1秒 后将山芋传递给下一个孩子,依次类推。规则是,在计时器每计时 7 秒时,手里有山芋的孩子退出游戏。该游戏直到剩下一个孩子时结束,最后剩下的孩子获胜。请使用队列实现该游戏策略,排在第几个位置最终会获胜。
-
分析: 在一轮游戏中山芋会被传递6次 山芋传递的次数不受孩子个数的影响 山芋传递六次后一轮游戏结束,淘汰一个孩子游戏继续 队列∶先进先出,只可以从对头取元素,从队尾添加元素。 准则:保证队头孩子手里面有山芋(谁手里有山芋谁作为队头) 方便删除元素。最终7秒到的时候需要将手里有山芋的孩子从队列中剔除。
-
# 烫手的山芋
kids = ['A', 'B', 'c', 'D', 'E', 'F']
queue = Queue()
for kid in kids:
queue.enqueue(kid) # 将6个孩子添加到了队列中
while queue.size() > 1:
# 山芋传递的次数
for i in range(6):
# 让队头的孩子出队列在入队列
kid = queue.dequeue()
queue.enqueue(kid)
# 当6次循环结束后,说明山芋被传递了6次,说明一轮游戏结束
# —轮游戏结束后,将队头孩子淘汰即可
queue.dequeue()
# 当while循环结束后,游戏结束,队列中仅剩的孩子就是获胜者
print('获胜者:', queue.dequeue())
- 使用栈实现队列
- 使用两个站如何实现队列
- 使用两个栈实现先进先出的特性
- 使用两个站如何实现队列
双端队列
一.双端队列(Deque)
- 概念:deque(也称为双端队列)是与队列类似的项的有序集合。它有两个端部,首部和尾部,并且项在集合中保持不变。
- 特性:deque 特殊之处在于添加和删除项是非限制性的。可以在前面或后面添加新项。同样,可以从任一端移除现有项。在某种意义上,这种混合线性结构提供了单个数据结构中的栈和队列的所有能力。
- 注意:即使 deque 可以拥有栈和队列的许多特性,它不需要由那些数据结构强制的 LIFO 和 FIFO 排序。这取决于你如何持续添加和删除操作。
-
同同列相比,有两个头部和尾部。可以在双端进行数据的插入和删除,提供了单数据结构中栈和队列的特性
- Deque() 创建一个空的新deque。它不需要参数,并返回空的deque。 - addFront(item) 将一个新项添加到deque的首部。它需要item参数并不返回任何内容。 - addRear(item) 将一个新项添加到deque的尾部。它需要item参数并不返回任何内容。 - removeFront) 从deque中删除首项。它不需要参数并返回item。deque被修改。 - removeRear() 从deque中删除尾项。它不需要参数并返回item。deque被修改。 - isEmpty() 测试deque是否为空。它不需要参数,并返回布尔值。 - size() 返回deque中的项数。它不需要参数,并返回一个整数。
class Deque:
def __init__(self):
self.items = []
def isEmpty(self):
return self.items == []
def addFront(self, item):
self.items.append(item)
def addRear(self, item):
self.items.insert(0,item)
def removeFront(self):
return self.items.pop()
def removeRear(self):
return self.items.pop(0)
def size(self):
return len(self.items)
内存
-
计算机的作用
- 存储和运算二进制的数据
-
计算机如何实现 1+ 1的操作?
- 将 1 加载到计算机内存中,然后基于计算机的加法寄存器对指定内存中存储的数据进行加法运算。
-
变量的概念
-
本质讲,变量指的就是计算机中的某一块内存空间。
-
内存空间有两个固有的属性
-
地址︰使用16进制的数表示
- 作用:方便cup寻址。门牌号。
-
大小: bit, byte, kb , mb , gb , tb
- 决定该块内存存储的数值的范围
-
-
-
理解 a=10 的内存图(引用,指向)
- 引用︰就是变量,通常将变量表示的就是一块内存空间的地址。
- 指向︰如果一个变量或者引用存储表示了某块内存空间的地址后,则该变量或引用指向了该块内存空间。
-
不同数据占用内存空间的大小
- bit (位):1 bit 只能存储一位二进制的数
- byte(字节):8 bit
-
给相同的数据类型的数据开辟固有大小的内存空间
- 整形数据:4 byte
- 浮点型:4, 8 byte
顺序表
- 多数据类型顺序表的内存图(内存非连续开辟 )
- 顺序表的弊端:顺序表的结构需要预先知道数据大小来申请连续的存储空间,而进行数据扩充时又需要进行数据的搬迁(时间复杂度是相当高的)
链表
相对于顺序表,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理且进行扩充是不需要进行数据搬迁。
链表(Linked list) 是一种常见的基础数据结构,是一种线性表,但是不像 顺序表一样连续存储数据,而是每一个节点(数据存储单元)里存放下一个节点的信息(即地址),非连续开辟数据空间并连到一起
-
is_empty(): 链表是否为空
-
length ( ): 链表长度
-
travel ( ): 遍历整个链表
-
add(item): 链表头部添加元素
-
append(item): 链表尾部添加元素
-
insert(pos, item): 指定位置添加元素
-
remove(item): 删除节点
-
search(item): 查找节点是否存在
# 节点的封装
class Node():
def __init__(self, item):
self.item = item
self.next = None
# 链表的封装
class Link():
def __init__(self): # 构建一个空链表
self._head = None # _head永远指向链表中的第一个节点,None表示没有节点
def add(self, item): # 向链表的头部添加节点(inster(0, item))
node = Node(item)
node.next = self._head
self._head = node # 向头部添加元素
def travel(self): # 遍历
cur = self._head
while cur:
print(cur.item)
cur = cur.next
def is_empty(self):
return self._head == None # 如果链表的头部为空,则链表为空
def length(self): # 获取链表的长度
cur = self._head
count = 0
while cur:
count += 1
cur = cur.next
print(count)
def append(self, item): # 往最后添加一个节点
node = Node(item)
cur = self._head
if cur == None:
cur = node
pre = None # pre 指向为 cur 的 前一位
while cur:
pre = cur
cur = cur.next
# 循环体结束后,pre成为最后一个节点。
pre.next = node # 添加成功
def search(self, item): # 查询
cur = self._head
find = False
while cur:
if cur.item == item:
find = True
break
else:
cur.next
return find
def reverse(self): # 链表倒序
pre = self._head
cur = pre.next
node_text = cur.next
pre.next = None
while True:
cur.next = pre
pre = cur
cur = node_text
if node_text != None:
node_text = node_text.next
else:
break
self._head = pre
def remove(self, item): # 删除节点
pre = None
cur = self._head
if cur.item == item:
self._head = cur.next
return
else:
while cur is not None:
pre = cur
cur = cur.next
if cur.item == item:
pre.next = cur.next
return
def insertTo(self,item,index): # 插入
cur = self._head
ex = 0
node = Node(item)
#插入到第一个节点位置
if index <= 0:
self.add(item)
#插入到最后一个节点位置
elif index >= self.length():
self.append(item)
else:
while cur is not None:
pre = cur
cur = cur.next
#此处插入的一定不是第一个节点和最后一个节点位置,因此index要减1
if ex == index-1:
node.next = cur
pre.next = node
break
ex += 1
链表排序
思路:
- 3,8,5,7,6,将这几个值封装到5个节点中,首先将3对应的节点插入到链表中。然后插入后序节点的时候,都需要先进行判断将节点合适的位置找到,然后将其插入到合适的为中。
两个队列生成一个链表
# 两个队列生成一个链表
items = [1,2,3,4,5,6,7]
q1 = Queue()
q2 = Queue()
for item in items:
q1.enqueue(item)
while True:
while q1.size() > 1:
item = q1.dequeue()
q2.enqueue(item)
print(q1.dequeue())
q1, q2 = q2, q1
if q1.size() == 0:
break
数组和链表的区别
1.链表是什么
链表是一种上一个元素的引用指向下一个元素的存储结构,链表通过指针来连接元素与元素;
链表是线性表的一种,所谓的线性表包含顺序线性表和链表,顺序线性表是用数组实现的,在内存中有顺序排列,通过改变数组大小实现。而链表不是用顺序实现的,用指针实现,在内存中不连续。意思就是说,链表就是将一系列不连续的内存联系起来,将那种碎片内存进行合理的利用,解决空间的问题。
所以,链表允许插入和删除表上任意位置上的节点,但是不允许随即存取。链表有很多种不同的类型:单向链表、双向链表及循环链表。
2.单向链表
单向链表包含两个域,一个是信息域,一个是指针域。也就是单向链表的节点被分成两部分,一部分是保存或显示关于节点的信息,第二部分存储下一个节点的地址,而最后一个节点则指向一个空值。
3.双向链表
从上图可以很清晰的看出,每个节点有2个链接,一个是指向前一个节点(当此链接为第一个链接时,指向的是空值或空列表),另一个则指向后一个节点(当此链接为最后一个链接时,指向的是空值或空列表)。意思就是说双向链表有2个指针,一个是指向前一个节点的指针,另一个则指向后一个节点的指针。
# coding=utf-8
# 双向链表
class Node:
"""节点"""
def __init__(self, item):
self.item = item
self.prev = None
self.next = None
class DLinkList:
"""双向链表"""
def __init__(self):
self._head = None
def is_empty(self):
"""判断链表是否为空"""
return self._head is None
def length(self):
"""获取链表长度"""
if self.is_empty():
return 0
else:
cur = self._head
count = 1
while cur.next is not None:
count += 1
cur = cur.next
return count
def travel(self):
"""遍历链表"""
print("↓↓" * 10)
if self.is_empty():
print("")
else:
cur = self._head
print(cur.item)
while cur.next is not None:
cur = cur.next
print(cur.item)
print("↑↑" * 10)
def add(self, item):
"""链表头部添加节点"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
node.next = cur
cur.prev = node
self._head = node
def append(self, item):
"""链表尾部添加节点"""
node = Node(item)
if self.is_empty():
self._head = node
else:
cur = self._head
# 遍历找到最后一个节点
while cur.next is not None:
cur = cur.next
# 在尾节点添加新的节点
cur.next = node
node.prev = cur
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
cur_pos = 0
while cur.next is not None:
if cur_pos == (pos - 1):
# 与下一个节点互相指向
node.next = cur.next
cur.next.prev = node
# 与上一个节点互相指向
cur.next = node
node.prev = cur
cur_pos += 1
cur = cur.next
def remove(self, item):
"""删除节点"""
if self.is_empty():
return
else:
cur = self._head
# 删除首节点
if cur.item == item:
self._head = cur.next
cur.next.prev = None
# 删除其他节点
else:
while cur.next is not None:
if cur.item == item:
# 删除之前:1 ←→ [2] ←→ 3
# 删除之后:1 ←→ 3
cur.prev.next = cur.next
cur.next.prev = cur.prev
cur = cur.next
# 删除尾节点
if cur.item == item:
cur.prev.next = None
def search(self, item):
"""查找节点是否存在"""
if self.is_empty():
return -1
else:
cur = self._head
cur_pos = 0
while cur.next is not None:
if cur.item == item:
return cur_pos
cur_pos += 1
cur = cur.next
if cur_pos == (self.length() - 1):
return -1
if __name__ == "__main__":
ll = DLinkList()
ll.add(1) # 1
ll.add(2) # 2 1
ll.append(3) # 2 1 3
ll.insert(2, 4) # 2 1 4 3
ll.insert(4, 5) # 2 1 4 3 5
ll.insert(0, 6) # 6 2 1 4 3 5
print("length:", ll.length()) # 6
ll.travel() # 6 2 1 4 3 5
print("search(3)", ll.search(3))
print("search(4)", ll.search(4))
print("search(10)", ll.search(10))
ll.remove(1)
print("length:", ll.length())
ll.travel()
print("删除首节点 remove(6):")
ll.remove(6)
ll.travel()
print("删除尾节点 remove(5):")
ll.remove(5)
ll.travel()
4.循环链表
循环链表就是首节点和末节点被连接在一起。循环链表中第一个节点之前就是最后一个节点,反之亦然。
# coding=utf-8
# 单向循环链表
class Node:
"""节点"""
def __init__(self, item):
self.item = item
self.next = None
def __str__(self):
return str(self.item)
class SinCycLinkedList:
"""单向循环链表"""
def __init__(self):
self._head = None
def is_empty(self):
"""判断链表是否为空"""
return self._head is None
def length(self):
"""链表长度"""
if self.is_empty():
return 0
count = 1
cur = self._head
while cur.next != self._head:
# print("cur", cur.item)
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:
node.next = self._head
cur = self._head
while cur.next != self._head:
cur = cur.next
cur.next = 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
# print(type(cur), cur.item, cur.next)
while cur.next != self._head:
cur = cur.next
# print(cur.item)
cur.next = node
node.next = self._head
def insert(self, pos, item):
"""指定位置pos添加节点"""
if pos <= 0:
self.add(item)
elif pos > (self.length() - 1):
self.append(item)
else:
node = Node(item)
cur = self._head
cur_pos = 0
while cur.next != self._head:
if (pos - 1) == cur_pos:
node.next = cur.next
cur.next = node
break
cur_pos += 1
cur = cur.next
def remove(self, item):
"""删除一个节点"""
if self.is_empty():
return
pre = self._head
# 删除首节点
if pre.item == item:
cur = pre
while cur.next != self._head:
cur = cur.next
cur.next = pre.next # 删除首节点(跳过该节点)
self._head = pre.next # 重新指定首节点
# 删除其他的节点
else:
cur = pre
while cur.next != self._head:
if cur.next.item == item:
cur.next = cur.next.next
cur = cur.next
def search(self, item):
"""查找节点是否存在"""
if self.is_empty():
return -1
cur_pos = 0
cur = self._head
if cur.item == item:
return cur_pos
while cur.next != self._head:
if cur.item == item:
return cur_pos
cur_pos += 1
cur = cur.next
if cur_pos == self.length() - 1:
return -1
if __name__ == "__main__":
ll = SinCycLinkedList()
ll.add(1) # 1
ll.add(2) # 2 1
# ll.travel()
ll.append(3) # 2 1 3
ll.insert(2, 4) # 2 1 4 3
ll.insert(4, 5) # 2 1 4 3 5
ll.insert(0, 6) # 6 2 1 4 3 5
print("length:", ll.length()) # 6
ll.travel() # 6 2 1 4 3 5
print("search(3)", ll.search(3)) # 4
print("search(7)", ll.search(7)) # -1
print("search(6)", ll.search(6)) # 0
print("remove(1)")
ll.remove(1)
print("length:", ll.length()) # 6 2 4 3 5
print("remove(6)")
ll.remove(6)
ll.travel()
5.数组和链表的区别?
链表通过指针来连接元素与元素,数组则是把所有元素按次序依次存储。
链表的插入删除元素相对数组较为简单,不需要移动元素,且较为容易实现长度扩充,但是寻找某个元素较为困难;
数组寻找某个元素较为简单,但插入与删除比较复杂,由于最大长度需要再编程一开始时指定,故当达到最大长度时,扩充长度不如链表方便。
相同:两种结构均可实现数据的顺序存储,构造出来的模型呈线性结构。
6.链表的应用、代码实践
约瑟夫问题:
传说在公园1世纪的犹太战争中,犹太约瑟夫是公元一世纪著名的历史学家。在罗马人占领乔塔帕特后,39 个犹太人与约瑟夫及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人俘虏,于是决定了一个流传千古的自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报到第3人该人就必须自杀,然后再由下一个人重新报数,直到所有人都自杀身亡为止。然而约瑟夫和他的朋友并不想遵从这个约定,约瑟夫要他的朋友先假装遵从,他将朋友与自己安排在第_个和第_个位置,于是逃过了这场死亡游戏,你知道安排在了第几个嘛?
针对以上问题,使用单向循环链表的方式求解:
二叉树
二叉树的插入
class Node():
def __init__(self, item):
self.item = item
self.left = None # 指向该节点的左节点
self.right = None # 指向该节点的右节点
class Tree():
def __init__(self): # 构建一个空树
self.root = None
def insert(self,item): # 向树中插入一个节点
node = Node(item)
# 如果树为空
if self.root == None:
self.root = node
return
# 树不为空
cur = self.root
a = [cur]
while True:
b = a.pop(0)
if b.left != None:
a.append(b.left)
else:
b.left = node
break
if b.right != None:
a.append(b.right)
else:
b.right = node
break
二叉树的遍历
# 广度遍历
def travel(self):
cur = self.root
q = [cur]
while q:
n = q.pop(0)
print(n.item)
if n.left != None:
q.append(n.left)
if n.right != None:
q.append(n.right)
tree = Tree()
tree.insert(1)
tree.insert(2)
tree.insert(3)
tree.insert(5)
tree.insert(6)
tree.travel()
# 深度遍历
# 参数root表示的是子树的根节点,需要给递归调用的forWord传入不同的子树的根节点
def forword(self, root): # 前
if root == None:
return
print(root.item)
# print(root.left.item)
self.forword(root.left)
# print(root.right.item)
self.forword(root.right)
def middle(self, root): # 中
if root == None:
return
self.middle(root.left)
print(root.item)
self.middle(root.right)
def back(self, root): # 后
if root == None:
return
self.back(root.left)
self.back(root.right)
print(root.item)
深度遍历的实现思路:
深度遍历是需要作用到每一颗子树中
子树和子树之间的区别体现在根节点中。
如果写一个函数,该函数可以将一个子树中的节点进行遍历,则将该函数作用到其他子树中就可以将整棵树进行深度遍历。
排序二叉树
class Node():
def __init__(self, item):
self.item = item
self.left = None # 指向该节点的左节点
self.right = None # 指向该节点的右节点
class SortTree():
def __init__(self):
self.root = None
def add(self, item):
node = Node(item)
if self.root == None: # 树为空的情况
self.root = node
return
cur = self.root
while True: # 非空
if cur.item > item: # 插入的节点值小于 根节点, 该节点需要插入根节点的左侧
if cur.left == None: # 左侧为空,直接插入
cur.left = node
break
else: # 左侧非空
cur = cur.left
else: # 插入的节点值大于 根节点, 该节点需要插入根节点的右侧
if cur.right == None:
cur.right = node
break
else:
cur = cur.right
def middle(self, root): # 中排序
if root == None:
return
self.middle(root.left)
print(root.item)
self.middle(root.right)
list = [3, 8, 5, 7, 6, 2, 1]
tree1 = SortTree()
for i in list:
tree1.add(i)
tree1.middle(tree1.root)
二分查找
- 二分查找只可以作用于有序序列中
def find(alist, item):
find = False
low = 0
high = len(alist) - 1
while low <= high:
mid = (low + high) // 2
if item < alist[mid]:
high = mid - 1
elif item > alist[mid]:
low = mid + 1
else:
find = True
break
return find
alist = [1,2,3,4,5]
print(find(alist, 4))
排序算法
冒泡算法
def sort(list):
for j in range(len(list)-1):
if list[j] > list[j+1]:
list[j], list[j+1] = list[j+1], list[j]
return list
# 先找到最大值放在最后面
def sort(alist):
for j in range(len(alist) - 1):
for i in range(len(alist) - 1 - j):
if alist[i] > alist[i + 1]:
alist[i], alist[i + 1] = alist[i + 1], alist[i]
return alist
# 先找到最大值,再找第二大,第三大。。。。。
list = [2, 4, 6, 3, 7, 9, 5, 1, 2, 2]
print(sort(list))
选择排序
先找出第一个最大值:把列表中的第一个值当成最大值,再跟第二个值相比较,再第二个与第三个,第三个与第四个相比较。。。。。最后找到最大值
def sort(list):
max_index = 0 # max_index 是最大值的下标,假设最开始最大值为下标为 0 的数
for i in range(len(list)-1):
if list[max_index] < list[i+1]:
max_index = i+1
list[len(list) - 1], list[max_index] = list[max_index], list[len(list) - 1]
return list
list1 = [7, 6, 10, 3, 8, 2, 4, 9, 1, 5]
print(sort(list1))
循环列表,先找到第一个最大值,再找到第二个最大值,第三个最大值。。。。
def sort(list):
for j in range(len(list) - 1):
max_index = 0 # max_index 是最大值的下标,假设最开始最大值为下标为 0 的数
for i in range(len(list) - 1 - j):
if list[max_index] < list[i + 1]:
max_index = i + 1
list[len(list) - 1 - j], list[max_index] = list[max_index], list[len(list) - 1 - j]
return list
list = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list))
插入排序
思路:
需要将原始序列分成两部分:有序部分,无序部分
– 将无序部分中的元素逐一插入到有序部分中
-
注意:初始情况下,有序部分为乱序序列的第一个元素,无序部分为乱序序列的n-1个元素
-
乱序序列:.[3,8,5,Z,61.
-
[3…8,5,7,6], 3就是初始的有序部分,8,5,7,6就是初始的无序部分
-
.[3,8…5,7,6].
-
.[3,5,8…7,6].
-
.[3,5,7,8…6].
-
.[3,5,6,7,8…].
# [2.....5,7,8,3。。。。] 最开始前list[0]为有序序列,后面的为无需序列
# [2,5.....7,8,3。。。。]
# [3,5,8.....7,6。。。。]
# [3,5,7,8.....6。。。。].
# [3,5,6,7,8....。。。。]
前面的为有序,后面的为无序
# list[i-1] i-1:有序序列的最后一个下标
# list[i] i:无需序列的第一个下标
# 先找到第一个最大值
def sort(list):
for i in range(1, len(list)):
if list[i-1] > list[i]:
list[i-1], list[i] = list[i], list[i-1]
return list
list = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list))
# 全部循环排序(完整代码)
def sort(list):
for i in range(1, len(list)):
while i > 0:
if list[i - 1] > list[i]:
list[i - 1], list[i] = list[i], list[i - 1]
i -= 1
else:
break
return list
list = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list))
希尔排序
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本,该方法的基本思想是:先将整个待排元素序列分割成若干个子序列(由相隔某个“增量(gap)”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率比直接插入排序有较大提高。
def sort(list):
gap = len(list) // 2 # 增量
while gap >= 1:
for i in range(gap, len(list)):
while i > 0:
if list[i - gap] > list[i]:
list[i - gap], list[i] = list[i], list[i - gap]
i -= gap
else:
break
gap //= 2
return list
list1 = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list1))
快速排序
def sort(list, left, right):
low = left # 下标为 0
high = right # 下标为list的最后一位
if low > high:
return
mid = list[low]
while low < high:
while low < high:
if list[high] > mid:
high -= 1
else:
list[low] = list[high]
break
while low < high:
if list[low] < mid:
low += 1
else:
list[high] = list[low]
break
if low == high:
list[low] = mid
break
sort(list, left, high-1) # 递归,执行左侧序列
sort(list, high+1, right) # 递归,执行右侧序列
return list
list1 = [7, 6, 3, 5, 8, 2, 4, 9, 1, 10]
print(sort(list1, 0, len(list1)-1))
(完!)