【 数据结构 & 算法 】—— 二叉树、图
2020-11-15 本文已影响0人
Du1in9
思维导图
预备知识:二叉树定义(★)
- 预备知识_二叉树定义.cpp
#include <stdio.h>
struct TreeNode { // 二叉树数据结构
int val; // 数据域
TreeNode *left; // 左子树指针
TreeNode *right; // 右子树指针
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 当前遍历的节点,当前节点的层数
void preorder_print(TreeNode *node, int layer){
if (!node){ // 若空,返回
return;
}
for (int i = 0; i < layer; i++){ // 根据层数,打印相应数量的 '-'
printf("-----");
}
printf("[%d]\n", node->val);
preorder_print(node->left, layer + 1); // 遍历左子树,层数 + 1
preorder_print(node->right, layer + 1); // 遍历右子树,层数 + 1
}
int main(){
TreeNode a(1);
TreeNode b(2);
TreeNode c(5);
TreeNode d(3);
TreeNode e(4);
TreeNode f(6);
a.left = &b;
a.right = &c;
b.left = &d;
b.right = &e;
c.right = &f;
preorder_print(&a, 0);
return 0;
}
预备知识:二叉树的深度遍历(★)
- 预备知识_二叉树深度遍历.cpp
#include <stdio.h>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
void traversal_print3(TreeNode *node,int layer){
if (!node){
return;
}
// 前序遍历
for (int i = 0; i < layer; i++){
printf("-----");
}
printf("[%d]\n", node->val);
traversal_print3(node->left, layer + 1);
traversal_print3(node->right, layer + 1);
}
void traversal_print1(TreeNode *node,int layer){
if (!node){
return;
}
// 中序遍历
traversal_print1(node->left, layer + 1);
for (int i = 0; i < layer; i++){
printf("-----");
}
printf("[%d]\n", node->val);
traversal_print1(node->right, layer + 1);
}
void traversal_print2(TreeNode *node,int layer){
if (!node){
return;
}
// 后序遍历
traversal_print2(node->left, layer + 1);
traversal_print2(node->right, layer + 1);
for (int i = 0; i < layer; i++){
printf("-----");
}
printf("[%d]\n", node->val);
}
int main(){
TreeNode a(1);
TreeNode b(2);
TreeNode c(5);
TreeNode d(3);
TreeNode e(4);
TreeNode f(6);
a.left = &b;
a.right = &c;
b.left = &d;
b.right = &e;
c.right = &f;
traversal_print1(&a, 0);
printf("\n");
traversal_print2(&a, 0);
printf("\n");
traversal_print3(&a, 0);
printf("\n");
return 0;
}
例1:路径之和(二叉树深搜)(★★)
- LeetCode 113.Path Sum .cpp
#include <stdio.h>
#include <vector>
struct TreeNode { // 二叉树数据结构
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public: // 根,路径和
std::vector<std::vector<int> > pathSum(TreeNode* root, int sum) {
std::vector<std::vector<int> > result; // 存储满足条件路径的数组
std::vector<int> path; // 路径栈
int path_value = 0; // 路径值
preorder(root, path_value, sum, path, result);
return result;
}
private:
void preorder(TreeNode *node, int &path_value, int sum,
std::vector<int> &path,
std::vector<std::vector<int> > &result){
if (!node){ // 若树空,跳出函数
return;
}
path_value += node->val; // 更新路径值
path.push_back(node->val); // 更新路径栈
if (!node->left && !node->right && path_value == sum){ // 到达叶节点,且路径和等于sum
result.push_back(path); // 更新路径数组
}
//(前序遍历)
preorder(node->left, path_value, sum, path, result);
preorder(node->right, path_value, sum, path, result);
path_value -= node->val; // 遍历完后,减去节点值
path.pop_back(); // 遍历完后,弹出节点值
}
};
int main(){
TreeNode a(5);
TreeNode b(4);
TreeNode c(8);
TreeNode d(11);
TreeNode e(13);
TreeNode f(4);
TreeNode g(7);
TreeNode h(2);
TreeNode x(5);
TreeNode y(1);
a.left = &b;
a.right = &c;
b.left = &d;
c.left = &e;
c.right = &f;
d.left = &g;
d.right = &h;
f.left = &x;
f.right = &y;
Solution solve;
// 返回满足条件路径的数组
std::vector<std::vector<int> > result = solve.pathSum(&a, 22);
for (int i = 0; i < result.size(); i++){
for (int j = 0; j < result[i].size(); j++){
printf("[%d] ", result[i][j]);
}
printf("\n");
}
return 0;
}
例2:最近的公共祖先(二叉树性质)(★★)
- LeetCode 236.Lowest Common Ancestor of a Binary Tree.cpp
#include <stdio.h>
#include <vector>
#include <set>
struct TreeNode { // 二叉树数据结构
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
void preorder(TreeNode* node, // 当前遍历节点
TreeNode *search, // 搜索的节点
std::vector<TreeNode*> &path, // 节点路径栈
std::vector<TreeNode*> &result, // 最终路径结果
int &finish){ // 记录是否搜索到节点
if (!node || finish){
return;
}
path.push_back(node); // 先序遍历,push入路径栈
if (node == search){ // 若搜索到节点,则更新 finish
finish = 1;
result = path;
}
preorder(node->left, search, path, result, finish); // 深度遍历 node左后代
preorder(node->right, search, path, result, finish); // 深度遍历 node右后代
path.pop_back(); // 结束遍历时,将 node节点弹出栈
}
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
std::vector<TreeNode*> path; // 遍历的临时栈
std::vector<TreeNode*> node_p_path; // p的路径栈
std::vector<TreeNode*> node_q_path; // q的路径栈
int finish = 0; // 记录是否搜索到节点
preorder(root, p, path, node_p_path, finish); // 获取 p的路径栈
path.clear();
finish = 0;
preorder(root, q, path, node_q_path, finish); // 获取 q的路径栈
TreeNode *result = 0;
for (int i = 0; i < node_p_path.size(); i++){ // 遍历完得到最近公共祖先节点
if (node_p_path[i] == node_q_path[i]){
result = node_p_path[i];
}
}
return result;
}
};
int main(){
TreeNode a(3);
TreeNode b(5);
TreeNode c(1);
TreeNode d(6);
TreeNode e(2);
TreeNode f(0);
TreeNode x(8);
TreeNode y(7);
TreeNode z(4);
a.left = &b;
a.right = &c;
b.left = &d;
b.right = &e;
c.left = &f;
c.right = &x;
e.left = &y;
e.right = &z;
Solution solve;
TreeNode *result = solve.lowestCommonAncestor(&a, &b, &f);
printf("root=3, p=5, q=0, 最近公共祖先:%d\n", result->val);
result = solve.lowestCommonAncestor(&a, &d, &z);
printf("root=3, p=6, q=4, 最近公共祖先:%d\n", result->val);
result = solve.lowestCommonAncestor(&a, &b, &y);
printf("root=3, p=5, q=7, 最近公共祖先:%d\n", result->val);
return 0;
}
例3:二叉树转链表(二叉树与链表)(★★)
- LeetCode 114.Flatten Binary Tree to Linked List (solve1).cpp
#include <stdio.h>
#include <vector>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
private:
void preorder(TreeNode *node, std::vector<TreeNode *> &node_vec){
if (!node){
return;
}
node_vec.push_back(node); // 前序遍历转换成链表
preorder(node->left, node_vec);
preorder(node->right, node_vec);
}
public:
void flatten(TreeNode *root) {
std::vector<TreeNode *> node_vec;
preorder(root, node_vec); // 链表
for (int i = 1; i < node_vec.size(); i++){ // 没有左枝的树
node_vec[i-1]->left = NULL;
node_vec[i-1]->right = node_vec[i];
}
}
};
int main(){
TreeNode a(1);
TreeNode b(2);
TreeNode c(5);
TreeNode d(3);
TreeNode e(4);
TreeNode f(6);
a.left = &b;
a.right = &c;
b.left = &d;
b.right = &e;
c.right = &f;
Solution solve;
solve.flatten(&a); // 生成树
TreeNode *head = &a;
while(head){ // 遍历树
printf("[%d]", head->val);
head = head->right;
}
printf("\n");
return 0;
}
- LeetCode 114.Flatten Binary Tree to Linked List (solve2).cpp
#include <stdio.h>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
private:
// 当前的节点,子树最后的节点
void preorder(TreeNode *node, TreeNode *&last){
if (!node){ // 遍历完节点返回
return;
}
if (!node->left && !node->right){ // 找到子树最后的节点
last = node;
return;
}
TreeNode *left = node->left; // 左指针 left
TreeNode *right = node->right; // 右指针 right
TreeNode *left_last = NULL; // 左子树最后的节点 left_last
TreeNode *right_last = NULL; // 右子树最后的节点 left_last
if (left){ // 若有左子树,递归将左子树转换为单链表
preorder(left, left_last);
node->left = NULL;
node->right = left; // 连接 node 和 left
last = left_last;
}
// 若有右子树,递归将右子树转换为单链表
if (right){
preorder(right, right_last);
if(left_last){
left_last->right = right; // 连接 left 和 right
}
last = right_last;
}
}
public:
void flatten(TreeNode *root) {
TreeNode *last = NULL;
preorder(root, last);
}
};
int main(){
TreeNode a(1);
TreeNode b(2);
TreeNode c(5);
TreeNode d(3);
TreeNode e(4);
TreeNode f(6);
a.left = &b;
a.right = &c;
b.left = &d;
b.right = &e;
c.right = &f;
Solution solve;
solve.flatten(&a);
TreeNode *head = &a;
while(head){
if (head->left){
printf("ERROR\n");
}
printf("[%d]", head->val);
head = head->right;
}
printf("\n");
return 0;
}
预备知识:二叉树层次遍历(★)
- 预备知识_二叉树层次遍历.cpp
#include <stdio.h>
#include <vector>
#include <queue>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 二叉树层次遍历
void BFS_print(TreeNode* root){
std::queue<TreeNode *> Q;
Q.push(root); // push 根
while(!Q.empty()){
TreeNode *node = Q.front();
Q.pop();
printf("[%d]\n", node->val);
if (node->left){ // push 左孩子
Q.push(node->left);
}
if (node->right){ // push 右孩子
Q.push(node->right);
}
}
}
int main(){
TreeNode a(1);
TreeNode b(2);
TreeNode c(5);
TreeNode d(3);
TreeNode e(4);
TreeNode f(6);
a.left = &b;
a.right = &c;
b.left = &d;
b.right = &e;
c.right = &f;
BFS_print(&a);
return 0;
}
例4:侧面观察二叉树(二叉树宽搜)(★★)
- LeetCode 199.Binary Tree Right Side View.cpp
#include <stdio.h>
#include <vector>
#include <queue>
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
class Solution {
public:
std::vector<int> rightSideView(TreeNode* root) {
std::vector<int> view; // 按层遍历的最后一个节点
std::queue<std::pair<TreeNode *, int> > Q;
if (root){ // 若根非空,push <root, 0>
Q.push(std::make_pair(root, 0));
}
while(!Q.empty()){
TreeNode *node = Q.front().first; // 第一个参数是节点
int depth = Q.front().second; // 第二个参数是层数
Q.pop(); // 弹出此层上一节点
if (view.size() == depth){
view.push_back(node->val);
}
else{ // 更新此层 view
view[depth] = node->val;
}
if (node->left){ // 更新层数 depth
Q.push(std::make_pair(node->left, depth + 1));
}
if (node->right){ // 更新层数 depth
Q.push(std::make_pair(node->right, depth + 1));
}
}
return view; // 返回每层的最后一个节点
}
};
int main(){
TreeNode a(1);
TreeNode b(2);
TreeNode c(5);
TreeNode d(3);
TreeNode e(4);
TreeNode f(6);
a.left = &b;
a.right = &c;
b.left = &d;
b.right = &e;
c.right = &f;
Solution solve;
std::vector<int> result = solve.rightSideView(&a);
for (int i = 0; i < result.size(); i++){
printf("【%d】\n", result[i]);
}
return 0;
}
预备知识
- 预备知识 _ 图的表示 (临接矩阵).cpp
#include <stdio.h>
int main(){
const int MAX_N = 5; // 顶点个数
int Graph[MAX_N][MAX_N] = {0}; // 初始化矩阵
// 将图连通,且不带权
Graph[0][2] = 1;
Graph[0][4] = 1;
Graph[1][0] = 1;
Graph[1][2] = 1;
Graph[2][3] = 1;
Graph[3][4] = 1;
Graph[4][3] = 1;
printf("Graph:\n");
// 打印输出图
for (int i = 0; i < MAX_N; i++){
for (int j = 0; j < MAX_N; j++){
printf("%d ", Graph[i][j]);
}
printf("\n");
}
return 0;
}
- 预备知识 _ 图的表示 (临接表).cpp
#include <stdio.h>
#include <vector>
struct GraphNode{
int label; // 图的顶点
std::vector<GraphNode *> neighbors; // 相邻节点的指针数组
GraphNode(int x) : label(x) {};
};
int main(){
const int MAX_N = 5;
GraphNode *Graph[MAX_N];
// 新建内存
for (int i = 0; i < MAX_N; i++){
Graph[i] = new GraphNode(i);
}
// 将图连通
Graph[0]->neighbors.push_back(Graph[2]);
Graph[0]->neighbors.push_back(Graph[4]);
Graph[1]->neighbors.push_back(Graph[0]);
Graph[1]->neighbors.push_back(Graph[2]);
Graph[2]->neighbors.push_back(Graph[3]);
Graph[3]->neighbors.push_back(Graph[4]);
Graph[4]->neighbors.push_back(Graph[3]);
// 打印输出
printf("Graph:\n");
for (int i = 0; i < MAX_N; i++){
printf("Label(%d) : ", i);
for (int j = 0; j < Graph[i]->neighbors.size(); j++){
printf("%d ", Graph[i]->neighbors[j]->label);
}
printf("\n");
}
// 释放内存
for (int i = 0; i < MAX_N; i++){
delete Graph[i];
}
return 0;
}
- 预备知识_图的深度优先遍历.cpp
#include <stdio.h>
#include <vector>
struct GraphNode{ // 图的邻接表数据结构
int label;
std::vector<GraphNode *> neighbors;
GraphNode(int x) : label(x) {};
};
void DFS_graph(GraphNode *node, int visit[]){
visit[node->label] = 1; // 标记已访问的顶点
printf("%d ", node->label);
for (int i = 0; i < node->neighbors.size(); i++){ // 访问相邻的且没有被访问的顶点
if (visit[node->neighbors[i]->label] == 0){
DFS_graph(node->neighbors[i], visit);
}
}
}
int main(){
const int MAX_N = 5;
GraphNode *Graph[MAX_N];
for (int i = 0; i < MAX_N; i++){ // 创建图的顶点
Graph[i] = new GraphNode(i);
}
// 添加图的边
Graph[0]->neighbors.push_back(Graph[4]);
Graph[0]->neighbors.push_back(Graph[2]);
Graph[1]->neighbors.push_back(Graph[0]);
Graph[1]->neighbors.push_back(Graph[2]);
Graph[2]->neighbors.push_back(Graph[3]);
Graph[3]->neighbors.push_back(Graph[4]);
Graph[4]->neighbors.push_back(Graph[3]);
int visit[MAX_N] = {0};
// 用来标记访问过的点
for (int i = 0; i < MAX_N; i++){
if (visit[i] == 0){ // 若该顶点未被访问
printf("From label(%d) : ", Graph[i]->label);
DFS_graph(Graph[i], visit);
printf("\n");
}
}
for (int i = 0; i < MAX_N; i++){ // 释放内存
delete Graph[i];
}
return 0;
}
- 预备知识_图的宽度优先遍历.cpp
#include <stdio.h>
#include <vector>
#include <queue>
struct GraphNode{
int label;
std::vector<GraphNode *> neighbors;
GraphNode(int x) : label(x) {};
};
void BFS_graph(GraphNode *node, int visit[]){
std::queue<GraphNode *> Q; // 宽度优先遍历使用队列
Q.push(node);
visit[node->label] = 1; // 标记已访问的顶点
while(!Q.empty()){ // 遍历队列
GraphNode *node = Q.front();
Q.pop();
printf("%d ", node->label);
for (int i = 0; i < node->neighbors.size(); i++){
if (visit[node->neighbors[i]->label] == 0){ // 访问相邻的且没有被访问的顶点
Q.push(node->neighbors[i]);
visit[node->neighbors[i]->label] = 1;
}
}
}
}
int main(){
const int MAX_N = 5;
GraphNode *Graph[MAX_N];
for (int i = 0; i < MAX_N; i++){ // 创建图的顶点
Graph[i] = new GraphNode(i);
}
// 添加图的边
Graph[0]->neighbors.push_back(Graph[4]);
Graph[0]->neighbors.push_back(Graph[2]);
Graph[1]->neighbors.push_back(Graph[0]);
Graph[1]->neighbors.push_back(Graph[2]);
Graph[2]->neighbors.push_back(Graph[3]);
Graph[3]->neighbors.push_back(Graph[4]);
Graph[4]->neighbors.push_back(Graph[3]);
int visit[MAX_N] = {0};
for (int i = 0; i < MAX_N; i++){
if (visit[i] == 0){
printf("From label(%d) : ", Graph[i]->label);
BFS_graph(Graph[i], visit);
printf("\n");
}
}
for (int i = 0; i < MAX_N; i++){ // 释放内存
delete Graph[i];
}
return 0;
}
例5:课程安排(有向图安排环)(★★)
- LeetCode 207.Course Schedule(solve1).cpp
#include <stdio.h>
#include <vector>
struct GraphNode{
int label; // 当前节点
std::vector<GraphNode *> neighbors; // 相邻节点
GraphNode(int x) : label(x) {};
};
bool DFS_graph(GraphNode *node, std::vector<int> &visit){
visit[node->label] = 0; // 该节点正在访问
for (int i = 0; i < node->neighbors.size(); i++){ // 遍历该节点的相邻节点
if (visit[node->neighbors[i]->label] == -1){ // 若该相邻节点未被访问
if (DFS_graph(node->neighbors[i], visit) == 0){ // 带入进行 DFS
return false;
}
}
else if (visit[node->neighbors[i]->label] == 0){ // 若该相邻节点正在访问
return false;
}
}
visit[node->label] = 1;
return true;
}
class Solution {
public:
// pair<课程1课程2> 课程1依赖课程2
bool canFinish(int numCourses, std::vector<std::pair<int, int> >& prerequisites) {
std::vector<GraphNode*> graph; // 邻接表
std::vector<int> visit; // 节点访问状态, -1没有访问过, 0正在访问, 1已完成访问
for (int i = 0; i < numCourses; i++){ // 创建图的节点,并赋访问状态为空
graph.push_back(new GraphNode(i));
visit.push_back(-1);
}
for (int i = 0; i < prerequisites.size(); i++){ // 创建图, 连接图的顶点
GraphNode *begin = graph[prerequisites[i].second];
GraphNode *end = graph[prerequisites[i].first];
begin->neighbors.push_back(end); // 课程2指向课程1
}
for (int i = 0; i < graph.size(); i++){
// 如果节点没访问过, 进行DFS, 如果DFS遇到环, 返回无法完成
if (visit[i] == -1 && !DFS_graph(graph[i], visit)){
return false;
}
}
for (int i = 0; i < numCourses; i++){ // 释放内存
delete graph[i];
}
return true;
}
};
int main(){
std::vector<std::pair<int, int> > prerequisites;
prerequisites.push_back(std::make_pair(1, 0));
prerequisites.push_back(std::make_pair(2, 0));
prerequisites.push_back(std::make_pair(3, 1));
prerequisites.push_back(std::make_pair(3, 2));
Solution solve;
printf("0->1->3, 0->2->3\n");
printf("是否可以将四个课程全部完成:%d\n", solve.canFinish(4, prerequisites));
prerequisites.push_back(std::make_pair(1, 0));
prerequisites.push_back(std::make_pair(2, 1));
prerequisites.push_back(std::make_pair(3, 2));
prerequisites.push_back(std::make_pair(0, 3));
Solution solve2;
printf("0->1->2->3->0\n");
printf("是否可以将四个课程全部完成:%d\n", solve2.canFinish(4, prerequisites));
return 0;
}
- LeetCode 207.Course Schedule(solve2).cpp
#include <stdio.h>
#include <vector>
#include <queue>
struct GraphNode{
int label;
std::vector<GraphNode *> neighbors;
GraphNode(int x) : label(x) {};
};
class Solution {
public:
// pair<课程1课程2> 课程 1依赖课程 2
bool canFinish(int numCourses, std::vector<std::pair<int, int> >& prerequisites) {
std::vector<GraphNode*> graph;
std::vector<int> degree; // 入度数组
for (int i = 0; i < numCourses; i++){ // 初始化入度,创建节点
degree.push_back(0);
graph.push_back(new GraphNode(i));
}
for (int i = 0; i < prerequisites.size(); i++){
GraphNode *begin = graph[prerequisites[i].second];
GraphNode *end = graph[prerequisites[i].first];
begin->neighbors.push_back(end); // 课程 2指向课程 1
degree[prerequisites[i].first]++; // 课程 1入度 ++
}
std::queue<GraphNode *> Q;
for (int i = 0; i < numCourses; i++){
if (degree[i] == 0){ // 初始时入度为 0的节点存入 Q
Q.push(graph[i]);
}
}
while(!Q.empty()){
GraphNode *node = Q.front();
Q.pop();
for (int i = 0; i<node->neighbors.size(); i++){ // 遍历节点
degree[node->neighbors[i]->label]--; // 该节点的相邻节点入度 --
if (degree[node->neighbors[i]->label] == 0){ // 遍历后入度为 0的节点存入 Q
Q.push(node->neighbors[i]);
}
}
}
for (int i = 0; i < graph.size(); i++){ // 释放内存
delete graph[i];
}
for (int i = 0; i < degree.size(); i++){ // 若存在节点入度不为 0
if (degree[i]){
return false;
}
}
return true;
}
};
int main(){
std::vector<std::pair<int, int> > prerequisites;
prerequisites.push_back(std::make_pair(1, 0));
prerequisites.push_back(std::make_pair(2, 0));
prerequisites.push_back(std::make_pair(3, 1));
prerequisites.push_back(std::make_pair(3, 2));
Solution solve;
printf("0->1->3, 0->2->3\n");
printf("是否可以将四个课程全部完成:%d\n", solve.canFinish(4, prerequisites));
prerequisites.push_back(std::make_pair(1, 0));
prerequisites.push_back(std::make_pair(2, 1));
prerequisites.push_back(std::make_pair(3, 2));
prerequisites.push_back(std::make_pair(0, 3));
Solution solve2;
printf("0->1->2->3->0\n");
printf("是否可以将四个课程全部完成:%d\n", solve2.canFinish(4, prerequisites));
return 0;
}