《面试算法 LeetCode 刷题班》——5.二叉树与图

本文内容是基于小象学院——林沐 《面试算法 LeetCode 刷题班》,后期仍将对相关内容进行不定期更新!

5. 二叉树与图

文章目录

      • 5. 二叉树与图
        • leetCode 113 路径之和2
        • LeetCode 236 最近的公共祖先
        • LeetCode 114 二叉树转链表
        • LeetCode 199 侧面观察二叉树
        • LeetCode 207 课程安排

leetCode 113 路径之和2

这里题目已经事先定义了一个,树节点的类型结构:

 struct TreeNode {
 int val;
 TreeNode *left;
 TreeNode *right;
 TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 };

代码:

 class Solution {
 public:
	vector> pathSum(TreeNode* root, int sum) {
		vector> result;
		vector path;
		int path_value = 0;
		preorder(root, path_value, sum, path, result);
		return result;
	}

	void preorder(TreeNode *node, int & path_value, int sum, vector& path, vector>&result) {
		if (!node)
		{
			return;
		}
		path_value += node->val;
		path.push_back(node->val);
		if (path_value == sum && !node->left && !node->right) // 除了值相等,还要保证当前的点是叶节点
		{
			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();
	}
};

LeetCode 236 最近的公共祖先

思路:两个路径对应的路径,同时出现最后一个相同的节点。

 class Solution {
 public:
	 TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
		 vector path; // 声明遍历用的临时栈
		 vector node_p_path; // 存储p节点路径
		 vector node_q_path; // 存储q节点路径
		 int finish = 0; //记录是否搜索的变量finish
		 preorder(root, p, path, node_p_path, finish); // 计算节点p路径
		 path.clear();
		 finish = 0; // 清空状态,计算节点q路径
		 preorder(root, q, path, 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;
		 for (int i = 0; i < path_len; i++) {
			 if (node_p_path[i] == node_q_path[i])
			 {
				 result = node_p_path[i];
			 }
		 }
		 return result;
	 }

	 void preorder(TreeNode* node, TreeNode* search, vector &path, vector &result, int & finish) { // 注意输入都是引用
		 if (!node || finish ) // 当node为空或已找到search节点直接返回,结束搜索
		 {
			 return;
		 }
		 path.push_back(node); //先序遍历,将节点压入path 栈
		 if (node == search)
		 {
			 finish = 1; // 当找到 search 节点后,标记finish变量
			 result = path; // 将当前的path存储到result中
		 }
		 preorder(node->left, search, path, result, finish); // 深度遍历 node 左孩子
		 preorder(node->right, search, path, result, finish); // 深度遍历 node 右孩子
		 path.pop_back(); // 结束遍历node时,将node 节点弹出 path 栈
	 }
 };

LeetCode 114 二叉树转链表

难点是需要将二叉树就地(in-place)转换为单链表。单链表中间节点顺序为二叉树前序遍历顺序。 方法一:

 class Solution {
 public:
	 void flatten(TreeNode* root) {
		 vector 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];
		 }
	 }

	 void preorder(TreeNode*node, vector &node_vec) {
		 if (!node)
		 {
			 return;
		 }
		 node_vec.push_back(node);
		 preorder(node->left, node_vec);
		 preorder(node->right, node_vec);
	 }
 };

方法二: (就地法)

分别将二叉树根节点的左子树和右子树分别转成链表,然后将链表进行拼接。

 class Solution {
 public:
	 void flatten(TreeNode* root) {
		 TreeNode* last = NULL;
		 preorder(root,last);
	 }

	 void preorder(TreeNode*node, TreeNode* &last) {
		 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;
		 }
		
	 }
 };

二叉树层次遍历,又称为宽度优先搜索(BFS),按树的层次依次访问树的节点。层次遍历使用队列对遍历节点进行存储,先进入队列的结点,优先遍历拓展其左孩子与右孩子。

 class Solution {
 public:
	 void BFS(TreeNode* root) {
		 queue Q;
		 Q.push(root);
		 while (!Q.empty())
		 {
			 TreeNode* node = Q.front();
			 Q.pop();
			 cout << node->val<left)
			 {
				 Q.push(node->left);
			 }
			 if (node->right)
			 {
				 Q.push(node->right);
			 }
		 }
	 }
 };

