刷题时间:2021.7.28-2021.7.31
图的深度优先遍历
从图中某个顶点v出发,首先访问该顶点,然后依次从它的各个未被访问的邻接点出发深度优先搜索遍历图,直至图中所有和v有路径相通且未被访问的顶点都被访问到。若此时尚有其他顶点未被访问到,则另选一个未被访问的顶点作起始点,重复上述过程,直至图中所有顶点都被访问到为止。
图的广/宽度优先遍历
从图中某个顶点v出发,在访问了v之后依次访问v的各个未曾访问过的邻接点,然后分别从这些邻接点出发依次访问它们的邻接点,并使得“先被访问的顶点的邻接点先于后被访问的顶点的邻接点被访问”,直至图中所有已被访问的顶点的邻接点都被访问到。如果此时图中尚有顶点未被访问,则需要另选一个未曾被访问过的顶点作为新的起始点,重复上述过程,直至图中所有顶点都被访问到为止。
1、LeetCode113路径总和 II
思路:1.从根节点深度遍历二叉树,先序遍历时,将该节点值存储至path栈中(vector实现),使用path_ value累加节点值。
2.当遍历至叶结点时,检查path_ value值 是否为sum,若为sum,则将path push进入result结果中。
3.在后序遍历时,将该节点值从path栈中弹出,path_value减去节点值。
/**
* Definition for a binary tree node.
* 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;
* }
* }
*/
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
List<List<Integer>> result = new ArrayList<>();//存储满足条件路径的数组
List<Integer> path = new ArrayList<>();//路径栈
int path_value = 0;//数组值
preorder(root,path_value,targetSum,path,result);
return result;
}
public void preorder(TreeNode node,int path_value,int sum,List<Integer> path,List<List<Integer>> result) {
if(node==null){
return;
}
path_value += node.val;//遍历一个节点即更新一次路径值
path.add(node.val);
if(node.left==null && node.right==null && path_value==sum){
result.add(new ArrayList<>(path));
}
preorder(node.left,path_value,sum,path,result);
preorder(node.right,path_value,sum,path,result);
path_value -= node.val;
path.remove(path.size()-1);//遍历完成后,将该节点送路径栈中弹出
}
}
2、LeetCode236二叉树的最近公共祖先
思路:1两个节点的公共祖先一定在从根节点至这两个节点的路径上。
2.由于求公共祖先中的最近公共祖先,那么即同时出现在这两条路径上的离根节点最远的节点(或离两个最近)。
3.最终算法即:求p节点路径, q节点路径,两路径上最后一个相同的节点。
求根节点至某节点路径(深度搜索):1.从根节点遍历(搜索)至该节点,找到该节点后就结束搜索。
2.将遍历过程中遇到的节点按照顺序存储起来,这些节点即路径节点。
求两路径上最后一个相同的节点:1.求出较短路径的长度n。
2.同时遍历p节点的路径与q节点的路径,遍历n个节点,最后一个发现的相同节点,即最近公共祖先。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List<TreeNode> path = new ArrayList<>();
List<TreeNode> node_p_path = new ArrayList<>();//存储p节点路径
List<TreeNode> node_q_path = new ArrayList<>();//存储q节点路径
int finish = 0;//记录是否完成搜索的变量
preorder(root,p,new Stack<>(),node_p_path,finish);
path.clear();//清空path、finish,计算q节点路径
finish = 0;
preorder(root,q,new Stack<>(),node_q_path,finish);
int path_len = 0;//较短路径的长度
if(node_p_path.size()<node_q_path.size()){
path_len = node_p_path.size();
}else{
path_len = node_q_path.size();
}
TreeNode result = null;//同时遍历根到p、q两个节点的路径上的节点
for(int i=0;i<path_len;i++){
if(node_p_path.get(i)==node_q_path.get(i)){
result = node_p_path.get(i);//找到了最近公共祖先
}
}
return result;
}
//正在遍历的节点,待搜索节点,遍历时的节点路径栈,最终搜索到节点search的路径结果,记录是否找到节点search变量
void preorder(TreeNode node,TreeNode search,Stack<TreeNode> path,List<TreeNode> result,int finish){
if(node==null || finish!=0){
return;
}
path.add(node);//先序遍历时,将节点压入path栈
if(node==search){
finish = 1;//当找到search节点后,标记finish变量
result.addAll(path);//将当前的path存储到result中
}
preorder(node.left,search,path,result,finish);//深度遍历node左孩子
preorder(node.right,search,path,result,finish);//深度遍历node右孩子
path.remove(path.size()-1);//结束遍历node时,将node节点弹出path栈
}
}
LeetCode题解代码
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null) return right;
if(right == null) return left;
return root;
}
}
3、LeetCode114二叉树展开为链表
思路:拆解并解决子问题。
(不知道为什么,改写成Java版本的这个代码一直有错,这里就放c++版本的了)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
void flatten(TreeNode* root) {
if(!root){
return;
}
TreeNode* last = NULL;
preorde(root,last);
}
void preorde(TreeNode* node,TreeNode* &last){
if(!node->left&&!node->right){
last = node;
return ;
}
TreeNode* left = node->left;
TreeNode* right = node->right;
TreeNode* left_last = NULL;
TreeNode* right_last = NULL;
if(left){
preorde(left,left_last);
node->left = NULL;
node->right = left;
last = left_last;
}
if(right){
preorde(right,right_last);
if(left_last){
left_last->right = right;
}
last = right_last;
}
}
};
LeetCode评论代码
class Solution {
public void flatten(TreeNode root){
if (root == null)
return;
flatten(root.left);
flatten(root.right);
// 左子节点不为null的时候才有必要调整
if (root.left != null){
TreeNode temp = root.right;
root.right = root.left;
root.left = null;
while (root.right != null){
root = root.right;
}
root.right = temp;
}
}
}
4、LeetCode199二叉树的右视图
思路:层次遍历时,将节点与层数绑定为pair,压入队列时,将节点与层数同时压入队列,并记录每一层中出现的最后一个节点。在层次遍历中,每一层中的最后一个节点最后遍历到,随时更新对每层的最后一个节点即可。
LeetCode评论代码
/**
* Definition for a binary tree node.
* 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;
* }
* }
*/
class Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> result = new ArrayList<>();
dfs(root, 0, result);
return result;
}
//逆序遍历:根结点-->右节点-->左节点。这样保证每层最先访问的是最右侧的节点,将每层最先访问的节点保存即可
void dfs(TreeNode root, int depth, List<Integer> result) {//root当前节点,depth节点在哪层,result结果集
if (root == null) {
return;
}
//当深度在n层,result放入第n该层元素后,则result中元素个数大于该层深度,即深度从0开始。如放完了第二层,result中有2个元素,而第二层的深度索引时1。只有到第3层时,且result未放入第三层的元素,此时深度索引是2,result中的元素个数也是2。也就是首次出现相等的时候加入元素就是该层最右侧的元素
if (depth == result.size()) {
result.add(root.val);
}
depth++;
dfs(root.right, depth, result);
dfs(root.left, depth, result);
}
}
5、LeetCode207课程表
思路:若有向图无坏,则可以完成全部课程,否则不能。问题转换成,构建图,并判断图是否有环。
方法一:深度优先搜索
在深度优先搜索时,如果正在搜索某一顶点(还未退出该顶点的递归深度搜索),又回到了该顶点,即证明图有环。
LeetCode官方题解:
class Solution {
List<List<Integer>> edges;
int[] visited;
boolean valid = true;
public boolean canFinish(int numCourses, int[][] prerequisites) {
edges = new ArrayList<List<Integer>>();
for (int i = 0; i < numCourses; ++i) {
edges.add(new ArrayList<Integer>());
}
visited = new int[numCourses];
for (int[] info : prerequisites) {
edges.get(info[1]).add(info[0]);
}
for (int i = 0; i < numCourses && valid; ++i) {
if (visited[i] == 0) {
dfs(i);
}
}
return valid;
}
//节点访问状态,1代表正在访问,0代表没有访问过,2代表已完成访问
public void dfs(int u) {
visited[u] = 1;
for (int v: edges.get(u)) {
if (visited[v] == 0) {
dfs(v);
if (!valid) {
return;
}
} else if (visited[v] == 1) {
valid = false;
return;
}
}
visited[u] = 2;
}
}
方法二:拓扑排序(宽度优先搜索)
在宽度优先搜索时,只将入度为0的点添加至队列。当完成一个顶点的搜索(从队列取出),它指向的所有顶点入度都减1,若此时某顶点入度为0则添加至队列,若完成宽度搜索后,所有的点入度都为0,则图无环,否则有环。
LeetCode官方题解:
class Solution {
List<List<Integer>> edges;
int[] indeg;
public boolean canFinish(int numCourses, int[][] prerequisites) {
edges = new ArrayList<List<Integer>>();
for (int i = 0; i < numCourses; ++i) {
edges.add(new ArrayList<Integer>());
}
indeg = new int[numCourses];
for (int[] info : prerequisites) {
edges.get(info[1]).add(info[0]);
++indeg[info[0]];
}
Queue<Integer> queue = new LinkedList<Integer>();
for (int i = 0; i < numCourses; ++i) {
if (indeg[i] == 0) {
queue.offer(i);
}
}
int visited = 0;
while (!queue.isEmpty()) {
++visited;
int u = queue.poll();
for (int v: edges.get(u)) {
--indeg[v];
if (indeg[v] == 0) {
queue.offer(v);
}
}
}
return visited == numCourses;
}
}