算法基础概述

1、二分查找

是什么:二分查找是一种算法,输入是一个有序的元素列表(必须是有序的),查找元素在列表中的位置。

二分查找,每次取中间的那个元素,根据大小将余下的数字排除一半。

运行时间:O(\log ^{}n)

2、选择排序

(1)链表

链表中的元素可存储在内存的任何地方,链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在了一起。在链表中添加元素很容易:只需要将其放入内存中,并将其地址存储到前一个元素中。使用链表时根本不需要移动元素。

(2)数组

数组中知道其中每个元素的地址。假设有一个数组,含有5个元素,起始地址为00,那么元素#5的地址通过计算可以得知为04。

在随机读取元素时,数组的效率很高,因为可以迅速找到数组的任何元素。在链表中,元素并非靠在一起,无法迅速的计算出第5个元素的内存地址,必须先访问第一个元素,以获取第二个元素的地址,再访问第二个元素以获取第三个元素的地址,以此类推直到访问第5个元素。

【链表存储的时候比较方便,数组读取的时候比较方便】

A. 在中间插入元素的时候,使用链表时,插入元素很简单,只需要修改它前面的那个元素指向的地址。因此,当需要在中间插入元素时,链表是更好的选择。

B. 在删除元素的时候,使用链表,只需要修改前一个元素指向的地址即可。使用数组时,删除元素后,必须将后面的元素都向前移。因此,当需要删除元素时,链表是更好的选择。

相对而言,数组的使用情况比较多一些,因为它支持两种访问方式:随机访问和顺序访问。顺序访问意味着从第一个元素开始逐个的读取元素,链表只能顺序访问。

是什么:对一个列表进行排序(按着一定的规则,从大到小或者从小到大),按着从大到小的顺序,首先找出最大的那个元素,然后再在剩余的里面找出第二大的那个元素,直到最后所有元素都被排列完为止。

运行时间:O(n^{}2)

3、快速排序

探索分而治之(divide and conquer,D&C)——一种著名的递归式问题解决方法。

使用D&C解决问题的过程包括两个步骤:

(1)找出基线条件,这种条件必须尽可能简单。

(2)不断将问题分解(或者说是缩小规模),直到符合基线条件。

是什么:快速排序是一种常用的排序算法,比选择排序快得多。

步骤:选择基准值;将数组分成两个子数组:小于基准值的元素和大于基准值的元素;对这两个子数组进行快速排序。

运行时间:O\left ( n\log ^{}n \right )

归纳证明,归纳证明是一种证明算法行之有效的方式,它分两步:基线条件和归纳条件。

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)性能

在平均情况下,散列表执行各种操作的时间都为 O\left ( 1 \right )O\left ( 1 \right )被称为常量时间。在最糟糕情况下,散列表所有操作的运行时间都为O\left ( n \right )——线性时间,这真的很慢。

在平均情况下,散列表的查找(获取给定索引处的值)速度与数组一样快,而插入和删除速度与链表一样快,因此它兼具二者的优点。在最糟糕情况下,散列表的各种操作的速度都很慢。因此,在使用散列表时,要避免冲突,需要有:较低的填装因子;良好的散列函数。

散列表的查找、插入和删除速度都非常快。

散列表适合用于模拟映射关系。

散列表可用于缓存数组(例如,在Web服务器上)。

散列表非常适合用于防止重复。

运行时间:O\left ( 1 \right )

5、广度优先搜素

广度优先算法是一种图算法,能够让你找出两样东西之间最短的距离,最短距离的含义有很多,使用广度优先搜索可以:

  • 编写国际跳棋AI,计算最少走多少步可获胜;
  • 编写拼写检查器,计算最少编辑多少个地方就可将错拼的单词改成正确的单词,如将READED改成READER需要编辑多少个地方;
  • 根据你的人际关系网络找到关系最近的医生。

解决最短路径问题的算法成为广度优先搜素。

队列

对列的工作原理类似于栈,你不能随机的访问队列中的元素,队列只支持两种操作:入队和出队。

如果将两个元素加入队列,先加入的元素将在后加入的元素之前出队。队列是一种先进先出(First In First Out,FIFO)的数据结构;栈是一种后进先出(Last In First Out,LIFO)的数据结构。

6、迪克斯特拉算法

迪克斯特拉算法的步骤:

  • 找出“最便宜”的节点,即可在最短时间内到达的节点。
  • 对于该节点的邻居,检查是否有前往它们的更短路径,如果有更新该节点的邻居的开销。
  • 重复这个过程,直到对图中的每个节点都这样做了。
  • 计算最终路径。

要计算非加权图的最短路径,可以使用广度优先搜素;要计算加权图中的最短路径,可以使用迪克斯特拉算法。

迪克斯特拉算法只适用于有向无环图。

7、贪婪算法

贪婪算法就是每步都采取最优的做法。(每步都选择局部最优解,最终得到的就是全局最优解)

启示:(个人感觉很有哲理)在有些情况下,完美是优秀的敌人。有时候,你只需找到一个能够大致解决问题的算法,此时贪婪算法正好可派上用场,因为它们实现起来很容易,得到的结果又与正确结果相当接近。

集合

对集合执行的一些操作:

  • 并集意味着将结合合并。
  • 交集意味着找出两个集合中都有的元素。
  • 差集意味着将从一个集合中剔除出现在另一个集合中的元素。

 8、K最近邻算法

K最近邻算法(k-nearest neighbours,knn)根据它最近的邻居判断它是什么。

计算两点之间的距离,使用毕达哥拉斯公式:

\sqrt{\left ( x_{}1 -x_{}2\right )^{}2+ ( y_{}1 -y_{}2\right))^{}2}

 

引用 

部分定义和方法来自《算法图解》人民邮电出版社。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值