赞
踩
Two Pointers同向:其中[0,i)的数据代表处理好的数据,[i,j)中的数据是那些处理过但不需要的数据,[j,array.length)区间的数据为接下来待处理的数据。这里的三个区间的开和闭需要根据题目要求定义,但是要保持一致。用此方法处理过的数组,处理好的数据的相对位置会保持一致
通用步骤
①initialize two pointers i and j,usually both equal to 0
②while j <array.length:
if we need array[j],then we keep it by assigning array[i] = array[j], and move i forward, make it ready at the next position
otherwise skip it. We do not need to move i since its spot is not fullfilled
Two Pointers反向:其中[0,i)和(j,array.length)内的数据均为处理好的数据,[i,j]中的数据待处理。用此方法处理过的数组不会保留原来元素的相对位置
通用步骤
①initialize two pointers i =0, j=array.length -1
②while i <j:
Decide what you should do based on the value of array[i] and array[j]
Move at least one pointer forward in its direction
练习题
11.盛最多水的容器
26.删除有序数组中的重复项
42.接雨水
80.删除有序数组中的重复项 II
283.移动零
344.反转字符串
1047.删除字符串所有相邻重复项
是一种针对有序区间内的O(logN)搜索方式,最常见用于已经排好序的Array
两大基本原则
每次都要缩减搜索区域
每次缩减不能排除潜在答案
找一个准确值
循环条件:l ≤ r
缩减搜索空间:l = mid +1,r=mid -1
找一个模糊值(比2大的最小数)
循环条件:l<r
缩减搜索空间:l = mid,r = mid-1或者l = mid +1,r=mid
万用型(最接近2的数)
循环条件:l<r-1
缩减搜索空间:l = mid,r = mid
练习题
410.分割数组的最大值
852.山脉数组的峰顶索引
1011.在D天内送达包裹的能力
1062.最长重复子串
1231.分享巧克力
1292.元素和小于等于阈值的正方形的最大边长
Array:一组内存里连续的数据
能用index访问 → O(1) Access
添加元素直接添加在最后 → Amortized O(1) Add(考虑到扩容)
删除元素需要挪动后面所有元素位置 → O(n) Delete
Linked List:内存里不一定连续的数据
不能用index访问 → O(n) Access
添加元素添加在最后 → O(n) Add,双链表O(1)
删除元素需要找到元素位置 → O(n) Delete
两个指针指向Linked List节点,不再是index
两个指针必定同向而行
①一个快一个慢,距离隔开多少
②两个指针移动速度
Bottom up recursion 3 steps:
①Ask for subproblem result
②Do something in current level of recursion
③Return result
第1,3步的值的含义必须相同
Recursion最重要的一点:相信自己的recursive function是对的
特性:LIFO (Last In First Out)
适用于需要记录之前的状态,必要的时候可以回到之前的状态,或者利用之前的值
不像array,不能用index访问,只能每次拿栈顶元素
题外话:动态规划Dynamic Programming
DP:记录之前所有状态,随时可能访问任何一个子问题,所以通常用Array或者Hash Table,而且不会回到之前的状态,只会利用之前的值
Stack:每次只需要栈顶元素,并且每个状态只会被用O(1)次
定义好Stack的含义
在arr[i]左侧所有比arr[i]大的数
递归之前的函数状态(Call Stack)
练习题
20.有效的括号
84.柱状图中最大的矩形
394.字符串解码
496.下一个更大元素 I
503.下一个更大元素 II
636.函数的独占时间
735.行星碰撞
739.每日温度
Heap本质是一个用Array实现的Complete Binary Tree,这个Tree的root节点代表整个Heap里的最大或最小值
Common APIs:
peek() → 查看堆顶元素O(1)
poll() → 拿出堆顶元素O(logN)
offer() → 添加元素O(logN)
Online Algorithm(using Heap):
针对一组流数据,没有固定长度,可以随时根据需求scalable
Offline Algorithm(using Sorting):
针对一组固定长度数据,每次scale后要重新计算
练习题
23.合并K个升序链表
215.数组中的第K个最大元素
253.会议室 II
295.数据流的中位数
347.前K个高频元素
703.数据流中的第K大元素
767.重构字符串
HashMap
用Array + LinkedList(chaining)实现的能在平均O(1)时间内快速增删查的数据结构
表内存储的数据需要实现equals()和hashCode()
LinkedHashMap
有顺序的hashmap,遍历顺序是key插入的顺序
所有的key按顺序存成一个LinkedList
TreeMap
有顺序的map,遍历顺序是key从小到大
所有的key存成一个红黑树(self-balancing binary tree)
增删查O(logN)
HashSet
没有value的HashMap
什么时候用HashMap?
当需要快速查找数据时,可以利用HashMap加速查找
主要要查找的数据结构实现了equal()和hashCode()
Array和HashMap的区别在于:Array无法快速查找,HashMap可以;Array里的元素是有顺序的,HashMap没有;Array的overhead比较小,HashMap实现比较复杂
练习题
1.两数之和
3.无重复字符的最长子串
49.字母异位词分组
138.复制带随机指针的链表
535.TinyURL的加密与解密
554.砖墙
560.和为K的子数组
类似LinkedList的概念,内存中不一定连续的数据,由各个节点的Reference串起来组成
节点可以分为Parent和Child两类
可以看做一个特殊的无环无向图
只有一个入口root
99%Tree的题目都是没有parent指针的Binary Tree
是按层的概念进行的搜索算法,利用Queue记录需要被展开的TreeNode
适合解决与层数相关的Tree题目
①Initialize Queue with all entry points
②While queue is not empty
for each node in the queue(currently)
poll out the element(add to result)
expand it,offer children to the queue in order
increase level
每个Node进出Queue一次 → O(n)
练习题
101.对称二叉树
102.二叉树的层序遍历
103.二叉树的锯齿形层序遍历
104.二叉树的最大深度
111.二叉树的最小深度
429.N叉树的层序遍历
515.在每个树行中找最大值
DFS是一种递归形式的搜索方式,更偏向于“垂直”的概念
①Base Case
②DoSomething
③Recurse for subproblems
Top Down DFS
把值通过参数的形式从上往下传
一般dfs本身不返回值
①Base Case
②利用父问题传下来的值做一些计算
③若有必要,做一些额外操作
④把值传下去给子问题继续递归
练习题
129.求根节点到叶节点数字之和
Bottom Up DFS(更难也更常见)
把值从下往上传
当前递归利用subproblem传上来的值计算当前层的新值并返回
一定会有返回值
①Base Case
②向子问题要答案(return value)
③利用子问题的答案构建当前问题(当前递归层)的答案
④若有必要,做一些额外操作
⑤返回答案(给父问题)
练习题
104.二叉树的最大深度
124.二叉树中的最大路径和
类似LinkedList的概念,内存中不一定连续的数据,由各个节点的Reference串起来组成
可能有环
分为有向图和无向图
没有固定入口
可能有多个入口
图该以什么形式存储?最常用的两大类:Adjacency Matrix和Adjacency List
Adjacency List
最常用的两种实现方式(List可用Set代替)
List<T>[n]
adjList[i]:All neighbors of node i
Need to know number of nodes(n) beforehand
Map<T,List<T>>
adjList.get(i):All neighbors of node i
以层为概念的搜索方式,因为是水平展开所有nodes,所以适合寻找最短路径
图可能有环,需要查重
①Initialize a Queue with all starting points,a HashSet to record visited nodes
②While queue is not empty
Retrieve current queue size as number of nodes in the current level
for each node in current level
poll out one node
if this is the node we want,return it
offer all its neighbor to the queue if not visited and valid
Increase level
小技巧:对于2D Matrix的图,matrix[i][j]的neighbors一般都是上下左右4个,所以预先存一个4 direction array可以帮助访问neighbors → directions = {{0,1},{1,0},{0,-1},{-1,0}}
Time Complexity O(V+E)
练习题
127.单词接龙
542.01矩阵
针对Non-uniform cost graph的一种算法,核心思想是优先展开最优的节点
如何每次快速计算最优 → Heap
最有名的graph中找最短路径的算法又称Dijsktra's Algorithm
①Initialize a Heap with all starting points marked with some initial costs, a HashSet to record visited nodes
②While heap is not empty
poll out one node
if it has already been expanded(visited),skip it
otherwise mark the node as visited,update its cost
if this is the destination node,return
for all of its neighbors,offer them in to the heap with current node's cost + edge cost
Time: O((E+V)logV) Space:O(V)
练习题
743.网络延迟时间
787.K站中转内最便宜的航班
不同于BFS一层一层往外扩展,DFS会一口气扎到最深层再递归回到原点,然后再次一口气扎到另一条路的最深层,如此往复
①Initialize HashSet to record visited nodes
②For all entry nodes,call dfs():
Validate current node,if visited or invalid or answer node,return
Do something(pre-order)
For each neighbor node:
Validate neighbor node,if visited or invalid or answer node,don't recurse on it or return answer
Recurse down on neighbor node → dfs(neighbor)
Do something(post-order)
DFS traverse graph一般不允许访问同一节点非常数次,所以Time Complexity O(N*k),k=max(time(b),time(d))
练习题
133.克隆图
200.岛屿数量
332.重新安排行程
399.除法求值
785.判断二分图
841.钥匙与房间
当一个大问题是由多个子问题构成时,我们可以通过不断分解问题来最终构建我们想求的大问题,这个过程称为搜索
搜索空间可以用Tree的形式展现出来,便于理解
时间复杂度取决于这棵树的深度和每个node的children个数
Search最重要的就是定义好状态,保证每个子问题都能用一个状态来描述
练习题
78.子集
如果我们Search Space有重复子问题的话,可以记录下这些子问题的答案来保证不会重复计算多次,所以DP也被称为Search+Memorization
如此一来,时间复杂度就取决于子问题的个数
搜索空间可以用Tree的形式展现出来,便于理解
所有DP都可以写成Bottom Up DFS的形式
重中之重仍然是定义好状态
小技巧:定义好状态后,可以从一个中间状态出发去思考递归规则
Bottom Up DFS
①Define state of subproblems
②initialize memo to record calculated subproblems
③return dfs(top_level_answer_state)
dfs(state):
①base case check
②if current problem is calculated,return its answer
③for each subproblem x
ask subproblem for their answers → call dfs(subproblem_state)
build up current state problem answer based on subproblem answers
④store current problem answer
练习题
139.单词拆分
练习题
53.最大子数组和
91.解码方法
96.不同的二叉搜索树
140.单词拆分 II
279.完全平方数
303.区域和检索 - 数组不可变
338.比特位计数
343.整数拆分
2D Array → state = (row,col)
2个1D Array → Each 1D state
1D Array + K → state = (i,k)
1D Array → 2D state (subarray)
练习题
5.最长回文子串
63.不同路径 II
123.买卖股票的最佳时机 III
188.买卖股票的最佳时机 IV
312.戳气球
322.零钱兑换
410.分割数组的最大值
516.最长回文子序列
873.最长的斐波那契子序列的长度
887.鸡蛋掉落
877.石子游戏
1000.合并石头的最低成本
1143.最长公共子序列
1216.验证回文字符串 III
1312.让字符串成为回文串的最少插入次数
1335.工作计划的最低难度
Backtrack是DFS的一种形式,基本写法类似于Top Down DFS,但是引入了状态回溯
每次搜索一个分支,会首先记录当前节点的状态,尝试完某一个分支后,把状态回溯到记录的状态,再去尝试另外的分支
为什么要回溯状态?如果不回溯,A分支的状态可能会被代入B分支,但他们又是独立的,所以会影响结果
①Base Case
②For each possibility p
memorize current state
backtrack(next_state)
restore current state
练习题
46.全排列
47.全排列 II
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。