leetcode--二叉树和图
来源:互联网 发布:淘宝上最火的店铺 编辑:程序博客网 时间:2024/06/06 12:40
基础知识
二叉树的数据结构
struct TreeNode{ int val; //数据域 TreeNode *left //左右指针 TreeNode *right TreeNode(int x):val(x),left(NULL),right(NULL){} //构造函数}
构造二叉树
构造如下的一棵二叉树:
int main(){ TreeNode a(1); TreeNode b(2); TreeNode c(3); TreeNode d(4); TreeNode e(5); TreeNode f(6); a.left=&b; a.right=&c; b.left=&d; b.right=&e; c.right=&f;}
二叉树的深度遍历
前序遍历访问 traversal(node->left)
中序遍历访问 traversal(node->right)
后序遍历访问
前中后指的是访问根节点的顺序,前序:先根再左再右
二叉树的层次遍历
也就是广度优先搜索,用队列一层一层的访问节点,访问一个节点压入该节点的孩子节点,队列不空,持续该过程。
void BFS_print(TreeNode* root){ queue<TreeNode*> Q; Q.push(root); while(!Q.empty()){ TreeNode* node=Q.front(); Q.pop(); printf("(%d)\n",node->val); if(node->left){ Q.push(node->left; } if(node->right){ Q.push(node->right; } }}
leetcode题目
113 Path Sum
题意:
给定一个二叉树与整数sum,找出所有从根节点到叶节点的路径,要求这些路径上节点值的和等于sum。
解题思路:
用深度优先搜索路径。
1 先序遍历时将节点值存储到path栈中,path_value累加节点值
2 当遍历到叶节点时判断path_value是不是等于sum,if等于push进result中。
3 在后序遍历时,将节点从path栈中弹出,path_value减去节点值
代码:
class Solution {public: vector<vector<int>> pathSum(TreeNode* root, int sum) { vector<vector<int>> result; 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,vector<int> &path,vector<vector<int>> &result){ //先序遍历内容: if(!node){ return; } path_value=path_value+node->val; path.push_back(node->val); if(!node->left&&!node->right&&path_value==sum){ result.push_back(path); } preorder(node->left,path_value,sum,path,result); preorder(node->right,path_value,sum,path,result); //后序遍历内容: path.pop_back(); path_value=path_value-node->val; }};
236 Lowest Common Ancestor of a Binary Tree
题意:
给出两个节点,求出他们最近的公共祖先。
解题思路:
1 用深度优先搜索找点,找到点后保存路径。
2 同时遍历两个路径,找最后一个相同点。
代码:
void preorder(TreeNode* node, TreeNode *search, std::vector<TreeNode*> &path, std::vector<TreeNode*> &result, int &finish){ if (!node || finish){ return; //结束技巧,finish用于标记已找到节点 } path.push_back(node); if (node == search){ finish = 1; result = path; } preorder(node->left, search, path, result, finish); preorder(node->right, search, path, result, finish); path.pop_back();//后序遍历时根节点要做退出操作,就是左右节点都不是要找的点,根节点弹出}class Solution {public: TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { std::vector<TreeNode*> path; std::vector<TreeNode*> node_p_path; std::vector<TreeNode*> node_q_path; int finish = 0; preorder(root, p, path, node_p_path, finish);//找p点的路径并保存于node_p_path path.clear(); finish = 0; preorder(root, q, path, node_q_path, finish);//找q点的路径并保存于node_q_path 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 = 0; //找两个路径中最后一个相同点 for (int i = 0; i < path_len; i++){ if (node_p_path[i] == node_q_path[i]){ result = node_p_path[i];//少写一点代码 } } return result; }};
114. Flatten Binary Tree to Linked List
题意:
不用大的额外空间,将二叉树转换为链表,left为null,right为链表的next指针。链表的顺序是树的先序遍历。
解题思路:
如果不考虑大的额外空间,最简单的做法是用一个vector存先序遍历的结果,然后遍历这个vector。
代码:
class Solution {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]; } }private: void preorder(TreeNode *node,vector<TreeNode *> &node_vec){ if (!node){ return; } node_vec.push_back(node); preorder(node->left, node_vec); preorder(node->right, node_vec); }};
解题思路2:
思考过程是:独立开树的一部分,思考前中后遍历时候应该做什么。考虑输入和输出:
* 先序遍历:第一次访问根节点时,我们要保留左右节点,让right指针指向左节点。右指针指向NULL。
* 中序遍历:第二次访问根节点,这时左子树完成了链表变形,要将左子树的最后节点指向,右子树的开始节点。所以要输出一个末尾节点,可以用&引用去维护。
* 后续遍历:维护最末的节点。
代码:
class Solution {public: void flatten(TreeNode* root) { TreeNode *last=NULL; preorder(root,last); }private: void preorder(TreeNode* node,TreeNode* &last){ //*& TreeNode*的意思是传入TreeNode的指针变量,&是可以在函数中改变这个变量 if(!node){ return; } 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){ preorder(left,left_last);//递归,维护左子树的末尾节点 node->left=NULL; //放在中序遍历执行时因为可以省去判断是否有左子树的语句。 node->right=left; last=left_last; } if(right){ preorder(right,right_last); if(left_last){ left_last->right=right;//让左子树的末尾节点指向右子树的开始节点。放在后序遍历执行可以省去判断是否有右子树的语句。 } last=right_last;//维护右子树的末尾节点 } }};
199.Binary Tree Right Side View
题意:
从右边观察二叉树,输出每一层最右边的节点。
解题思路:
层次遍历二叉树,将节点和层数绑定微pair,压入队列时,将节点和层数同时压入,记录每一层的最后一个节点。
代码:
class Solution {public: vector<int> rightSideView(TreeNode* root) { vector<int> view; //存储每层最后的节点。 queue<pair<TreeNode*,int>> Q; //广度优先搜索,用一个pair存储节点和它对应的层数。 if(root){ Q.push(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[depth]=node->val; } if(node->left){ Q.push(make_pair(node->left,depth+1)); //压入左节点,标记为下一层 } if(node->right){ Q.push(make_pair(node->right,depth+1)); //压入右节点,标记为下一层 } } return view; }};
图
图的表示(邻接矩阵):
代码:
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;}
表示稠密图作用比较大,但是表示稀疏图一般用邻接表。
图的表示(邻接表):
代码:
#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;}
深度优先搜索
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); } }}
广度优先搜索
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; } } }}
leetcode题目
207. Course Schedule
题意:给出课程之间的依赖关系,求是否可以将所有的课程全部完成。其实就是求有向图是否有环。
解题思路一:
- 用深度优先搜索,如果递归搜索某一条路径时发现路径中有重复的节点,则有环,不能完成。
- 这里的visit的数组要设计三种状态:-1是未被搜索,0是正在被搜索的路径上,1是已经完成搜索的节点。
- 像递归二叉树思考方式一样,思考先序中序后序遍历要进行什么操作:
- 先序遍历:将该节点标记状态visit为0
- 中序遍历:如果访问过程中出现访问到visit为0的点则返回false
- 后续遍历:访问完成将该节点标记状态visit为-1
代码:
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){ return false; } } else if (visit[node->neighbors[i]->label] == 0){ return false; } } visit[node->label] = 1; return true;}class Solution {public: bool canFinish(int numCourses, std::vector<std::pair<int, int> >& prerequisites) { std::vector<GraphNode*> graph; std::vector<int> visit; 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); } 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; }};
解题思路二:
用广度优先搜索,用入度的概念,每次只将入度为0的点压入队列,它指向的所有节点的入度都-1,-1后入度为0的点可以压入队列,如果能遍历整个图,则可以完成,不能遍历则不能完成。
代码:
struct GraphNode{ int label; std::vector<GraphNode *> neighbors; GraphNode(int x) : label(x) {};};class Solution {public: 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); degree[prerequisites[i].first]++; } std::queue<GraphNode *> Q; for (int i = 0; i < numCourses; i++){ if (degree[i] == 0){ 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){ Q.push(node->neighbors[i]); } } } for (int i = 0; i < graph.size(); i++){ delete graph[i]; } for (int i = 0; i < degree.size(); i++){ if (degree[i]){ return false; } } return true; }};
- leetcode--二叉树和图
- Path Sum 二叉树路径和 @LeetCode
- leetcode之二叉树最长路径和
- leetcode:构建二叉树
- 重建二叉树 leetcode
- LeetCode -- Flatten 二叉树
- leetcode-构造二叉树
- leetcode二叉树遍历
- leetcode 重建二叉树
- 二叉树题目-leetcode
- LeetCode:反转二叉树
- [leetcode]二叉树遍历
- LeetCode二叉树合并
- LeetCode基础--二叉树
- [LeetCode]对称二叉树
- 【Leetcode】二叉树简单路径最大和问题
- LeetCode (12) Path Sum (二叉树路径和判断)
- 搭建leetcode 链表和二叉树本地测试环境
- 自学mysql(基础篇)
- Eclipse的安装和配置(Windows)
- Java中Synchronized的用法
- 花式窃取NetNTLM哈希的方法
- mysql存储过程介绍(-)
- leetcode--二叉树和图
- c++计算数字字符串的和
- Hadoop开发初探--Hadoop环境搭建
- 氚云常用代码
- 【sql】oracle运维查看隐含参数
- 在多用户OFDM系统中的无线信息和能量传输
- Python学习 第二天任务 (一:【基于Python编程从入门到实践】第二章 变量和字符串 书本及动手试一试)
- Netty实现简单RPC
- Split Linked List in Parts:切分链表