学习日记4.22-4.26

2022.4.22

1、链表刷题:

2、哈希表刷题:

理论知识:哈希表理论基础
总结:
  • 一般来说哈希表都是用来快速判断一个元素是否出现集合里
  • 对于哈希表,要知道哈希函数哈希碰撞在哈希表中的作用.
    哈希函数是把传入的key映射到符号表的索引上。
    哈希碰撞处理有多个key映射到相同索引上时的情景,处理碰撞的普遍方式是拉链法和线性探测法。
  • 三种哈希结构:
    • 数组
    • HashSet
    • HashMap
  • 什么时候使用数组做哈希表? 数组就是简单的哈希表,但是数组的大小是受限的!题目包含小写字母,那么使用数组来做哈希最合适不过。
  • 什么时候使用HashSet做哈希表? 如果数组空间够大,但哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。数组的大小是有限的,受到系统栈空间(不是数据结构的栈)的限制。那就考虑HashSet。
  • 什么时候使用HashMap做哈希表? 同时存放两个元素,且两个元素有对应关系。

3、二叉树刷题:

二叉树的递归遍历三要素:
1、确定递归函数的参数和返回值
2、确定终止条件
3、确定单层递归的逻辑

理论知识: 二叉树基础篇
  • 二叉树(没有值)
    • 满二叉树
    • 完全二叉树
  • 二叉搜索树(有序树)
    • 平衡二叉搜索树(它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。)
  • 二叉树的存储方式(一般用链式存储)
    • 链式存储
    • 顺序存储
  • 二叉树的遍历方式
    • 深度优先遍历(先往深走,遇到叶子节点再往回走)
      • 前序遍历(递归法,迭代法)
      • 中序遍历(递归法,迭代法)
      • 后序遍历(递归法,迭代法)
        在这里插入图片描述
    • 广度优先遍历 (一层一层的去遍历)
      • 层次遍历(迭代法)
  • 节点定义:(TreeNode)
public class TreeNode {
	int val;
	TreeNode left;
	TreeNode right;
	TreeNode() {}
	TreeNode(int val) { this.val = val; }
	TreeNode(int val, TreeNode left, TreeNode right) {
		this.val = val;
		this.left = left;
		this.right = right;
   }
}

2022.4.24 星期天

1、字符串刷题:

344. 反转字符串
541. 反转字符串 II
1、法1:用StringBuffer方法 (可以直接用类中的reverse方法)
2、法2:用数组方法 (将字符串转char数组、s.toCharArray())
剑指 Offer 05. 替换空格
1、法1:使用辅助数组(对于很多数组填充问题)
2、法2:扩容,大小为带填充后的大小,然后在从后向前进行操作。(good!)
总结:要么变字符串数组,要么用StringBuilder。

2022.4.25 星期一

1、字符串刷题:

151. 颠倒字符串中的单词
剑指 Offer 58 - II. 左旋转字符串
28. 实现 strStr()(不会代码,只看懂了理论,KMP算法)

KMP算法:
  • 要在文本串:aabaabaafa 中查找是否出现过一个模式串:aabaaf。
  • KMP有什么用?
    KMP主要应用在字符串匹配上。
    KMP的主要思想是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免从头再去做匹配了。
  • next数组里的数字表示的是什么,如何计算?有什么作用?
    next数组就是一个前缀表,记录下标i之前(包括i)的字符串中,相同前缀后缀的最大长度。
    前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。
  • 前缀表与next数组的关系?
    这并不涉及到KMP的原理,而是具体实现,next数组即可以就是前缀表,也可以是前缀表统一减一(右移一位,初始位置为-1)。

如何得到前缀表?前缀和后缀相等的数量。
next数组(prefix数组)

总结:
  • 字符串方法很多,可以转数组toCharArray,可以转StringBuilder(StringBuffer),可以用自带很多函数substring、split、trim、charAt等等。
  • 双指针法是字符串处理的常客。
  • KMP算法是字符串查找最重要的算法。(难)

