刷题注意事项及相关知识(持续更新)

注意事项

先将思路写下来,不要只想,一步一步确定

在无法确定运算先后顺序时加上括号是最稳妥的方法

字符串:首先确定字符串长度,字符范围,将边界条件先写出来
树:二叉树还是二叉搜索树,节点个数,节点是否会重复

  • 递归三要素不同的方法一定不是一个方法

对于大数打印,打印String是一个很好的方法

if语句中定义的局部变量其作用域只在该判断语句中,一般不这么定义

当元素范围已知且较小,能够与角标有某种对应关系,数组比哈希表更快

如果对有序数组,时间复杂度的要求有 log,通常都需要用到二分查找

不重复

  • 保证三个数 a ≤ b ≤ c
  • 枚举时跳过相同元素

需要枚举数组中的两个元素时,如果随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从 O(N^2)减少至 O(N)

相关知识

1.位运算

  • & 与运算符,两个数字做与运算指的是相应的二进制,按照对应位置进行与运算,1为真,0为假,一假即假
  • | 或运算符,两个数字做或运算指的是相应的二进制,按照对应位置进行或运算,1为真,0为假,一真即真
  • 向下整除 n //2 等价于 右移一位n>>1 ;乘2相当于左移一位
  • 取余数 n % 2 等价于 判断二进制最右一位值 n&1
  • n&(n-1) 作用是消除数字 n 的二进制表示中的最后一个 1
    • 一个数如果是 2 的指数,那么它的二进制表示一定只含有一个 1
  • 一个数和它本身做异或运算结果为 0,即 a ^ a = 0;一个数和 0 做异或运算的结果为它本身,即 a ^ 0 = a
  • Integer的equals被重写过,比较的是内部整数的值;==在[-128, 127]之间会被cache缓存,比较的是值,超过此范围则比较的是对象是否相同

2.数组

  • Arrays.sort();对数组排序
  • Arrays.copyOfRange(int[] arr, int start, int end);获得该数组的子数组,含头不含尾
  • return new int[];返回一个空数组

3.字符及字符串

String

  • String StringBuffer(线程安全) StringBuilder三者之间的区别
  • String类的构造方法:new String(char[ ] ch, int offset, int count)从字符数组中按指定角标offset为开端,count为个数,构成字符串
  • substring(index1, index2)按角标位获得子串,含头不含尾
  • contains()判断是否包含指定字符
  • toCharArray():将字符串转化为字符数组
  • trim():去除字符串开头和结尾的空白字符
  • String[] split(String s):以s为界限来分割该字符串,返回一个字符串数组,当字符串开头由空格时,会产生一个"",三个连续空格中会产生两个""
  • public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements),第一个参数为字符串,第二个参数为数组或集合:连接指定数组的元素或集合的成员,在每个元素或成员之间使用指定的分隔符
  • replace(old, new)将旧字符串中的所有old(可以是字符也可以是字符串,但new要与其相同类型)替换为new
  • replaceAll(regex),基于正则表达式的替换,但也兼容replace()形式的替换

Character

  • Character.isDigit():判断一个字符是否为数字

ASCII码

  • 空格的ASCII码值为32;
    数字0到9的ASCII码值分别为48到57;
    大写字母“A”到“Z”的ASCII码值分别为65到90;
    小写字母“a”到“z”的ASCII码值分别为97到到122

正则表达式:

  • \s 匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效
  • +:一次或多次匹配前面的字符或子表达式。例如,"zo+"与"zo"和"zoo"匹配,但与"z"不匹配。+ 等效于 {1,}
  • 在Java中 两个\表示:插入一个正则表达的反斜线,其后的字符具有特殊意义,如"\\s"表示匹配任何空白字符的正则表达式

4.树

把题目的要求细化,搞清楚根节点应该做什么,然后剩下的事情抛给前/中/后序的遍历框架

  • 树的相关一般选择递归:递归的三个元素:递归函数的作用、递归的终止条件、递归关系式
  • 树递归存储节点一般需要使用成员变量
  • 序列化二叉树时单个中序遍历无法得到原树
  • 任意节点n的深度为从根到该节点唯一路径的长,其高度为从该节点到一片树叶的最长路径的长,所以对于n,其高度和深度不一定相同
  • 树的深度等于其高度
  • 将递归转化为迭代一般引入一个队列
  • 树的遍历总体分为两类:DFS和BFS
    DFS:先序、中序、后序遍历,一般使用递归/栈实现
    BFS:层序遍历(按层遍历),一般使用队列实现
  • 队列Queue和栈Stack都是LinkedList实现的,入队列/栈方法是offer()/push(),出队列/栈方法是poll()/pop(),返回栈的顶端元素/队列中的下一个元素都是方法peek()

