java成长之路 一 数据结构及算法(上)

第一篇技术博客,希望有一个好的开端,每周一篇坚持下去
今天分享的是我结合最近学习的《算法图解》 的心得,以及在工作过程中意识到算法的重要性
目录
1.数据结构:数组、链表、散列表、栈、队列、图
2.算法:二分查找法、快速排序(分而治之)、递归、广度优先搜索、狄克斯特拉算法
3.工具:大O表示法
正文

1.大O表示法

大O表示法是一种用于表示算法速度的方法,一般是指算法最糟糕情况下的速度

算法运行时间是从其增速的角度度量的,大O表示法及表示算法的增速趋势

下面按从快到慢的顺序列出了经常会遇到的5种大O运行时间。
 O(log n),也叫对数时间,这样的算法包括二分查找。
 O(n),也叫线性时间,这样的算法包括简单查找。
 O(n  * log n),例如快速排序——一种速度较快的排序算法。
 O(n 2 ),例如选择排序——一种速度较慢的排序算法。

 O(n!),例如旅行商问题的解决方案——一种非常慢的算法。

2.链表、数组、链表、散列表、栈、队列、图

链表的每个元素都存储了下一个元素的地址,从而使一系列随机的内存地址串在一起


这犹如寻宝游戏。你前往第一个地址,那里有一张纸条写着“下一个元素的地址为123”。因
此,你前往地址123,那里又有一张纸条,写着“下一个元素的地址为847”,以此类推。在链表
中添加元素很容易:只需将其放入内存,并将其地址存储到前一个元素中。
使用链表时,根本就不需要移动元素。这还可避免另一个问题。假设你与五位朋友去看一部
很火的电影。你们六人想坐在一起,但看电影的人较多,没有六个在一起的座位。使用数组时有
时就会遇到这样的情况。假设你要为数组分配10 000个位置,内存中有10 000个位置,但不都靠
在一起。在这种情况下,你将无法为该数组分配内存!链表相当于说“我们分开来坐”,因此,

只要有足够的内存空间,就能为链表分配内存。

组与此不同:你知道其中每个元素的地址。例如,假设有一个数组,它包含五个元素,起

始地址为00,那么元素#5的地址是多少呢


只需执行简单的数学运算就知道:04。需要随机地读取元素时,数组的效率很高,因为可迅
速找到数组的任何元素。在链表中,元素并非靠在一起的,你无法迅速计算出第五个元素的内存
地址,而必须先访问第一个元素以获取第二个元素的地址,再访问第二个元素以获取第三个元素
的地址,以此类推,直到访问第五个元素。

 栈有两种操作:压入和弹出  这种数据结构称为栈。栈是一种简单的数据结构

散列函数“将输入映射到数字”

 散列函数总是将同样的输入映射到相同的索引。

 散列函数将不同的输入映射到不同的索引

 散列函数知道数组有多大,只返回有效的索引。

散列表的填装因子很容易计算

散列表使用数组来存储数据,因此你需要计算数组中被占用的位置数。例如,下述散列表的

填装因子为2/5,即0.4。



图由节点和边组成。一个节点可能与众多节点直接相连,这些节点被称为邻居。

队列的工作原理与现实生活中的队列完全相同。
假设你与朋友一起在公交车站排队,如果你排在他前
面,你将先上车。队列的工作原理与此相同。队列类
似于栈,你不能随机地访问队列中的元素。队列只支

持两种操作:入队和出队。

如果你将两个元素加入队列,先加入的元素将在后加入的元素之前出队。因此,你可使用队
列来表示查找名单!这样,先加入的人将先出队并先被检查。
队列是一种先进先出(First In First Out,FIFO)的数据结构,而栈是一种后进先出(Last In

First Out,LIFO)的数据结构。


3.算法

3.1 选择排序

选择排序是一种灵巧的算法,但其速度不是很快,将数组中的元素选择出最大值然后依次放入新的数组中,不停循环这个操作,以得出最终顺序

需要的总时间为 O(n × n),即O(n 2 )。

3.2 递归