2022.4.26 星期二/5.5

1、栈与队列刷题:

232. 用栈实现队列(出栈和返回头元素代码很像,可以抽出来变成一个函数或工具类,记得代码复用!不要复制粘贴!)
225. 用队列实现栈(可以使用queue(linkedlist)、deque(arraydeque))
方法1:
(使用两个 Queue 实现)
入队列的时候利用辅助队列,让队头永远是新进元素,
出队列的直接出队头
可以实现栈的先进先出

方法2:
(使用两个 Deque 实现)
出队列的时候利用辅助队列,除了队尾的最后一个元素,其他元素入辅助队列,再出最后一个元素
入队列的直接入队尾
可以实现栈的先进先出

方法3:
(优化,使用一个 Deque 实现)
出队列的时候,除了队尾的最后一个元素,其他元素入重新入该队列,将队头元素出出去,可以实现栈的先进先出
入队列的直接入队尾
可以实现栈的先进先出
20. 有效的括号(栈)
1047. 删除字符串中的所有相邻重复项(栈)
150. 逆波兰表达式求值(栈)与前面两做运算

理论知识: (栈与队列理论知识)
  • 栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
  • 栈和队列往往不被归类为容器,而被归类为container adapter(容器适配器
  • 栈的底层实现主要就是数组和链表的底层实现。
总结:
  • queue队列是简单的FIFO队列,Deque队列是double ended queue,双端队列,可以在首尾插入或删除元素。

  • 在这里插入图片描述

  • 其实ArrayDeque和LinkedList都可以作为栈以及队列使用,但是从执行效率来说,ArrayDeque作为队列以及LinkedList作为栈使用会是更好的选择。

  • 在java中,Queue被定义成单端队列使用,Deque被定义成双端队列使用。
    而由于双端队列的定义,Deque可以作为栈或者队列使用,而Queue只能作为队列或者依赖于子类的实现作为堆使用。

  • 递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中, 然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。

  • 在企业项目开发中,尽量不要使用递归! 在项目比较大的时候,由于参数多,全局变量等等,使用递归很容易判断不充分return的条件,非常容易无限递归(或者递归层级过深),造成栈溢出错误(这种问题还不好排查!)

  • 栈与递归之间在某种程度上是可以转换的!

  • 逆波兰表达式相当于是二叉树中的后序遍历。

  • 单调队列

  • 优先队列

  • 对频率进行排序,这里我们可以使用一种 容器适配器就是优先级队列

  • 什么是优先级队列呢?
    其实就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。(看起来像队列,实际上是堆)

  • 什么是堆?
    堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。
    1、堆是一棵完全二叉树
    2、树中每个结点的值都不小于(或不大于)其左右孩子的值。

  • 最大堆(大根堆):如果父亲结点是大于等于左右孩子就是大顶堆,
    最小堆(小根堆):小于等于左右孩子就是小顶堆。
    缺省情况下priority_queue利用max-heap(大顶堆)完成对元素的排序,

  • 优先队列PriorityQueue是Queue接口的实现,

2022.4.28

1、栈与队列表刷题:

239.滑动窗口最大值

2、二叉树刷题:

二叉树的迭代遍历

for(int i=res.size()-1;i>=0;i--)
	{
    	Res.add(res.get(i));
    }
总结:
  • 此时我们写出了统一风格的迭代法,不用在纠结于前序写出来了,中序写不出来的情况了。
    但是统一风格的迭代法并不好理解,而且想在面试直接写出来还有难度的。
    所以大家根据自己的个人喜好,对于二叉树的前中后序遍历,选择一种自己容易理解的递归和迭代法。
  • 队列先进先出,符合一层一层遍历的逻辑,
    而是用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
    队列 - 层次遍历, 栈 - 深度优先遍历
  • 二叉树遍历:
    1、递归(前中后序)
    2、迭代(前中后序)DFS
    3、层次遍历 BFS
  • 做完102层次遍历,可以做5道类似题型!

2022.4.29

1、二叉树刷题:

总结:
  • 红黑树其实是一种二叉平衡搜索树
  • 递归与迭代究竟谁优谁劣呢?
    从时间复杂度上其实迭代法和递归法差不多,但是空间复杂度上,递归开销会大一些,因为递归需要系统堆栈存参数返回值等等。
    递归更容易让程序员理解,但收敛不好,容易栈溢出。
    这么说吧,递归是方便了程序员,难为了机器(各种保存参数,各种进栈出栈)。
    在实际项目开发的过程中我们是要尽量避免递归!因为项目代码参数、调用关系都比较复杂,不容易控制递归深度,甚至会栈溢出。
  • 一定要掌握前中后序一种迭代的写法,并不因为某种场景的题目一定要用迭代,而是现场面试的时候,面试官看你顺畅的写出了递归,一般会进一步考察能不能写出相应的迭代。
  • 层序遍历遍历相对容易一些,只要掌握基本写法(也就是框架模板),剩下的就是在二叉树每一行遍历的时候做做逻辑修改。
  • 本周总结:本周我们都是讲解了二叉树,从理论基础到遍历方式,从递归到迭代,从深度遍历到广度遍历,最后再用了一个翻转二叉树的题目把我们之前讲过的遍历方式都串了起来。

2、回溯刷题:

理论知识:回溯算法理论基础
总结:
  • 本篇我们讲解了,什么是回溯算法,知道了回溯和递归是相辅相成的。
    接着提到了回溯法的效率,回溯法其实就是暴力查找,并不是什么高效的算法。
    然后列出了回溯法可以解决几类问题,可以看出每一类问题都不简单。
    最后我们讲到回溯法解决的问题都可以抽象为树形结构(N叉树),并给出了回溯法的模板。
  • 回溯函数也就是递归函数,指的都是一个函数。
  • 虽然回溯法很难,很不好理解,但是回溯法并不是什么高效的算法。因为回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案。
  • 回溯法,一般可以解决如下几种问题:
    组合问题:N个数里面按一定规则找出k个数的集合
    切割问题:一个字符串按一定规则有几种切割方式
    子集问题:一个N个数的集合里有多少符合条件的子集
    排列问题:N个数按一定规则全排列,有几种排列方式
    棋盘问题:N皇后,解数独等等
  • 组合是不强调元素顺序的,排列是强调元素顺序
  • 回溯法解决的问题都可以抽象为树形结构,是的,我指的是所有回溯法的问题都可以抽象为树形结构!
  • 因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度
  • 回溯三部曲:
    1、回溯函数模板返回值以及参数
    2、回溯函数终止条件
    3、回溯搜索的遍历过程
    在这里插入图片描述
  • for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历
  • 回溯算法模板框架:
void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

3、贪心算法刷题:

理论知识:贪心算法理论基础
总结:
  • 贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
  • 什么时候用贪心?
    说实话贪心算法并没有固定的套路。
    有没有什么固定策略或者套路呢?
    不好意思,也没有!
  • 如何验证可不可以用贪心算法呢?
    最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧。
  • 贪心算法一般分为如下四步:
    1、将问题分解为若干个子问题
    2、找出适合的贪心策略
    3、求解每一个子问题的最优解
    4、将局部最优解堆叠成全局最优解
    其实这个分的有点细了,真正做题的时候很难分出这么详细的解题步骤,可能就是因为贪心的题目往往还和其他方面的知识混在一起。
  • 所以这也是为什么很多同学通过(accept)了贪心的题目,但都不知道自己用了贪心算法,因为贪心有时候就是常识性的推导,所以会认为本应该就这么做!
  • 贪心没有套路,说白了就是常识性推导加上举反例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值