1、算法复杂度分析
1.1、 时间复杂度
- 大O表示法
- 7种时间复杂度——O(1)、O(logn)、O(n)、O(n2)、O(n3)、O(kn)、O(n!)
- 不需要考虑前面的常数系数,反应了一个增长速度
- 判断方法:代码语句的执行次数
- ✨递归(例如求斐波拉契数列)的时间复杂度:画递归状态的递归树-> O(2n)
-
✨主定理(补充,以下内容为记忆中算法课中讲的)
作用:用于递归分治的时间复杂度分析
定义:参考百度
简化记忆:记住公式后,两者相比取较大,相同则乘上logn
需要注意的问题:并不适用于所有情况,关注一下第三种情况需要满足的条件
几个需要记忆的递归复杂度:
样例:🙊二叉树遍历的复杂度可以由该方法求出,也可以换一种思想——每个节点只被访问了一次,所以复杂度为O(n)。这种方法同样适用于图遍历、DFS、BFS复杂度(原理:摊还分析)
1.2、空间复杂度
- 数组长度、递归深度,二者取最大
- ✨递归的空间复杂度=递归树的深度 * 每次占用的变量空间(基本上是1)
- 递归的本质是入栈出栈,栈的深度就是递归的空间复杂度
2、数组、链表、跳表(Java)
(由于我现在用的不是Java,所以仅仅是记录老师所讲的知识点)
2.1、数组
- Array ArrayList
- 数组在内存中开辟连续的地址空间,可以随机访问,时间复杂度为O(1)
- 插入/删除的时间复杂度为O(n),参考ArrayList的源码,数组空间不足时有扩容的操作
- ArrayList源码:<http://developer.classpath.org/doc/java/util/ArrayList-source.html>
2.2、链表
- 基本元素(value,next),有单链表、双向链表、循环链表
- value可以是一个class,可以为泛型,next在Java中为引用
- Java中的LinkedList是双向链表
标准实现代码<https://www.geeksforgeeks.org/implementing-a-linked-list-in-java-using-class/>
示例代码<http://www.cs.cmu.edu/~adamchik/15-121/lectures/Linked%20Lists/code/LinkedList.java>
Java源码分析<http://developer.classpath.org/doc/java/util/LinkedList-source.html> - 优点:插入/删除效率高——O(1)
缺点:访问元素效率低——O(n) - 工程应用:
LRU cache <https://www.jianshu.com/p/b1ab4a170c3c>
Leetcode的题目 <https://leetcode-cn.com/problems/lru-cache/>
2.3、跳表
- SkipList是对LinkedList的优化,在一堆有序元素里查找某个元素时,数组可以使用二分查找的方法,复杂度只有O(logn),但在链表中行不通,所以提出了跳表的概念
- ✨前提条件:元素有序
- 对标的是平衡树(AVL Tree)和二分查找
- 🙊插入/删除/搜索的时间复杂度——O(logn)
- ✨✨两个重要的算法思想:
①一维的数据结构想要加速,经常采用升维的思想,多出的维度用来存储一维数据结构中可能要通过查询多次才能得到的信息
②以空间换时间 -
跳表的基本思想:加入了多级索引
- 以每次跳2为例,查询时,每层最多访问3个节点
- 时间复杂度:O(logn)——深度
- 空间复杂度:O(n)——元素数+索引数(等比数列)
- 工程应用:Redis
<https://redisbook.readthedocs.io/en/latest/internal-datastruct/skiplist.html>
2.4实战例题——移动0
- 做法:双指针法,swap
- 🎁刷题方法
①5-10分钟读题想思路,如果还没有思路的话,看题解
②先整理思路,把想到的解题方法都简单写下来,然后进行复杂度对比
③题目写完之后一定要看题解,在国际站找到这个题,看discussion的前几个,学习别人的代码思路
④刷题不能只刷一遍
✔习题(双指针法)
🎈删除排序数组中的重复项
🎈盛最多水的容器
方法:首尾两个指针i、j进行控制,每次移动height更小的一边,对每次得到的容量取max
原理简述:如果移动 i ,表明以 i 为边界所形成的最大值为刚才记录的值,或者是之前记录的值,所以取max即可
🎁面试经验
👀阿里、百度前端面经分享
👀头条、蚂蚁金服面经分享
👀作为面试官,我是怎么考察候选人的?
2、树、二叉树、二叉搜索树
2.1、树与二叉树
树是二维空间上的数据结构,树与图的区别是没有环路
链表是特殊化的树,树是特殊化的图
树的一般结构:value,*left, *right
为什么需要树这种数据结构:解决日常生活中的问题时,通常是以树的形式进行扩散,例如下棋比赛等,描述了一个决策树空间,按照树的形状进行扩散,叶子节点代表的是终极状态
二叉树的三种遍历:区别在于三句话的顺序不同,复杂度都是O(n)(使用上面提到的两种方法都可以进行判断)
①前序:root,左,右
②中序:左,root,右
③后序:左,右,root
✨树的遍历查找元素最常用的方法:递归
✨树的面试题解法一般都是递归:因为树的构造本质上就是递归
2.2、二叉搜索树
- 排序好的一棵二叉树,方便进行搜索:左子树<root<右子树 💕❗严格的小于号,二叉搜索树中不会有重复的元素
- 中序遍历会将数字按照升序进行排列
- ✨基本操作的复杂度为O(logn)
①查询:小的往左找,大的往右找,所以复杂度是树的深度
复杂度为O(logn)~O(n),当树不平衡的时候,例如在{1,2,3,4}这样的极端情况下,会达到O(n)的复杂度
②插入:查询该元素,算法结束的位置就是元素应该插入的位置
③创建:不断将元素插入到树中O(nlogn)
❓❓不确定,有待深究,之前好像看到过堆的创建复杂度可以是O(n),记忆模糊,还没有仔细看,不知道搜索树是否也有相似的结果
④删除:找到该元素,删除;两种删除方法,一般找到第一个大于x的元素,放在x的位置上
2.3、实战例题——二叉树的中序遍历
- 二叉树的中序遍历
- 使用的方法:递归、迭代
- 递归的本质是出栈入栈,所以迭代的写法就是手动维护一个栈即可,需要注意元素放入顺序的不同,可以使用空指针法或者黑白棋染色法记录栈中的哪个元素已经被访问过,下次再访问的时候可以直接输出该元素的值。
- 题解中还有一个莫里斯方法(待补)
✔习题(树的遍历)
🎈N 叉树的前序遍历 注意一下特殊情况的判断:root为空
🎈二叉树的前序遍历
3、递归
3.1、递归的相关知识
- 递归通过不断调用自己来实现循环
- 递归的概念理解
①递:向下进入到不同的递归层;归:向上返回原来的一层
②使用参数来进行不同层之间的变量传递
③层之间的大环境是互不影响的,主角(参数、全局变量)可以发生变化并将变化带回 -
模板:不会写的时候先把模板列出来,顺着进行思考
①递归终结者
②处理当前层的逻辑
③向下一层传递参数
④清理当前层
- 思维要点
①不要人肉进行递归(穷举),建议直接看函数写
②找到最近最简方法,将其拆解成可重复解决的问题(重复子问题)
③数学归纳法:n=1,2,3成立,k=n成立,n+1也成立 - 个人感觉递归需要知道三点:向下传递什么信息、向上返回什么信息、从状态树来看信息是从哪个节点传上来(下去)的
3.2 实战例题
- 爬楼梯
- 💖括号生成
①先生成所有的括号排序,最后一步进行判断,去除不合法的括号
感想:虽然不是最优的方法,但对我启发很大,在没有思路的情况下,可以套用模板,先列出所有的情况,再去除不合法的
②分析左括号在任何时候都可以添加,只要左括号数量没用完;右括号添加的时候,当前数量应该小于左括号(有一个题目是判断括号使用是否合法,从栈的角度就更好理解这个规律了) - 总结:递归有两种
①从n->n-1->n-2.......->1,类似斐波拉契求解问题,有递有归,需要考虑n项与之前项的关系
②从无到有的构建过程,类似括号的生成,有递无归,需要考虑在一步步的生成过程中需要满足什么限制条件(或者先生成,再剔除)
✔习题(树的遍历)
🎈二叉树的最大深度 可以再做一下最近公共祖先
🎈验证二叉搜索树
二叉搜索树的性质:中序遍历为升序
需要注意的问题:只有一个节点(为负数)的情况、二叉搜索树中不能有重复的元素
✔练习题
1️⃣数组
2️⃣链表
- 环形链表 🎈双指针法(快慢指针)
- 两两交换链表中的节点 迭代很简单,也可以试试递归
3️⃣二叉树
4️⃣递归
emmm,还有两次直播课,直播连线面试,考leetcode的题,瞄了一眼题目解码方法,其他的我都没听,我,打王者去了,我已经是一个尊贵的王者了!不亏~
总结:其实就是他们有一个小两千的完整的课程,为了做推广,搞了这么一个活动,当时我有新人优惠,花了4.5。课程的话还不到三个小时,讲的也都是些基础的东西,虽然没学到什么东西,不过有人带着过过基础,感觉也不亏。最大的收获可能就是,我现在想坚持刷算法题了吧,因为还有别的事情,暂时定个小目标,每天一题吧。
有两个关于刷题的链接,还没看,分享给大家~
https://github.com/youngyangyang04/leetcode-master
https://www.1point3acres.com/bbs/thread-130162-1-1.html