算法和设计模式

常用算法学习

1.算法:解题方案的准确而完整的描述,是一系列解决问题的清晰指令。
①有穷性(Finiteness):算法的有穷性是指算法必须能在执行有限个步骤之后终止;

②确切性(Definiteness):算法的每一步骤必须有确切的定义;

③输入项(Input):一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输 入是指算法本身定出了初始条件;

④输出项(Output):一个算法有一个或多个输出,以反映对输入数据加工后的结果。没 有输出的算法是毫无意义的;

⑤可行性(Effectiveness):算法中执行的任何计算步骤都是可以被分解为基本的可执行 的操作步,即每个计算步都可以在有限时间内完成(也称之为有效性);

⑥高效性(High efficiency):执行速度快,占用资源少;

⑦健壮性(Robustness):对数据响应正确。

时间复杂度概念:一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。


2.常用排序
①冒泡排序(Bubble Sort):这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端,故名。
if data_set[i] > data_set[i+1]: #每次将i与i+1比较,每次大循环将最大的放最后。小的逐渐放在最前
tmp = data_set[i]
data_set[i] = data_set[i+1]
data_set[i+1] = tmp

②选择排序:在不断缩小的范围内选出最小值放在前面。
smallest_num_index = 0 #初始列表最小值,默认为第一个
for j in range(len(data_set)): #每次确定一个最小数,从头到尾确定
for i in range(j,len(data_set)): #从已确定数后一位开始
if data_set[i] < data_set[smallest_num_index]: #当前值 比之前选出来的最小值 还要小,那就把它换成最小值
smallest_num_index = i #记录最小值的位置,然后互换给当前位置J,每次即可确定一个最小值
loop_count +=1

③插入排序(Insertion Sort):将列表分为2部分,左边为排序好的部分,右边为未排序的部分,循环整个列表,
每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。
一开始,已经排序的元素序列为空。

for i in range(len(data_set)):
while i > 0 and data_set[i] < data_set[i-1]: #如果后一个小,就放前一位,类似于插牌
tmp = data_set[i]
data_set[i] = data_set[i-1]
data_set[i-1] = tmp
i -= 1

④快速排序(quick sort):首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,
所有比它大的数都放到它后面。

不稳定:多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序可能变化



⑤二叉树:边的上端节点称为父节点,下端称为子节点。

⑥堆排序:堆分为最大堆和最小堆,其实就是完全二叉树。最大堆要求节点的元素都要大于其孩子,最小堆要求节点元素都小于其左右孩子,
两者对左右孩子的大小关系不做任何要求,
堆排序就是把堆顶的最大数取出再排序,再取出

⑦希尔排序(shell sort):插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。
不断缩小增量的间隔组成的元素进行大小排序,最后一次直接插入排序。

2018/03/15 周四

常用设计模式学习

设计模式:代码设计经验的总结,创建型、结构型和行为型3大类

①创建型模式:对对象的创建进行研究。
简单工厂模式(Simple Factory);

工厂方法模式(Factory Method);

抽象工厂模式(Abstract Factory);

创建者模式(Builder);

原型模式(Prototype);

单例模式(Singleton)。
②结构型模式:对象的组成以及对象之间的依赖关系。
外观模式(Facade);

适配器模式(Adapter);

代理模式(Proxy);

装饰模式(Decorator);

桥模式(Bridge);

组合模式(Composite);

享元模式(Flyweight)
③行为型模式:在对象的结构和对象的创建问题都解决了之后,就剩下对象的行为问题了。
模板方法模式(Template Method);

观察者模式(Observer);

状态模式(State);

策略模式(Strategy);

职责链模式(Chain of Responsibility);

命令模式(Command);

访问者模式(Visitor);

调停者模式(Mediator);

备忘录模式(Memento);

迭代器模式(Iterator);

解释器模式(Interpreter)。

2.设计模式的六大原则

①开闭原则(Open Close Principle)

对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码。
所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类。

②里氏代换原则(Liskov Substitution Principle)

面向对象设计的基本原则之一。即任何基类可以出现的地方,子类一定可以出现。
LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。
里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。
而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:是对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,
从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。
所以上文中多次出现:降低依赖,降低耦合。

5、迪米特法则(最少知道原则)(Demeter Principle)

一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。


2018/03/16 周五

