大家好,小编为大家解答python算法设计的步骤有哪些?的问题。很多人还不知道算法设计与分析(python),现在让我们一起来看看吧!
一、常见设计模式
设计模式是经过总结、优化的,对我们经常会碰到的一些编程问题的可重用解决方案
设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言
23种设计模式,其中比较常用的是单例设计模式,工厂设计模式,代理模式,装饰者模式等等
1.单例设计模式
1.1概念
什么是单例设计模式?
单例:单个实例【单个对象】,一个类只能创建一个对象
程序运行过程中,确保某一个类只有一个实例【对象】,不管在哪个模块获取这个类的对象,获取到的都是同一个对象简单编程代码python。该类有一个静态方法,向整个工程提供这个实例,例如:一个国家只有一个主席,不管他在哪
单例设计模式的核心:一个类有且仅有一个实例,并且这个实例需要应用于整个程序中,该类被称为单例类
思考:验证两个变量表示的是同一个对象,该怎么验证?
解决:验证地址,方式:变量1 is 变量2 或者 id(变量1) == id(变量2)
1.2应用场景
应用程序中描述当前使用用户对应的类 ———> 当前用户对于该应用程序的操作而言是唯一的——> 所以一般将该对象设计为单例
实际应用:数据库连接池操作 ——> 应用程序中多处地方连接到数据库 ———> 连接数据库时的连接池只需一个就行,没有必要在每个地方都创建一个新的连接池,这种也是浪费资源 ————> 解决方案也是单例
1.3实现
# 方式一
class Person(object):
# super().__new__(cls)每执行一次,就会创建一个新的对象,
# 不管p1 = Person('张三',10)执行多少次,如果让super().__new__(cls)只执行一次,那么则表示只创建了一个对象
# 思路:定义一个类属性,用于表示当前类可以创建的唯一的对象
# 注意1:定义类属性的原因:不同的对象访问到的类属性都是同一个
# 注意2:为了防止在类的外面通过类名进行修改,则将该类属性进行私有化
__instance = None
def __new__(cls, *args, **kwargs): # cls表示当前类,相当于Person
# print("new~~~~~~~~~")
# 判断__instance是否为None,如果为None,则将该类创建的唯一的对象赋值给它并返回
# 如果不为None,则直接返回
if not cls.__instance:
# print("new~~~if~~~~~~")
cls.__instance = super().__new__(cls)
return cls.__instance
def __init__(self,name,age):
# print("init~~~~~~~")
self.name = name
self.age = age
p1 = Person('张三',10) # 创建,动态绑定属性
print(p1)
p2 = Person("李四",20) # 获取,给属性重新赋值
print(p2)
print(p1 is p2) # True
print(id(p1) == id(p2)) # True
print(p1.name,p2.name) # 李四
p1.name = 'jack'
print(p1.name,p2.name) # jack
# 方式二
# 书写一个装饰器,可以将任意一个类设置为单例类,则给装饰器的内部函数设置不定长参数
def singleton(cls):
# 定义一个函数作用域内的变量,用于表示被装饰的类可以创建的唯一的对象
instance = None
# 内部函数的作用:创建被装饰的类的对象并返回
def getinstance(*args,**kwargs): # 打包
# 判断instance是否为None,如果为None,则创建被装饰的类的唯一的对象赋值给它并返回
# 如果不为None,则直接返回
nonlocal instance
if not instance:
instance = cls(*args,**kwargs) # 拆包
return instance
return getinstance
@singleton
class Person(object):
def __init__(self,name,age):
print("init~~~~~~~")
self.name = name
self.age = age
p1 = Person('张三',10) # 调用getinstance
print(p1)
p2 = Person("李四",20) # 调用getinstance
print(p2)
print(p1 is p2) # True
print(id(p1) == id(p2)) # True
print(p1.name,p2.name) # 张三
p1.name = 'jack'
print(p1.name,p2.name) # jack
# 方式三
def singleton(cls):
# 定义一个函数作用域内的字典变量,key:被装饰器的类,value:被装饰的类可以创建的唯一的对象
instance_dict = {}
# 内部函数的作用:创建被装饰的类的对象并返回
def getinstance(*args,**kwargs): # 打包
# 判断instance_dict是否为空,如果为空,则创建被装饰的类的唯一的对象,并添加为字典的键值对
# 如果不为None,则直接返回
if not instance_dict:
instance_dict[cls] = cls(*args,**kwargs) # 添加键值对
return instance_dict[cls]
return getinstance
@singleton
class Person(object):
def __init__(self,name,age):
print("init~~~~~~~")
self.name = name
self.age = age
p1 = Person('张三',10) # 调用getinstance
print(p1)
p2 = Person("李四",20) # 调用getinstance
print(p2)
print(p1 is p2) # True
print(id(p1) == id(p2)) # True
print(p1.name,p2.name) # 张三
p1.name = 'jack'
print(p1.name,p2.name) # jack
2.生产者消费者模式
"""
生产者消费者设计模式是通过一个容器解决强耦合问题,,生产者消费者之间不直接通信,而是通过队列进行通信
所以生产者生产完数据之后不需要等待消费者处理,直接将数据扔给队列,消费者不需要找生产者,而是直接从队列获取
队列相当于是一个缓存区,平衡了生产者消费者的处理能力
"""
import threading,time,queue,random
# 生产者:向队列中添加数据
def product(id,queue):
while True:
num = random.randint(1,100)
queue.put(num)
print(f"生产者{id}向队列中添加了{num}数据")
time.sleep(1)
# 任务完成
queue.task_done()
# 消费者:从队列中获取数据
def customer(id,queue):
while True:
num = queue.get()
if num is None:
break
print(f"消费者{id}从队列中获取了{num}数据")
time.sleep(0.5)
queue.task_done()
if __name__ == '__main__':
#创建一个队列
queue = queue.Queue()
# 创建并启动生产者的线程
for i in range(2):
threading.Thread(target=product,args=(i,queue)).start()
# 创建并启动消费者的线程
for j in range(5):
threading.Thread(target=customer, args=(j, queue)).start()
queue.put(None) # 此处由于生产者一直是死循环所以这一步执行不到所以线程都不会主动结束
"""
在生产者消费者模式中,队列没有元素不会向列表一样返回空值,也就不存在消费者get到None,消费者get会一直阻塞在哪里,直到生产者重新生产出数据,消费者才就会继续获取数据,想要退出除非生产者往队列传输一个None 消费者才会结束线程。
"""
二、简单算法
1.冒泡排序
排序思路:比较两个相邻的下标对应的元素,如果符合条件就交换位置(最值出现在最后位)
# 冒泡排序
# 以升序为例
list1 = [34,45,6,74,45,5,6,7,10,67]
# 外层循环:控制的是比较的次数
for i in range(len(list1) - 1):
# 内层循环:控制的是每一轮比较的次数,同时兼顾参与比较的下标
for j in range(len(list1) - 1 - i):
# 比较:只要符合条件则交换位置
# 如果下标小的元素 > 下标大的元素 ,则交换位置
# 参与比较的下标,j 和j + 1
if list1[j] > list1[j + 1]:
list1[j],list1[j + 1] = list1[j + 1],list1[j]
print(list1)
# 降序为例
list1 = [34,45,6,74,45,5,6,7,10,67]
# 外层循环:控制的是比较的次数
for i in range(len(list1) - 1):
# 内层循环:控制的是每一轮比较的次数,同时兼顾参与比较的下标
for j in range(len(list1) - 1 - i):
# 比较:只要符合条件则交换位置
# 如果下标小的元素 < 下标大的元素 ,则交换位置
# 参与比较的下标,j 和j + 1
if list1[j] < list1[j + 1]:
list1[j],list1[j + 1] = list1[j + 1],list1[j]
print(list1)
2.选择排序
排序思路:固定一个下标,然后拿这个下标对应的值依次和后面的元素进行比较,最值出现在头角标位置上
# 选择排序
# 以升序为例
list1 = [34,45,6,74,45,5,6,7,10,67]
# 外层循环:控制的是比较的轮数
for i in range(len(list1) - 1):
# 内层循环:控制的是每一轮比较的次数,兼顾参与比较的下标
for j in range(i + 1,len(list1)):
# 比较:只要符合条件则交换位置
# 如果下标小的元素 > 下标大的元素 ,则交换位置
# 参与比较的下标,i 和 j
if list1[i] > list1[j]:
list1[i],list1[j] = list1[j],list1[i]
print(list1)
# 以降序为例
list1 = [34,45,6,74,45,5,6,7,10,67]
# 外层循环:控制的是比较的轮数
for i in range(len(list1) - 1):
# 内层循环:控制的是每一轮比较的次数,兼顾参与比较的下标
for j in range(i + 1,len(list1)):
# 比较:只要符合条件则交换位置
# 如果下标小的元素 < 下标大的元素 ,则交换位置
# 参与比较的下标,i 和 j
if list1[i] < list1[j]:
list1[i],list1[j] = list1[j],list1[i]
print(list1)
3.快速排序
# 快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
# (1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
# (2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
# (3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
# (4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
def quick_sort(data):
"""快速排序"""
if len(data) >= 2: # 递归入口及出口
mid = data[len(data)//2] # 选取基准值,也可以选取第一个或最后一个元素
left, right = [], [] # 定义基准值左右两侧的列表
data.remove(mid) # 从原始数组中移除基准值
for num in data:
if num >= mid:
right.append(num)
else:
left.append(num)
return quick_sort(left) + [mid] + quick_sort(right)
else:
return data
# 示例:
array = [2,3,5,7,1,4,6,15,5,2,7,9,10,15,9,17,12]
print(quick_sort(array))
# 输出为[1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 9, 9, 10, 12, 15, 15, 17]
"""
面试官:你了解快排吗?
你:快排基本思想是:从数据集中选取一个基准,然后让数据集的每个元素和基准值比较,小于基准值的元素放入左边分区大于基准值的元素放入右边分区,最后以左右两边分区为新的数据集进行递归分区,直到只剩一个元素。
面试官:快排有什么优点,有什么缺点?
你:分治思想的排序在处理大数据集量时效果比较好,小数据集性能差些。
你:对大规模数据集进行快排,当分区的规模达到一定小时改用插入排序,插入排序在小数据规模时排序性能较好。
"""
4.插入排序
# 插入排序
# 冒泡排序是拿一个元素和无序序列去遍历比较,比较得到整个无序序列中最值,然后放入有序序列,而一旦放入有序序列,就不再碰了
# 冒泡排序的当前元素是无论如何不会触摸有序序列的
# 非常生动的体现了冒泡的情形,泡泡是乱的,最大的泡泡在大大小小杂乱无章的泡泡群中漂浮到最上面而插入排序呢恰恰相反
# 插入排序是拿一个元素和有序的数列去比,从单一元素的假有序一个一个加,拿一个元素进入有序序列,碰到比自己大或小的就即刻坐下,不再继续比较
# 插入排序的当前元素是无论如何不会触摸无序序列的
def insert_sort(tg_list):
for i in range(1, len(tg_list)):
for j in range(i, 0, -1):
if tg_list[j] < tg_list[j - 1]:
tg_list[j - 1], tg_list[j] = tg_list[j], tg_list[j - 1]
else:
break
5.归并排序
# 归并排序
def MergeSort(lists):
if len(lists) <= 1:
return lists
num = int( len(lists) / 2 )
left = MergeSort(lists[:num])
right = MergeSort(lists[num:])
return Merge(left, right)
def Merge(left,right):
r, l=0, 0
result=[]
while l<len(left) and r<len(right):
if left[l] <= right[r]:
result.append(left[l])
l += 1
else:
result.append(right[r])
r += 1
result += list(left[l:])
result += list(right[r:])
return result
print MergeSort([1, 2, 3, 4, 5, 6, 7, 90, 21, 23, 45])