二叉搜索树BST

  • 二叉搜索树的中序遍历为递增序列,交换左右子树顺序可以变为降序
  • BST 相关的问题,要么利用 BST 左小右大的特性提升算法效率,要么利用中序遍历的特性满足题目的要求
  • 如果当前节点会对下面的子节点有整体影响,可以通过辅助函数增长参数列表,借助参数传递信息。
  • 在二叉树递归框架之上,扩展出一套 BST 代码框架:
void BST(TreeNode root, int target) {
    if (root.val == target)
        // 找到目标,做点什么
    if (root.val < target) 
        BST(root.right, target);
    if (root.val > target)
        BST(root.left, target);
}

5.集合

Map

  • containsKey()判断是否包含指定键
  • 哈希表中相同的键会覆盖前面的
  • getOrDefault(Key, DefaultValue):如果存在Key对应的值,返回此值,不存在则返回DefaultValue
  • Map的遍历
    最常用的方法是在for-each中使用entries实现
Map<Integer, Integer> map = new HashMap<>();

for(Map.Entry<Integer, Integer> entry: map.entrySet)
{
	entry.getKey();
	entry.getValue();
}
map.keySet()获得键的集合
map.values()获得值的集合

  • Deque < Integer > stack = new ArrayDeque<>();
    push()
    pop()
    peek() //peek前要判断栈是否为空,否则会产生空指针异常

List

  • java.util.Collections:public static void reverse(List<?> list):反转一个List
  • 队列:Queue是由LinkedList实现的,入队列方法offer(),出队列方法poll(),返回队列中的下一个元素peek();
  • 双端队列:接口Deque,继承Queue,使用LinkedList实现
    offerFirst(),offerLast(), pollFirst(), pollLast(), peekFirst(), peekLast()
    • 使用时要尽量避免Collection的add()和remove()方法,而是要使用offer()来加入元素,使用poll()来获取并移出元素。它们的优点是通过返回值可以判断成功与否,add()和remove()方法在失败的时候会抛出异常
  • 优先队列:类PriorityQueue,自动将放入的元素升序排列(自定义类要自己定义比较器)
    Comparator cmp = new Comparator() {
    public int compare(Object o1, Object o2) {
    //升序;返回值大于0则交换顺序
    return o1-o2;
    //降序
    return o2-o1;
    }
    };
  • 集合使用isEmpty()判断是否为空

链表
链表是一种兼具迭代和递归性质的数据结构

  • 解决链表问题优先思考1双指针2递归和3栈,4将递归优化为循环
  • 倒序链表时可以使用递归,归的时候遇到结果就返回
  • public LinkedList(Collection<? extends E> c):将c中元素复制到新链表中(动态数组同理)

Arrays

  • public static List asList(T… a)将数组转化为一个List集合

Collections

  • public static void reverse(List<?> list) 反转列表

6.Math

  • Math类:a的b次方:Math.pow(a, b),返回值为double类型
  • 判断奇偶
    除法:奇数:a%2 != 0(负奇数为0), 偶数:a%2 == 0;
    位运算: 奇数:(a&1) == 1; 偶数:(a&1) == 0;
  • Math.max(int a, int b);只能得到两个数的最大值
  • 获得随机数字
    • (数据类型)(最小值+Math.random()*(最大值-最小值+1)):Math.random()获得[0, 1)的一个随机浮点数
    • new Random().nextInt(int a):获得[0, a)的一个随机整数

7. 图/矩阵

  • 图的解法首先考虑DFS 和BFS

解法

动态规划

基本框架

1、确定该问题是否为动态规划问题

  • 具有最优子结构,要符合最优子结构,子问题间必须相互独立

2、如何列出正确的状态转移方程

  1. 确定base case
  2. 确定状态,也就是原问题和子问题中会变化的变量
  3. 确定选择,也就是导致状态产生变化的行为
  4. 明确dp函数/数组的定义
  5. 列出状态转移方程

3、优化

  • 消除重叠子问题:画出递归树
  • 自顶向下带备忘录的递归
  • 自底向上迭代

4、套路框架

# 初始化 base case
dp[0][0][...] = base
# 进行状态转移
for 状态1 in 状态1的所有取值:
    for 状态2 in 状态2的所有取值:
        for ...
            dp[状态1][状态2][...] = 求最值(选择1,选择2...)

5、遍历方向

  • 取决于状态转移方程,现在的状态依赖于过去的状态,因此要先求出过去的状态

6、状态压缩

  • 按照遍历方向进行状态压缩

1.经典问题之编辑距离

解决两个字符串的动态规划问题,一般都是用两个指针 i,j 分别指向两个字符串的最后,然后一步步往前走,缩小问题的规模

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值