数据结构与算法之美学习笔记(1~4章)

第一章.为什么要学习数据结构和算法

 


 

第二章.数据结构和算法的定义,学习的方法

1.数据结构和算法的概念

广义:

数据结构指的是一组数据的存储结构,算法指的是操作数据的一组方法.

例如:图书馆里一般会将书分类放,然后有相应的编号规则.这就是数据结构,我们具体来查找这本书的方法就是算法,比如我们可以从头到尾一本一本的找也可以按照分类,编号去找.

狭义:

数据结构就是指的一些著名的数据结构,比如,数组,链表,栈,堆,针对每种数据结构服务的方法比如二分查找这些就是算法.

两者是相辅相成的.脱离各自都会没有意义.

2.怎么学习

(1).复杂度分析这个必须非常熟练

(2).20 个最常用的、最基础数据结构与算法.

不管是应付面试还是工作需要,只要集中精力逐一攻克这 20 个知识点就足够了。

这里面有 10 个数据结构:数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie 树;10 个算法:递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法。

(3).不要只是死记硬背

要学习它的“来历”“自身的特点”“适合解决的问题”以及“实际的应用场景”

(4).学习技巧

1. 边学边练,适度刷题,可以“适度”刷题,但一定不要浪费太多时间在刷题上。我们学习的目的还是掌握,然后应用

2. 多问、多思考、多互动

3. 打怪升级学习法,学习的过程中,我们碰到最大的问题就是,坚持不下来,我们在枯燥的学习过程中,也可以给自己设立一个切实可行的目标就像打怪升级一样。

4.知识需要沉淀,不要想试图一下子掌握所有,如果哪个知识点没有怎么学懂,不要着急,这是正常的。可以先沉淀一下,过几天再重新学一遍。所谓,书读百遍其义自见,我觉得是很有道理的!


 

第三章.复杂度分析(上):如何分析、统计算法的执行效率和资源消耗

1.大 O 时间复杂度表示法

所有代码的执行时间 T(n) 与每行代码的执行次数 n 成正比

T(n) 表示代码执行的时间;n 表示数据规模的大小;f(n) 表示每行代码执行的次数总和。因为这是一个公式,所以用 f(n) 来表示。公式中的 O,表示代码的执行时间 T(n) 与 f(n) 表达式成正比。

公式中的低阶、常量、系数三部分并不左右增长趋势,所以都可以忽略。我们只需要记录一个最大量级就可以了

例如:T(n) = O(2n^2+n+100)  用大O表达法表示T(n) = O(n);

我们直接可以忽略低阶的n 常数的100 和2n^2中的系数2

大 O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度

2.时间复杂度分析

1. 只关注循环执行次数最多的一段代码

2. 加法法则:总复杂度等于量级最大的那段代码的复杂度

如果 T1(n)=O(f(n)),T2(n)=O(g(n));那么 T(n)=T1(n)+T2(n)=max(O(f(n)), O(g(n))) =O(max(f(n), g(n))).

3. 乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

假设 T1(n) = O(n),T2(n) = O(n2),则 T1(n) * T2(n) = O(n3)。我们可以把乘法法则看成是嵌套循环

3.常见时间复杂度

多项式量级非多项式量级

其中,非多项式量级只有两个:O(2^n) 和 O(n!)。

数据规模 n 越来越大时,非多项式量级算法的执行时间会急剧增加,求解问题的执行时间会无限增长。所以,非多项式时间复杂度的算法其实是非常低效的算法

1. O(1)

一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1)

2. O(logn)、O(nlogn)

i=1;
 while (i <= n)  {
   i = i * 2;
 }

变量 i 的值从 1 开始取,每循环一次就乘以 2 。当大于 n 时,循环结束。

2^x=n    所以执行次数x=log2n  比如n=4的时候 x=2  执行了2次

所以T(n) = O(log2n)

 i=1;
 while (i <= n)  {
   i = i * 3;
 }

上面代码换成3  得出结果就是log(3)n=log(3)2*log(2)n 

计算过程(好久没用都忘记了....)

忽略掉常数,结果同样为log2n

至于O(nlog2n) 就是外层多了一个嵌套循环,执行n次 这里套用乘法规则嵌套代码的复杂度等于嵌套内外代码复杂度的乘积


 

第四章.复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度

// n 表示数组 array 的长度
int find(int[] array, int n, int x) {
  int i = 0;
  int pos = -1;
  for (; i < n; ++i) {
    if (array[i] == x) {
       pos = i;
       break;
    }
  }
  return pos;
}

最好情况时间复杂度就是,在最理想的情况下,执行这段代码的时间复杂度

在最理想的情况下,要查找的变量 x 正好是数组的第一个元素,这时候最好情况就是O(1)

最坏情况时间复杂度就是,在最糟糕的情况下,执行这段代码的时间复杂度

如果数组中没有要查找的变量 x,我们需要把整个数组都遍历一遍才行这时候最坏情况就是O(n)

平均情况时间复杂度

有 n+1 种情况:在数组的 0~n-1 位置中不在数组中。我们把每种情况下,查找需要遍历的元素个数累加起来,然后再除以 n+1,就可以得到需要遍历的元素个数的平均值,即:

我们知道,时间复杂度的大 O 标记法中,可以省略掉系数、低阶、常量,所以,咱们把刚刚这个公式简化之后,得到的平均时间复杂度就是 O(n)。

但是计算过程稍微有点儿问题。究竟是什么问题呢?我们刚讲的这 n+1 种情况,出现的概率并不是一样的。

我们知道,要查找的变量 x,要么在数组里,要么就不在数组里。这两种情况对应的概率统计起来很麻烦,为了方便你理解,我们假设在数组中与不在数组中的概率都为 1/2。另外,要查找的数据出现在 0~n-1 这 n 个位置的概率也是一样的,为 1/n。所以,根据概率乘法法则,要查找的数据出现在 0~n-1 中任意位置的概率就是 1/(2n)。

因此,前面的推导过程中存在的最大问题就是,没有将各种情况发生的概率考虑进去。如果我们把每种情况发生的概率也考虑进去,那平均时间复杂度的计算过程就变成了这样:

这个值就是概率论中的加权平均值,也叫作期望值,所以平均时间复杂度的全称应该叫加权平均时间复杂度或者期望时间复杂度

引入概率之后,前面那段代码的加权平均值为 (3n+1)/4。用大 O 表示法来表示,去掉系数和常量,这段代码的加权平均时间复杂度仍然是 O(n)。

实际上,在大多数情况下,我们并不需要区分最好、最坏、平均情况时间复杂度三种情况。很多时候,我们使用一个复杂度就可以满足需求了。只有同一块代码在不同的情况下,时间复杂度有量级的差距,我们才会使用这三种复杂度表示法来区分。

均摊时间复杂度(摊还分析法)

对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间存在前后连贯的时序关系,这个时候,我们就可以将这一组操作放在一块儿分析,看是否能将较高时间复杂度那次操作的耗时,平摊到其他那些时间复杂度比较低的操作上。而且,在能够应用均摊时间复杂度分析的场合,一般均摊时间复杂度就等于最好情况时间复杂度。

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值