1、二分查找
是什么:二分查找是一种算法,输入是一个有序的元素列表(必须是有序的),查找元素在列表中的位置。
二分查找,每次取中间的那个元素,根据大小将余下的数字排除一半。
运行时间:
2、选择排序
(1)链表
链表中的元素可存储在内存的任何地方,链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在了一起。在链表中添加元素很容易:只需要将其放入内存中,并将其地址存储到前一个元素中。使用链表时根本不需要移动元素。
(2)数组
数组中知道其中每个元素的地址。假设有一个数组,含有5个元素,起始地址为00,那么元素#5的地址通过计算可以得知为04。
在随机读取元素时,数组的效率很高,因为可以迅速找到数组的任何元素。在链表中,元素并非靠在一起,无法迅速的计算出第5个元素的内存地址,必须先访问第一个元素,以获取第二个元素的地址,再访问第二个元素以获取第三个元素的地址,以此类推直到访问第5个元素。
【链表存储的时候比较方便,数组读取的时候比较方便】
A. 在中间插入元素的时候,使用链表时,插入元素很简单,只需要修改它前面的那个元素指向的地址。因此,当需要在中间插入元素时,链表是更好的选择。
B. 在删除元素的时候,使用链表,只需要修改前一个元素指向的地址即可。使用数组时,删除元素后,必须将后面的元素都向前移。因此,当需要删除元素时,链表是更好的选择。
相对而言,数组的使用情况比较多一些,因为它支持两种访问方式:随机访问和顺序访问。顺序访问意味着从第一个元素开始逐个的读取元素,链表只能顺序访问。
是什么:对一个列表进行排序(按着一定的规则,从大到小或者从小到大),按着从大到小的顺序,首先找出最大的那个元素,然后再在剩余的里面找出第二大的那个元素,直到最后所有元素都被排列完为止。
运行时间:
3、快速排序
探索分而治之(divide and conquer,D&C)——一种著名的递归式问题解决方法。
使用D&C解决问题的过程包括两个步骤:
(1)找出基线条件,这种条件必须尽可能简单。
(2)不断将问题分解(或者说是缩小规模),直到符合基线条件。
是什么:快速排序是一种常用的排序算法,比选择排序快得多。
步骤:选择基准值;将数组分成两个子数组:小于基准值的元素和大于基准值的元素;对这两个子数组进行快速排序。
运行时间:
归纳证明,归纳证明是一种证明算法行之有效的方式,它分两步:基线条件和归纳条件。
4、散列表
散列表的特点:
- 散列表总是将同样的输入映射到相同的索引。
- 散列表数将不同的输入映射到不同的索引。
- 散列函数知道数组有多大,只返回有效的索引。
- 散列表是无序的。
Python中的dict就是一个散列表的实现。
#检查,某人是否已经进行过投票
voted = dict()
def check_voter(name):
if voted.get(name):
print("kick them out!")
else:
voted[name] = True
print("let them vote!")
while True:
name = input("Please input your name:")
check_voter(name)
在查询过程中,如果将数据存储在列表中,函数的速度终将变得非常慢,因为它必须使用简单查找搜索整个列表,但是如果将它存储在散列表中,散列表能够迅速进行查找。使用散列表进行检验是否重复,速度非常快。
(1)冲突
当在散列表中,将不同的键映射到了数组相同的位置时,就会发生了冲突。
处理冲突的方式:如果两个键映射到了同一个位置,就在这个位置存储一个链表。
如果某个位置的散列表很长,就和将所有的元素存储到一个链表中一样糟糕,散列表的速度会很慢。
两个教训:
- 散列函数很重要。前边的散列函数将所有的键都映射到了一个位置,而最理想的情况是,散列函数将键均匀地映射到散列表的不同位置。
- 如果散列表存储的链表很长,散列表的速度将急剧下降。然而,如果使用的散列函数很好,这些链表就不会很长。
(2)性能
在平均情况下,散列表执行各种操作的时间都为 。被称为常量时间。在最糟糕情况下,散列表所有操作的运行时间都为——线性时间,这真的很慢。
在平均情况下,散列表的查找(获取给定索引处的值)速度与数组一样快,而插入和删除速度与链表一样快,因此它兼具二者的优点。在最糟糕情况下,散列表的各种操作的速度都很慢。因此,在使用散列表时,要避免冲突,需要有:较低的填装因子;良好的散列函数。
散列表的查找、插入和删除速度都非常快。
散列表适合用于模拟映射关系。
散列表可用于缓存数组(例如,在Web服务器上)。
散列表非常适合用于防止重复。
运行时间:
5、广度优先搜素
广度优先算法是一种图算法,能够让你找出两样东西之间最短的距离,最短距离的含义有很多,使用广度优先搜索可以:
- 编写国际跳棋AI,计算最少走多少步可获胜;
- 编写拼写检查器,计算最少编辑多少个地方就可将错拼的单词改成正确的单词,如将READED改成READER需要编辑多少个地方;
- 根据你的人际关系网络找到关系最近的医生。
解决最短路径问题的算法成为广度优先搜素。
队列
对列的工作原理类似于栈,你不能随机的访问队列中的元素,队列只支持两种操作:入队和出队。
如果将两个元素加入队列,先加入的元素将在后加入的元素之前出队。队列是一种先进先出(First In First Out,FIFO)的数据结构;栈是一种后进先出(Last In First Out,LIFO)的数据结构。
6、迪克斯特拉算法
迪克斯特拉算法的步骤:
- 找出“最便宜”的节点,即可在最短时间内到达的节点。
- 对于该节点的邻居,检查是否有前往它们的更短路径,如果有更新该节点的邻居的开销。
- 重复这个过程,直到对图中的每个节点都这样做了。
- 计算最终路径。
要计算非加权图的最短路径,可以使用广度优先搜素;要计算加权图中的最短路径,可以使用迪克斯特拉算法。
迪克斯特拉算法只适用于有向无环图。
7、贪婪算法
贪婪算法就是每步都采取最优的做法。(每步都选择局部最优解,最终得到的就是全局最优解)
启示:(个人感觉很有哲理)在有些情况下,完美是优秀的敌人。有时候,你只需找到一个能够大致解决问题的算法,此时贪婪算法正好可派上用场,因为它们实现起来很容易,得到的结果又与正确结果相当接近。
集合
对集合执行的一些操作:
- 并集意味着将结合合并。
- 交集意味着找出两个集合中都有的元素。
- 差集意味着将从一个集合中剔除出现在另一个集合中的元素。
8、K最近邻算法
K最近邻算法(k-nearest neighbours,knn)根据它最近的邻居判断它是什么。
计算两点之间的距离,使用毕达哥拉斯公式:
引用
部分定义和方法来自《算法图解》人民邮电出版社。