闻缺陷则喜何志丹

闻缺陷则喜何志丹

常用英文

最近公共祖先(Lowest Common Ancestor,简称LCA)
posterity,英语单词,主要用作名词,作名词时译为“子孙,后裔;后代”。

什么是深度优先搜索

深度优先搜索,Depth First Search, 简称DFS。它从初始节点出发,按预定的顺序扩展到下一个节点,然后从下一节点出发继续扩展新的节点,不断递归执行这个过程,直到某个节点不能再扩展下一个节点为止。此时,则返回上一个节点重新寻找一个新的扩展节点。如此搜索下去,直到找到目标节点,或者搜索完所有节点为止。
通俗的说:
每个节点都有选择类表,如果列表为空,则结束。否则依次DFS(x) , x ∈ \in 选择列表。节点中间的数字就是DFS序(时间戳)
深度优先搜索汇总-LMLPHP
某个节点为cur,它的任意祖先节点为anc,任意后代为pos。
深度优先有如下性质:
一,当DFS(cur)没有开始执行时,DFS(pos)一定没有开始执行。
二,当DFS(cur)执行结束时,DFS(pos)一定执行结束。
三,如果cur是长子节点,则DFS(cur)结束后。DFS(anc)正在执行,DFS(pos)已经执行结束。其它节点,都没有开始执行。
四,当前节点,可能是后代节点的前置条件,比如: 求层次(leve)。后代节点也可能是当前节点的前置条件,如:求后代数量。
五,DFS(cur)时的DFS序为time1 ,结束时的DFS序为time2。 则DFS序为[time1,time2)的节点    ⟺    \iff cur及cur的后代。

经典应用

无向图的DFS。需要cur节点parent,当next == parent 是忽略,否则无需循环{cur,parent,cur,parent ⋯ \cdots }
有向图,一般不需要,因为大部分情况是无环图。

封装类

枚举

CEnumNeiBo 枚举邻接点,如果有环,以第一次访问为准。CEnumChild 枚举孩子、无环、无向图。

class IDFSEnum
{
public:
	virtual void Enum(int cur, std::function<void(int)> fun) =0;
	virtual int NodeCount() = 0;
	virtual void Clear() = 0;
 };

class CEnumNeiBo : public IDFSEnum
{
public:
	CEnumNeiBo(const vector<vector<int>>& vNeiBo) :m_vNeiBo(vNeiBo) {
		Clear();
	}
	virtual void Clear() override{ m_vVisit.assign(m_vNeiBo.size(), false); };
protected:
	virtual void Enum(int cur, std::function<void(int)> fun) override {
		m_vVisit[cur] = true;
		for (const auto& next : m_vNeiBo[cur]) {
			if (m_vVisit[next]) { continue; }
			fun(next);
		}
	}
	virtual int NodeCount() { return m_vNeiBo.size(); };
	const vector<vector<int>>& m_vNeiBo;
	vector<int> m_vVisit;
};

class CEnumChild : public IDFSEnum
{
public:
	CEnumChild(const vector<vector<int>>& vChild) :m_vChild(vChild) {}
protected:
	virtual void Enum(int cur, std::function<void(int)> fun) override {
		for (const auto& next : m_vChild[cur]) {fun(next);}
	}
	virtual int NodeCount() { return m_vChild.size(); };
	virtual void Clear() override {};
	const vector<vector<int>>& m_vChild;
};

枚举各节点的孩子

class CDFSChild
{
public:
	const vector<vector<int>>& DFSChild(IDFSEnum& dfsEnum, int root) {
		dfsEnum.Clear();
		m_vChild.resize(dfsEnum.NodeCount());
		DFS(dfsEnum, root);
		return m_vChild;
	};
protected:
	void DFS(IDFSEnum& dfsEnum, int cur) {
		dfsEnum.Enum(cur, [&](int next) {m_vChild[cur].emplace_back(next); DFS(dfsEnum, next); });
	}
	vector<vector<int>> m_vChild;
};

计算各节点层次

class CDFSLeve 
{
public:
	vector<int>& DFSLeve(IDFSEnum& dfsEnum,int root) {
		dfsEnum.Clear();
		m_vLeve.assign(dfsEnum.NodeCount(), -1);
		m_vLeve[root] = 0;
		DFS(dfsEnum,root);
		return m_vLeve;
	}
protected:
	void DFS(IDFSEnum& dfsEnum, int cur) {
		dfsEnum.Enum(cur, [&](int next) {m_vLeve[next] = m_vLeve[cur] + 1; DFS(dfsEnum, next); });	}
	vector<int> m_vLeve;
};

暴力计算树直径

class CDFSForDiameter 
{
public:
	
	int DFSDiameter(IDFSEnum& dfsEnum, int root) {
		dfsEnum.Clear();
		m_iRet = 1;
		DFS(dfsEnum, root);
		return m_iRet;
	}
protected:
	int m_iRet = 1;//记录树的直径,0个节点的树会出错。
	int DFS(IDFSEnum& dfsEnum,int cur) {
		int left = 0;
		dfsEnum.Enum(cur, [&](int next) {
			auto right = DFS(dfsEnum,next);
			m_iRet = max(m_iRet, left + right + 1);
			left = max(left, right);
			});
		return left + 1;
	}
};

题解

深度优先搜索汇总-LMLPHP

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

深度优先搜索汇总-LMLPHP

05-16 09:10