由此引入下列问题:

LeetCode 199 侧面观察二叉树

突破点:则是记录每一层最后一个节点。

思路: 将层数与节点绑定为pair,压入队列,并记录每一层中出现的最后一个节点,在层次遍历中,每一层遍历到最后一个节点,记录即可。

 class Solution {
 public:
	 vector rightSideView(TreeNode* root) {
		 vector view;
		 queue> Q;
		 if (root)
		 {
			 Q.push(make_pair(root, 0));
		 }
		 while (!Q.empty())
		 {
			 TreeNode* node = Q.front().first;
			 int depth = Q.front().second;
			 Q.pop();
			 if (depth == view.size())
			 {
				 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;
	 }
 };

LeetCode 207 课程安排

预备知识:

图(graph)分为有向图,无向图

图的两个表示方式:

(1) 邻接矩阵

以二维矩阵表示:

#include
using namespace std;
int main()
{
	const int MAX_N = 4;
	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;
	for  (int i =0; i

(2) 邻接表

代码:

struct GraphNode
{
	int label;
	vector neighbors;
	GraphNode(int x) : label(x) {};
};
 
int main()
{
	const int MAX_N = 5;
	GraphNode *Graph[MAX_N];

	for  (int i =0; ineighbors.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[3]->neighbors.push_back(Graph[3]);

	for (int i = 0; i < MAX_N; i++)
	{
		for (int j = 0; j < Graph[i]->neighbors.size(); j++)
		{
			cout << Graph[i]->neighbors[j]->label;
		}
		cout << endl;
	}
	for (int i = 0; i < MAX_N; i++)
	{
		delete Graph[i];
	}
	system("pause");
	return 0;
}

图的深度优先遍历(DFS):

struct GraphNode
{
	int label;
	vector neighbors;
	GraphNode(int x) : label(x) {};
};

void DFS_Graph(GraphNode *node, int visit[]) {
	visit[node->label] = 1;
	cout << node->label << endl;
	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; ineighbors.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[3]->neighbors.push_back(Graph[3]);

	int visit[MAX_N] = { 0 };
	for (int i = 0; i < MAX_N; i++)
	{
		if (visit[i] == 0)
		{
			DFS_Graph(Graph[i], visit);
		}

	}
	for (int i = 0; i < MAX_N; i++)
	{
		delete Graph[i];
	}
	system("pause");
	return 0;
}

图的宽度优先遍历:

void BFS_Graph(GraphNode *node, int visit[])
{
	queue Q;
	Q.push(node);
	visit[node->label] = 1;
	while (!Q.empty())
	{
		GraphNode *node = Q.front();
		Q.pop();
		cout << node->label << endl;
		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;
			}
		}
	}
}

题目:

问题最终转变成为寻找图中是否有环得问题。有环则不能完成。

思路1:

深度优先搜索:如果某一顶点还未退出该顶点得深度搜索时,又回到了该顶点,证明有环。

struct GraphNode
{
	int label;
	vector neighbors;
	GraphNode(int x) : label(x) {};
};

class Solution {
public:
	bool canFinish(int numCourses, vector>& prerequisites) {
		vector graph;
		vector 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); // 课程2指向课程1
		}
		
		for (int i = 0; i < graph.size(); i++) {
			if (visit[i] == -1 && !DFS_graph(graph[i], visit)) {
				return false; // 如果没有DFS 进行 DFS,遇到环则结束
			}
		}
		for (int i = 0; i < numCourses; i++) {
			delete graph[i]; // 依次回收
		}
		return true; // 返回则可以完成
	}

bool DFS_graph(GraphNode *node, vector &visit) {
	visit[node->label] = 0; // -1没访问,0正在访问,1已访问
	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;
}
};

方法二: 拓扑排序(宽度优先搜索)

struct GraphNode
{
	int label;
	vectorneighbors;
	GraphNode(int x) : label(x) {};
};

class Solution {
public:
	bool canFinish(int numCourses, vector>& prerequisites) {
		vector graph; // 邻接表
		vector 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]++;
		}
		queueQ;
		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;
	}
};

你可能感兴趣的:(C++,数据结构,leetcode)