二叉树与图
问题1 路径之和2
Given a binary tree and a sum, find all root-to-leaf paths where each path’s sum equals the given sum.
Note: A leaf is a node with no children.
Example:
Given the below binary tree and sum = 22
,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
Return:
[
[5,4,11,2],
[5,8,4,5]
]
链接:https://leetcode-cn.com/problems/path-sum-ii/
思路:
- 从根节点深度遍历二叉树,先序遍历时,将该节点值保存至path栈中,使用path_value累加节点值。
- 当遍历至叶节点时,检查path_value值是否为sum,若为sum,则将path push进result结果中。
- 在后序遍历时,将该节点值从path栈中弹出,path_value减去节点值。
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
explicit TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
vector<vector<int> > pathSum(TreeNode *root, int sum) {
vector<vector<int> > result;
vector<int> path; // 当前路径
int path_value = 0;
preoder(root, path_value, sum, path, result);
return result;
}
private:
void preoder(TreeNode *node, int &path_value, int sum, vector<int> &path,
vector<vector<int> > &result) {
if (!node)
return;
path_value += node->val;
path.push_back(node->val);
if (!node->left && !node->right && path_value == sum) {
result.push_back(path);
}
preoder(node->left, path_value, sum, path, result);
preoder(node->right, path_value, sum, path, result);
path_value -= node->val;
path.pop_back();
}
};
问题2 最近公共祖先
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).”
Given the following binary tree: root = [3,5,1,6,2,0,8,null,null,7,4]
Example 1:
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
Output: 3
Explanation: The LCA of nodes 5 and 1 is 3.
Example 2:
Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
Output: 5
Explanation: The LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition.
Note:
- All of the nodes’ values will be unique.
- p and q are different and both values will exist in the binary tree.
链接:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
-
两个节点的公共祖先一定在从根节点到这两个节点的路径上。
-
由于求公共祖先中最近的公共祖先,那么即同时出现在这两条路径上的离根节点最远的节点。
-
求p和q节点的路径,两路径上最后一个相同的节点。
问题3 二叉树转链表
Given a binary tree, flatten it to a linked list in-place.
For example, given the following tree:
1
/ \
2 5
/ \ \
3 4 6
The flattened tree should look like:
1
\
2
\
3
\
4
\
5
\
6
链接:https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/
基础知识 二叉树的层次遍历
广度优先搜索
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x): val(x), left(NULL), right(NULL) {}
};
void BFS_print(TreeNode* root) {
queue<TreeNode *> Q;
Q.push(root);
while (!Q.empty()) {
TreeNode *node = Q.front();
Q.pop();
print(node);
if (node->left) {
Q.push(node->left);
}
if (node->right) {
Q.push(node->right);
}
}
}
问题4 侧面观察二叉树
Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.
Example:
Input: [1,2,3,null,5,null,4]
Output: [1, 3, 4]
Explanation:
1 <---
/ \
2 3 <---
\ \
5 4 <---
链接:https://leetcode-cn.com/problems/binary-tree-right-side-view/
深度优先搜索和广度优先搜索都能解决,深度优先搜索都不需要生成pair,
class Solution { public: vector<int> rightSideView(TreeNode *root) { vector<int> result; dfs(root, result, 0); return result; } private: void dfs(TreeNode *root, vector<int> &result, int layer) { if (!root) return; if (result.size() <= layer) result.push_back(root->val); else result[layer] = root->val; dfs(root->left, result, layer + 1); dfs(root->right, result, layer + 1); } };
广度优先搜索可以不用想上面那样使用pair,可以这样:
先将root push进队列,(队列长度=1)
然后取出root,并添加到result中
然后将root的左右child放入队列(队列长度=2)
取出队列中的两个child,将最后一个child添加到result中,
然后把child的child都添加到队列中(队列长度=4)
取出队列中的4个孙子,将最后一个孙子放到result中,
将8个曾孙放到队列中(队列长度=8)…class Solution { public: vector<int> rightSideView(TreeNode *root) { if (!root) return {}; queue<TreeNode *> Q; Q.push(root); vector<int> result; while (!Q.empty()) { TreeNode *temp; int count = Q.size(); while (count--) { temp = Q.front(); Q.pop(); if (temp->left) Q.push(temp->left); if (temp->right) Q.push(temp->right); } result.push_back(temp->val); } return result; } };
问题5 课程安排
There are a total of numCourses
courses you have to take, labeled from 0
to numCourses-1
.
Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?
Example 1:
Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.
Example 2:
Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should
also have finished course 1. So it is impossible.
Constraints:
- The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
- You may assume that there are no duplicate edges in the input prerequisites.
1 <= numCourses <= 10^5
链接:https://leetcode-cn.com/problems/course-schedule/
思路1 深度优先搜索
visit[3]=1表示访问过了,0->2->3再遇到3时并没有环,不在同一条链上。visit[*]=0的节点都在一条链上。
struct GraphNode {
int label;
vector<GraphNode *> neighbors;
explicit GraphNode(int x) : label(x) {}
};
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int> > &prerequisites) {
vector<GraphNode *> graph;
vector<int> visit; // 访问状态 -1没有访问 0正在访问 1已访问
for (int i = 0; i < numCourses; i++) {
graph.push_back(new GraphNode(i));
visit.push_back(-1);
}
for (auto & prerequisite : prerequisites) {
GraphNode *begin = graph[prerequisite[1]];
GraphNode *end = graph[prerequisite[0]];
begin->neighbors.push_back(end);
}
// 以各个起点的图都要判断一遍
for (int i = 0; i < graph.size(); ++i) {
if (visit[i] == -1 && !DFS_graph(graph[i], visit)) {
return false;
}
}
for (int i = 0; i < numCourses; i++) {
delete graph[i];
}
return true;
}
private:
// 判断以node为起点的图里面有没有环 true=无环
bool DFS_graph(GraphNode *node, vector<int> &visit) {
visit[node->label] = 0;
for (auto & neighbor : node->neighbors) {
if (visit[neighbor->label] == -1) {
if (!DFS_graph(neighbor, visit)) {
return false;
}
} else if (visit[neighbor->label] == 0) { // 表示邻居就是自己
return false;
}
}
visit[node->label] = 1;
return true;
}
};
为什么把图分成好几块了还是正确的。因为如果有环一定属于同一块。
为什么当return false
产生的时候不用把visit
改成1,现场应该只出现0或2:
因为一旦出现return false
,表明整个算法出结果了,保留不正确的现场也无所谓。
在不执行return false
的时候,是一定不会出现visit==0
的节点的。所以很安全
思路2 广度优先搜索(拓扑排序)
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
struct GraphNode {
int label;
vector<GraphNode *> neighbors;
explicit GraphNode(int x) : label(x) {}
};
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int> > &prerequisites) {
vector<GraphNode *> graph;
vector<int> degree; // 入度
for (int i = 0; i < numCourses; ++i) {
degree.push_back(0);
graph.push_back(new GraphNode(i));
}
for (auto &prerequisite : prerequisites) {
GraphNode *begin = graph[prerequisite[1]];
GraphNode *end = graph[prerequisite[0]];
begin->neighbors.push_back(end);
degree[end->label]++;
}
queue<GraphNode *> Q;
for (int i = 0; i < numCourses; ++i) {
if (!degree[i]) {
Q.push(graph[i]);
}
}
while (!Q.empty()) {
GraphNode *node = Q.front();
Q.pop();
for (auto &neighbor : node->neighbors) {
degree[neighbor->label]--;
if (!degree[neighbor->label]) {
Q.push(neighbor);
}
}
}
for (auto &i : graph) {
delete i;
}
for (int i : degree) {
if (i) {
return false;
}
}
return true;
}
};