笔记
1.链表和数组
数组
没啥可说的
链表
没啥可说的
练习
[1.翻转链表](https://leetcode.com/problems/reverse-linked-list)
1 | class ListNode { |
两个指针,`head`指向链表现在所处的位置,`p`代表翻转链表。
[2.链表两两节点反转](https://leetcode.com/problems/swap-nodes-in-pairs)
1 | public static ListNode swapPairs(ListNode head) { |
这个想了好久好久,最后总结出如果首元节点改变的话,基本都要添加头指针,否则由于首元节点的改变,最后无法回到新的首元节点,我们需要三个指针分别存储cur.next,cur.next.next,以及cur(cur负责对链表进行遍历并进行相应操作)。为了防止链表的自身循环,一定要注意指令的执行顺序。思路很简单:就是在遍历的时候将指针进行翻转,之后cur.next指针后移两位。但是最后一步不能使用cur.next = a.next 或者 cur.next = b.next,因为使用了->next会使原本头指针发生指向错误(会牵制到p)。但是所幸leetcode结果还是不错:
[3.判断链表是否有环](https://leetcode.com/problems/linked-list-cycle)
在leetcode时间允许范围内遍历链表,如果有Null节点,返回true,否则返回false(不推荐)
运用set,判断set中是否存在对应节点
1
2
3
4
5
6
7
8
9
10
11
12public static boolean hasCycle(ListNode head) {
Set<ListNode> nodes = new HashSet<>();
while (head != null) {
if (nodes.contains(head))
return true;
else {
nodes.add(head);
head = head.next;
}
}
return false;
}快慢指针法,若有环,迟早相遇
1
2
3
4
5
6
7
8
9
10
11public static boolean hasCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (slow != null && fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast)
return true;
}
return false;
}
- 运用set,返回相应节点
1 | public ListNode detectCycle(ListNode head) { |
运用快慢指针,但是需要注意一点,两者相遇并不能确保是第一个开始循环的节点,网上的解法很巧妙,但是运用到了数学证明,所以…emmm。
1 | public static ListNode reverseKGroup(ListNode head, int k) { |
自己没想出来,看得别人的提交,这个比较好理解,双重循环,里面的循环解决一组翻转,两个指针分别代表翻转开始时的头结点和准备翻转的节点。
2.堆栈和队列
堆栈
队列
优先队列(PriorityQueue)
正常入、按照优先级出
实现机制:
1.heap(Binary(大根堆,小根堆),Binomial,Fibonacci)
2.Binary Search Tree(二叉搜索树)
练习
1 | public static boolean isValid(String s) { |
这是之前做过的一道leetcode的题目,这个是别人的解法,代码很巧妙。运用了栈的思想,先将左括号压入栈,如果碰见右括号,如果该字符串有效的话,应该可以正好匹配栈顶的左括号,否则就无效,当扫描完字符串,栈不为空时,字符串也无效。我想起了这道题的一个变体,给你括号的对数,能输出几种有效的组合呢?
1 | //char str = new char[2 * count] |
1 | class MyQueue { |
小根堆
1 | //优先队列 |
java中PriorityQueue底层数据结构是平衡二叉堆,jdk1.8源码中对该数据结构的介绍:
1 | /** |
大意就是说该堆的实现是由comparator决定的(大根堆、小根堆),如果没有实现该默认的方法则默认为小根堆。
解法1:利用大根堆(优先队列)
1 | public int[] maxSlidingWindow(int[] nums, int k) { |
解法2:利用双端队列
1 | public static int[] maxSlidingWindowii(int[] nums, int k) { |
window记录下标,res记录结果。如果window记录数值的话,不好解决当窗口内元素为k时,窗口向前滑动的情况。
3.映射(Map)和集合(Set)
映射
key: value 形式
HashMap vs TreeMap (哈希表对二叉搜索树)
查询 O(1) O(log<sub>2</sub>n)
无序 有序
集合
HashSet vs TreeSet (哈希表对二叉搜索树)
查询 O(1) O(log<sub>2</sub>n)
无序 有序
练习
排序
1 | public static boolean isAnagram(String s, String t) { |
Map:计数
1 | public static boolean isAnagramii(String s, String t) { |
改善做法(map计数)
1 | if (s.length() == 0 && t.length() == 0) return true; |
通过数组下标记录个数
1 | public static int[] twoSum(int[] nums, int target) { |
使用Hashmap记录下标
使用双重循环,将需要的数字放入set中,如果在遍历过程中发现这个数字就加入到结果集中
1 | public static List<List<Integer>> threeSum(int[] nums) { |
指针法 排序、遍历,如果三个数相加<0 左指针右移,否则右指针左移
1 | public static List<List<Integer>> threeSumii(int[] nums) { |
反人类,不搞了
4.树和图
树
*二叉搜索树*
*二叉树遍历*:
- pre-order:根左右
- in-order:左根右
- post-order:左右根
图
练习
中序遍历(递增)
1 | public boolean isValidBST(TreeNode root) { |
递归判断 最大值最小值 牛皮
1 | public boolean isValid(TreeNode root, Integer min, Integer max) { |
1 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q){ |
这玩意思路很简单,但是做了好久,因为我忘写递归的return了,怎么写都错,我佛了
1 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { |
查看左子树和右子树,将结点保存到left、right结点
5.递归和分治
递归(Recrusion)
- 终止条件
- 递归体(业务逻辑、调用递归函数、做一些收尾工作(不必须))
分治(Divide&Conquer)
一般不适用于有中间结果、或者重复计算
一般使用递归
步骤:
1.终止条件
2.拆分问题(准备数据)
3.处理数据
4.合并子结果
练习
时间复杂度为o(n)的做法,由于我用的递归,但是会造成stackflowerror,就是说递归函数会占用太多的栈内存空间,所以递归太深就会报错。
改进版,时间复杂度为(logn),从中对折。
1 | public double myPow(double x, int n) { |
使用mypow(x * x, n/ 2) 而不是mypow(x, n/2) * mypow(x, n/2)的原因是写法简单,因为nk * nk = n2的k次幂。为什么要判断Integer.MIN_VALUE,因为图片:
改进的非递归版本
1 | public double myPow(double x, int n) { |
用的位运算,实质上还是for循环,这样性能应该会好一些。还是运算了n次
暴力 O(n2)
map O(n)
排序 O(nlogn)
分治
1 | public int majorityElement(int[] nums) { |
6.贪心算法
- 在问题求解时,做出当前最优选择
- 适用场景:问题可以分成子问题,而且子问题的最优解可以变成全局最优解。
练习
贪心
1 | public int maxProfit(int[] prices) { |
7.广度优先、深度优先遍历
广度优先:
深度优先:
思维类似于栈
练习
BFS
1 | public List<List<Integer>> levelOrder(TreeNode root) { |
通过使用for循环,将每一个层次的树节点输出
DFS
1 | public List<List<Integer>> levelOrder(TreeNode root) { |
注意存入每个节点应该属于的层次,通过深度遍历将值放入所属于的层次中。
DFS
1 | public int maxDepth(TreeNode root) { |
BFS
1 | public static int maxDepthii(TreeNode root) { |
DFS
1 | public int minDepth(TreeNode root) { |
这个处理的很巧妙,我的方法就有点low了
1 | public static int DFSii(TreeNode root, int level) { |
BFS
1 | public static int minDepthii(TreeNode root) { |
1 | public List generateParenthesis(int n) { |
也可通过递归生成所有可能性,然后依次判断,麻烦!
8.剪枝
搜索的常用手段 ,把差的分支去掉(alpha go)
练习
1 | public List<List<String>> solveNQueens(int n) { |
1 | public boolean isValidSudoku(char[][] board) { |
1 | public void solveSudoku(char[][] board) { |
9.二分查找
- 有序
- 数组形式
- 有界
练习
1 | public int mySqrt(int x) { |
10.字典树(Trie Tree)
实际应用
统计排序大量字符串,文本词频统计(搜索引擎)
基本结构
![](https://cxlsky.oss-cn-beijing.aliyuncs.com/blog/img/trie.png?x-oss-process=style/blogimg)
核心思想
空间换时间
基本性质:
- 根节点不包含字符,除根节点外每一个节点都只包含一个字符
- 每个节点所有子节点的字符都不同
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串
练习
1 | class TrieNode { |
1 | public List<String> findWords(char[][] board, String[] word) { |
11.位运算
练习
1 | public class Solution { |
1 | public boolean isPowerOfTwo(int n) { |
1 | public int[] countBits(int num) { |
arr 记录1至n中1的个数 得到相关递推公式
1 | public static int totalNQueens(int n){ |
12.动态规划
图
练习
1 | public int climbStairs(int n) { |
1 | public int minimumTotal(List<List<Integer>> triangle) { |
1 | public int maxProduct(int[] nums) { |
121、122、123 、309、188、714
1 |
o(n²)
1 | public static int lengthOfLIS(int[] nums) { |
dp[i]代表在i位置的数字时,当前的最长子序列。
O(nlogn)
1 | public int lengthOfLIS(int[] nums) { |
维护一个数组dp ,通过二分查找寻找插入的数(替换掉比其大的最小的数),最后返回长度
1 | public int coinChange(int[] coins, int amount) { |
1 | public int minDistance(String word1, String word2) { |
13.并查集
练习
DFS
1 | if (grid.length == 0) return 0; |
染色法,将附近的小岛全部染色
并查集
1 | 比较麻烦,思路类似 最后数一数祖先数有多少 |
1 | public int findCircleNum(int[][] grid) { |
与孤岛问题略有不同
14.LRU Cache
Cache缓存:
- 记忆
- 钱包-储物柜 (容量小)
- 代码模块
LRUCache:
- 最近最少使用(least recently used)
- Double LinkedList(双向链表)
- O(1)查询(最前面)
- O(1) (修改)