高级数据结构 | 二叉树查询 —层次、深度、结点数、双亲结点、公共祖先、路径总和 ...



二叉树:
.
A
B
G
C
D
H
E
F
一、查询二叉树第K层的数据元素

假设根结点所在的层数为第一层,则左右孩子所在的层为第二层,层数依次向下递归增长。

实现给定二叉树层数,打印此层的所有数据元素。

递归实现
void PrintK_r(struct BtNode* p, int k)
{
	if (k == 0 && p != NULL)
	{
		std::cout << p->data << " ";
	}
	else if (p != NULL)
	{
		PrintK_r(p->leftchild, k - 1);
		PrintK_r(p->rightchild, k - 1);
	}
}
void Print_KLevel_Item_R(struct BtNode* p, int k)
{
	if (NULL == p || k <= 0)	return;
	PrintK_r(p, k - 1);
	std::cout << std::endl;
}
非递归实现
void PrintK(struct BtNode* p, int k)
{
	std::queue<struct BtNode*> qua, qub;
	qua.push(p);	// 第一层(根结点)存入a队列
	int i = 1;		// 记录当前层与待输出层的层差
	while (!qua.empty() || !qub.empty())
	{
		while (!qua.empty())    // a队列中元素的下一层 存入b队列
		{
			p = qua.front(); qua.pop();	
			if (i == k)
			{
				std::cout << p->data << " ";
			}
			else
			{
				if (p->leftchild != NULL)
				{
					qub.push(p->leftchild);
				}
				if (p->rightchild != NULL)
				{
					qub.push(p->rightchild);
				}
			}

		}
		i++;

		while (!qub.empty())    // b队列中元素的下一层 存入a队列
		{
			p = qub.front(); qub.pop();
			if (i == k)
			{
				std::cout << p->data << " ";
			}
			else
			{
				if (p->leftchild != NULL)
				{
					qua.push(p->leftchild);
				}
				if (p->rightchild != NULL)
				{
					qua.push(p->rightchild);
				}
			}
		}
		i++;
	}
	std::cout << std::endl;
}
void Print_KLevel_Item(struct BtNode* p,int k)
{
	if (NULL == p || k <= 0)	return;
	PrintK(p, k);
}
输出测试
int main() {

	const char* pstr = "ABC##DE##F##G#H##";

	BinaryTree root = NULL;
	root = CreateTree1(&pstr);
	strPreOrder(root);	printf("\n");		// 先序创建字符串
	std::cout << "---------------" << std::endl;

	/* 层序遍历 */
	LeveOrder(root);

	int i = 0;
	while (std::cin >> i, i != -1)		// 输入 -1 结束测试
	{
		Print_KLevel_Item(root, i);		// 非递归
		Print_KLevel_Item_R(root, i);	// 递归
	}

	std::cout << std::endl;
	return 0;
	
}	

运行结果:

ABC##DE##F##G#H##
---------------
A B G C D H E F
0		// 输入
1		// 输入
A
A
2		// 输入
B G
B G
3		// 输入
C D H
C D H
4		// 输入
E F
E F
5		// 输入


6		// 输入


-1		// 输入 -1

二、查询二叉树的深度和结点数

假设树的根结点深度为0,处于第一层。它的孩子深度为1,处于第二层。那么我们求这个树的深度和结点个数。

2.1 树的深度
/* 二叉树深度 */
int TreeDepth(struct BtNode* p)
{
	if (p == NULL) return -1;	// NULL 的父节点为叶子结点
	else
	{
		int l = TreeDepth(p->leftchild);
		int r =	TreeDepth(p->rightchild);
		return (l > r ? l : r) + 1;
	}
}
int GetDepth(struct BtNode* p)
{
	if (NULL == p) return -1;
	return TreeDepth(p);
}

简化:

int GetDepth2(struct BtNode* p)
{
	if (p == NULL) return -1;
	else return std::max(GetDepth2(p->leftchild), GetDepth2(p->rightchild)) + 1;
}

说明:当传入根节点指针为NULL时,输出 -1 。

2.2 树的结点数
/* 二叉树结点个数 */
int TreeSize(struct BtNode* p)
{
	if (p->leftchild == NULL && p->rightchild == NULL) return 1;
	else
	{
		int sum = 1;
		if (p->leftchild != NULL)
		{
			sum += TreeSize(p->leftchild);
		}
		if (p->rightchild != 0)
		{
			sum += TreeSize(p->rightchild);
		}
		return sum;
	}
}
int GetSize(struct BtNode* p)
{
	if (NULL == p) return 0;
	return TreeSize(p);
}

简化:

int GetSize2(struct BtNode* p)
{
	if (p == NULL) return 0;
	else return GetSize2(p->leftchild) + GetSize2(p->rightchild) + 1;
}

说明:传入空的根节点(NULL),输出结点个数为 0 。

三、查询二叉树中元素位置