设计三大类模式

2018/03/17 周六

一. 创建型模式
1.工厂模式
意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
①简单工厂模式:一个接口类负责决定创建哪个ShapeFactory的子类,即一个工厂。
总结:创建好多个产品的类后,写一个接口类对用哪个产品的类进行逻辑判断。

②工厂方法模式:对每一个产品都有相应的工厂,客户端判断使用哪个接口(工厂)。
把简单工厂抽象为一个Factory类(可以是抽象类和接口),制定了一些规范,具体的生产由其子类工厂完成。
总结:写一个接口类,然后再写继承的子类,子类里的方法对应不同的产品类

③抽象工厂模式:工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则需要面对多个产品等级结构。
两个重要的概念:产品族和产品等级。
特点:使用同一个工厂等级结构来对付这些相同或者极为相似的产品等级结构。即一个工厂创建一族产品。
功能:为一系列相关对象或相互依赖的对象创建一个接口。切换产品族的时候,切换工厂即可。
客户端使用抽象工厂来创建需要的对象,而根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。
总结:同一等级结构的产品有一个总类,对应不同族写不同的子类继承。写好多个等级结构后,再写一个总工厂类,
不同族继承不同的子工厂类,子工厂类里创建该族的一族产品。
最后写一个抽象工厂类,执行创建的动作。输入不同族产品即可创建不同族。

2.建造者模式
意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
基本思想:某类产品的构建由很多复杂组件组成;(类比一族产品)
这些组件中的某些细节不同,构建出的产品表象会略有不同;(类比不同族的工厂)
通过一个指挥者按照产品的创建步骤来一步步执行产品的创建;(类比工厂的创建过程)
当需要创建不同的产品时,只需要派生一个具体的建造者,重写相应的组件构建方法即可。(切换工厂)
总结:写一个基类,子类继承时细节不同,再写一个指挥者的类,执行创建。

3.单例模式
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
总结:只有唯一的一个实例,后者会覆盖前面的。通过cls._instance绑定,唯一化实例

二.结构型模式
1.适配器模式:
意图:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
应用场景:希望复用一些现存的类,但是接口又与复用环境要求不一致。
总结:新写一个类,类的方法指向现存的类的方法,即可转换。

2.桥接模式:
概述:某些类型由于自身的逻辑,它具有两个或多个维度的变化,
意图:将抽象部分与实现部分分离,使它们都可以独立的变化。
效果及实现要点:
①Bridge模式使用“对象间的组合关系”解耦了抽象和实现之间固有的绑定关系,使得抽象和实现可以沿着各自的维度来变化。
②所谓抽象和实现沿着各自维度的变化,即“子类化”它们,得到各个子类之后,便可以任意它们,从而获得不同路上的不同汽车。
③Bridge模式有时候类似于多继承方案,但是多继承方案往往违背了类的单一职责原则(即一个类只有一个变化的原因),复用性比较差。Bridge模式是比多继承方案更好的解决方法。
④Bridge模式的应用一般在“两个非常强的变化维度”。注意的是,有时候即使有两个变化的维度,但是某个方向的变化维度并不剧烈——换言之两个变化不会导致纵横交错的结果,并不一定要使用Bridge模式。
总结:自由组配不同对象与不同实现,每种组合都有可能实现。这点与创建型模式——抽象工厂不同,抽象工厂只能创建一族的产品。

3.组合模式
意图:将对象组合成树形结构以表示“部分-整体”的层次结构。用户对单个对象和组合对象的使用具有一致性。
用途:比如总店和分店,在总店购买时,积分也一样累加在分店卡上。
总结:写总店的对象的类时,写一个储存列表的方法,在实例时把分店加到里面,在写刷卡方法时,循环列表取出刷卡。
总店刷卡时,即可累加积分到分店上。从而单个对象(总店)和组合对象(总店和分店)使用(刷卡积分)一致。

4.外观模式
意图:定义了一个高层接口,为子系统中的一组接口提供一个一致的界面,这个接口使得这一子系统更加容易使用。
概述:Facade 可以提供一个简单的缺省视图(用于接口),需要定制(改写继承)则越过facade层。
不同层之间建立外观模式,定义各自的facade接口,作为子系统中每层的入口点,它们仅通过facade进行通讯。
总结:为多层次的子类的不同层各写一个方法,归在一个总类下。

