链表
链表千万别递归,递归栈会导致你的空间复杂度无法做到O(1)
快慢指针
- 判断是否有环
- 一次遍历找到链中的倒数第几个点
- 找到中间节点
空置头节点
new一个空节点指向头节点可以有效的防止空指针报错
数组
前缀和数组
new一个新数组preSum来存放之前数组nums(该数组不能被修改)的和preSum[i]=nums[i]+nums[i-1]
。就可以通过preSum数组的加减快速求出原数组某个区间内元素的和。
进阶
- 可以扩展到二维数组
- 可以用哈希表来代替数组计算前缀和
差分数组
类似于前缀和数组,new一个新数组diff来存放之前数组nums的差diff[i]=nums[i]-nums[i-1]
。当需要频繁的改动nums中某个区间的值时只需要在对应的差分数组位置加减即可。diff[i]+1
表示nums[i:]
都加1。diff[i]+1&&diff[j]-1
表示nums[i:j-1]
都加1。
坑
题目可能会花里胡哨导致你看不出这道题应该用差分数组来做。比如力扣1109. 航班预订统计。一般来说需要频繁的对数组的某个区间进行修改的都可以用差分数组来做。
双指针
数组的双指针不同于链表的快慢指针。数组的指针指的是数组的索引,且一般定义为一头一尾来解决类似反转数组,或者有序列表的相关问题(如二分查找,有序列表的两数和等)
滑动窗口
其实也是双指针,两个指针之间为“窗口”。通过改变两个指针的值来改变窗口的大小,当尾指针到数组末尾时滑动结束。一般用来做一些匹配问题,时间复杂度O(n)。原理比较简单,但是实现起来的细节很多,很容易出bug.=。一般会使用哈希表来存储窗口数据以及返回条件最小覆盖字串
//基本框架
int left=0,right=0;
int valid=0;
Map<Object,Integer> need=new HashMap<>();
Map<Object,Integer> window=new HashMap<>();
while(right<s.length){//增大窗口
right++;
//增大后操作
if(valid符合条件)return res;
while(减小窗口){
left++;
//减小后操作
}
}
二叉树
递归淦他!只要是个树,那基本上递归都比迭代写起来简单。
public TreeNode Tree(TreeNode root){
if(root==null) return;
//do sth 先序遍历
Tree(root.left);
//do sth;中序遍历
Tree(root.right);
//do sth;后序遍历;
}
二叉搜索树
二叉搜索树:
- 左子树的值小于根结点的值
- 右子树的值大于根节点的值
- 左右子树都是二叉搜索树
因为搜索树的中序遍历就是结点的升序排序。可以通过不同的遍历顺序来做到不同的排序方式。
可以使用二叉搜索树左小右大的特点来对搜索树的特定结点进行快速的操作
public TreeNode BST(TreeNode root,int key){
if(root==null) return
if(root.val==key){
//找到结点do sth;
return;
}else if(key>root.val){
root.right=BST(root.right,key);
}else{
root.left=BST(root.left,key);
}
return root;
}
回溯算法
就是穷举。先做一个选择,然后回退再做一个选择
public back(){
if(符合条件){
//do sth;
}
for(所有能做的选择){
做选择(数据入队等)
back()
撤销选择(数据出队等)
//做选择和撤销一般是对应的
}
}