(1) 创建一个要查找的盒子堆。
(2) 从盒子堆取出一个盒子,在里面找。
(3) 如果找到的是盒子,就将其加入盒子堆中,以便以后再查找。
(4) 如果找到钥匙,则大功告成!

(5) 回到第二步。

OR

(1) 检查盒子中的每样东西。
(2) 如果是盒子,就回到第一步。

(3) 如果是钥匙,就大功告成!

编写递归函数时,必须告诉它何时停止递归。正因为如此,每个递归函数都有两部分:基线
条件(base case)和递归条件(recursive case)。递归条件指的是函数调用自己,而基线条件则

指的是函数不再调用自己,从而避免形成无限循环。

3.3 快速排序\分而治之(D&C)

使用D&C解决问题的过程包括两个步骤。
(1) 找出基线条件,这种条件必须尽可能简单。

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

快速排序是一种常用的排序算法,比选择排序快得多。快速排序也使用了D&C。

下面来使用快速排序对数组进行排序。对排序算法来说,最简单的数

组什么样呢?还记得前一节的“提示”吗?就是根本不需要排序的数组。

因此,基线条件为数组为空或只包含一个元素。在这种情况下,只需

原样返回数组——根本就不用排序。

要使用D&C,因此需要将数组分解,直到满足基线条件。下面介绍快速排序的工
作原理。首先,从数组中选择一个元素,这个元素被称为基准值(pivot)。我们暂时将数组的第一个元素用作基准值。

接下来,找出比基准值小的元素以及比基准值大的元素。

这被称为分区(partitioning)。现在你有:
 一个由所有小于基准值的数字组成的子数组;
 基准值;

 一个由所有大于基准值的数组组成的子数组。

如果子数组是有序的,就可以像下面这样合并得到一个有序的数组:左边的数组 + 基准值 +

右边的数组。在这里,就是[10, 15] + [33] + [],结果为有序数组[10, 15, 33]。

这个子数组都只有一个元素,而你知道如何对这些数组进行排序。现在你就知道如何对包含
三个元素的数组进行排序了,步骤如下。
(1) 选择基准值。
(2) 将数组分成两个子数组:小于基准值的元素和大于基准值的元素。

(3) 对这两个子数组进行快速排序。


还有一种名为合并排序(merge sort)的排序算法,其运行时间为O(n log n),比选择排序快

得多!

快速排序的性能高度依赖于你选择的基准值。假设你总是将第一个元素用作基准值,且要处

理的数组是有序的。由于快速排序算法不检查输入数组是否有序,因此它依然尝试对其进行排序。


注意,数组并没有被分成两半,相反,其中一个子数组始终为空,这导致调用栈非常长。现

在假设你总是将中间的元素用作基准值,在这种情况下,调用栈如下。


调用栈短得多!因为你每次都将数组分成两半,所以不需要那么多递归调用。你很快就到达
了基线条件,因此调用栈短得多。
第一个示例展示的是最糟情况,而第二个示例展示的是最佳情况。在最糟情况下,栈长为

O(n),而在最佳情况下,栈长为O(log n)。

 D&C将问题逐步分解。使用D&C处理列表时,基线条件很可能是空数组或只包含一个元
素的数组。
 实现快速排序时,请随机地选择用作基准值的元素。快速排序的平均运行时间为O(n log n)。
 大O表示法中的常量有时候事关重大,这就是快速排序比合并排序快的原因所在。
 比较简单查找和二分查找时,常量几乎无关紧要,因为列表很长时,O(log n)的速度比O(n)

快得多。


3.4 广度优先搜索是一种用于图的查找算法,可帮助回答两类问题。

 第一类问题:从节点A出发,有前往节点B的路径吗?

 第二类问题:从节点A出发,前往节点B的哪条路径最短?

l例子待续

3.5 狄克斯特拉算法

狄克斯特拉算法用于每条边都有关联数字的图,这些数字称为权重(weight)。

带权重的图称为加权图(weighted graph),不带权重的图称为非加权图(unweighted graph)。


 广度优先搜索用于在非加权图中查找最短路径。
 狄克斯特拉算法用于在加权图中查找最短路径。
 仅当权重为正时狄克斯特拉算法才管用

持续更新ing,下班再写
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值