5.享元模式
意图:运用共享技术有效地支持大量细粒度的对象。
概述:
①抽象享元角色(Flyweight):
此角色是所有的具体享元类的超类,为这些类规定出需要实现的公共接口或抽象类。
那些需要外部状态(External State)的操作可以通过方法的参数传入。
抽象享元的接口使得享元变得可能,但是并不强制子类实行共享,因此并非所有的享元对象都是可以共享的。

②具体享元(ConcreteFlyweight)角色:
实现抽象享元角色所规定的接口。如果有内部状态的话,必须负责为内部状态提供存储空间。
享元对象的内部状态必须与对象所处的周围环境无关,从而使得享元对象可以在系统内共享。
有时候具体享元角色又叫做单纯具体享元角色,因为复合享元角色是由单纯具体享元角色通过复合而成的。

③复合享元(UnsharableFlyweight)角色:
复合享元角色所代表的对象是不可以共享的,但是一个复合享元对象可以分解成为多个本身是单纯享元对象的组合。
复合享元角色又称做不可共享的享元对象。这个角色一般很少使用。

④享元工厂(FlyweightFactoiy)角色:
本角色负责创建和管理享元角色。本角色必须保证享元对象可以被系统适当地共享。
当一个客户端对象请求一个享元对象的时候,享元工厂角色需要检查系统中是否已经有一个符合要求的享元对象,
如果已经有了,享元工厂角色就应当提供这个已有的享元对象;如果系统中没有一个适当的享元对象的话,
享元工厂角色就应当创建一个新的合适的享元对象。

⑤客户端(Client)角色:
本角色还需要自行存储所有享元对象的外部状态。

⑥内部状态与外部状态:
在享元对象内部并且不会随着环境改变而改变的共享部分,可以称之为享元对象的内部状态,
反之随着环境改变而改变的,不可共享的状态称之为外部状态。
总结:写一个抽象基类(抽象享元),再继承子类(具体享元),实例化子类时(享元工厂)处理一下(删除相同实例的外部状态)。
那么可以用相对较少的共享对象取代很多组对象。

6.代理模式
意图:为其他对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题。如买火车票,路远不去火车站买,去代售点买即可。
总结:写一个代理类,其方法指向被代理的类的方法。
缺点:增加了一层代理,请求处理时可能会速度变慢。
使用场景:
①远程代理。
②虚拟代理。
③Copy-on-Write 代理。
④保护(Protect or Access)代理。
⑤Cache代理。
⑥防火墙(Firewall)代理。
⑦同步化(Synchronization)代理。
⑧智能引用(Smart Reference)代理。

三. 行为型模式

1.模板方法模式
概述:一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现。
意图:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中(重写)。行为(次数,数量)由父类控制,子类实现。
何时使用:一些方法通用,却在每一个子类都重新写了这一方法。
总结:在父类中写好全部方法(次序、数量),子类继承时重写部分方法,通过覆盖某些步骤,
使得相同的算法框架可以有不同的执行结果。

2.责任链模式
意图:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
总结:写一个基类,基类中要有从一个对象转下一个对象的方法,def successor(self,successor):
self._successor = successor
该方法的功能就是把另一个对象通过successor()传递进来(进这个对象类中)。
在需要传下去时,写else self._successor.handle(request)即可。
最后一个对象不需要传递。
实例化时要把链子通过successor串起来。

3.观察者模式 又名发布-订阅(Publish/Subscribe)模式
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
总结:写一个基类,包含基本的添加、解除、通知等需要的方法,其中通知方法遍历使用被观察者类的被通知方法;
然后写一个继承的观察者类,里面包括传入信息,执行通知方法的方法;
再写几个被观察者类,有被通知方法;
最后实例时,添加被观察者到观察者的列表中,输入msg即可触发通知方法。

4.策略模式
意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换(即可随时更换)。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
总结:原本要更换算法的话,需要重新实例,要想同一个实例,需要在类里面定义一个重新指向算法的方法。
先写好几个算法,写接口时,除了有执行算法的方法,还需有重新指向另一个算法的方法,
更换时输入算法类的名字即可更换。

posted @ 2018-07-28 15:49  心平万物顺  阅读(1121)  评论(0编辑  收藏  举报