在二叉树中查询value值,如果存在返回节点的地址,否则返回NULL 。

二叉树中查找元素可以理解为二叉树遍历中的特殊情况,因此查找的算法有很多,这里以深度优先遍历的先序遍历为例 。

递归算法:

struct BtNode* FindValue(struct BtNode* p, ElemType val)
{
	if (p == NULL || p->data == val) return p;
	else
	{
		struct BtNode* pos = NULL;
		pos = FindValue(p->leftchild, val);
		if (pos == NULL)
		{
			pos = FindValue(p->rightchild, val);
		}
		return pos;
	}
}

非递归算法:

struct BtNode* NiceFindValue(struct BtNode* p, ElemType val)
{
	if (p == NULL || p->data == val) return p;

	std::stack<struct BtNode*> st;
	while (!st.empty() || p != NULL)			// 最终的退出条件
	{
		while (p != NULL)						// 一直向左遍历左子树
		{
			if (p->data == val) return p;		// 在遍历时,每一步访问的都是根,可以输出
			st.push(p);
			p = p->leftchild;
		}		/* while 结束  表示,此结点没有左孩子 */
		p = st.top();	st.pop();				// 回退至父节点
		p = p->rightchild;						// 试探右结点
	}
	return p;

}
四、查询结点的双亲

给定一个二叉树,找到该树中某个指定节点的双亲。

/*                      二叉树             指定孩子结点    */
struct BtNode* Parent1(struct BtNode* p, struct BtNode* child)
{
	if (p == NULL || p == child)	return p;	// 把查询到的子节点返回上层调用进行判断
	else
	{
		struct BtNode* pos = NULL;
		pos = Parent1(p->leftchild, child);
		if (pos == NULL)	/* 左子树没找到孩子 */
		{
			pos = Parent1(p->rightchild, child);
			if (pos == child)	pos = p;	/* 右子树找到孩子 */
		}
		else if(p->leftchild == child) pos = p;	/* 左子树找到孩子 */
		
		return pos;		/* pos如果找到就返回pos,没有找到就为NULL */

	}
}
struct BtNode* Parent2(struct BtNode* p, struct BtNode* child)
{							// 直接在当前结点判断子节点是否找到
	if (p == NULL || p->leftchild == child || p->rightchild == child) return p;
	else
	{
		struct BtNode* pos = NULL;
		pos = Parent2(p->leftchild, child);
		if (pos == NULL)
		{
			pos = Parent2(p->rightchild, child);
		}
		return pos;
	}
}
struct BtNode* FindParent(struct BtNode* p, struct BtNode* child)
{
	if (NULL == p || NULL == child || p == child)
	{
		return NULL;
	}
	else return Parent1(p, child);	
	//else return Parent2(p, child);
}
五、查询两结点的公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
力扣原题:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

注意:

  1. 如果所给两结点在二叉树中不存在,返回NULL 。
  2. 如果所给两结点,其中一个结点为NULL,则返回另一个结点 。
  3. 如果所给两结点,是同一个结点,则返回该结点本身。
  4. 如果所给两结点,一个结点本身就是另一个结点的祖先结点,则返回该祖先结点本身。
struct BtNode* FindAncestor(struct BtNode* root, struct BtNode* p, struct BtNode* q)
{
	if (root == NULL || root == p || root == q) return root;

	struct BtNode* left = FindAncestor(root->leftchild, p, q);
	struct BtNode* right = FindAncestor(root->rightchild, p, q);

	if (left != NULL && right != NULL) return root;		// 在左右子树中找到p、q
	if (left != NULL) return left;			// 只有左子树找到
	if (right != NULL) return right;		// 只有右子树找到
	return NULL;		// 没有找到
}
struct BtNode* lowestCommonAncestor(struct BtNode* root, struct BtNode* p, struct BtNode* q)
{
	if (NULL == p) return q;
	if (NULL == q) return p;
	return FindAncestor(root, p, q);
}
六、查询二叉树的路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
力扣原题:https://leetcode-cn.com/problems/path-sum/comments/

bool hasPathSum(struct BtNode* root, int sum)
{
	if (root == NULL) return false;
	if (root->leftchild == NULL && root->rightchild == NULL)   // 叶子结点
	{
		if (root->data == sum) return true;
		else return false;
	}

	bool res = false;
	/*
	*	//位运算符 ‘|’ ,有一个为真,结果为真。 
	*	//真:	true|true 、false|true、true|false
	*	//假:	false|false
	*	res = res | PathSum(root->leftchild, sum - root->data); 
	*	res = res | PathSum(root->rightchild, sum - root->data);
	*/
	res = res | hasPathSum(root->leftchild, sum - root->data);   // 左子树查询
	res ? res : res |= hasPathSum(root->rightchild, sum - root->data);	// 如果左子树查询到,就不需要再查找右子树。

	return res;
}
  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫RT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值