二叉树类问题框架

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二叉树类问题框架相关的知识,希望对你有一定的参考价值。

前言

花些时间,把这段时间刷的题目分门别类的记录一下基础框架,留待后来人。
每个人的路不同,对我来说,不见得要把大部分时间投入到算法当中,我还是更喜欢系统框架搭建以及底层原理这种大开大合的功法吧。

系列开头先来个最简单的二叉树,使用此框架的前提是:你能把实际问题抽象成为二叉树问题。题目也好,现实问题也罢,都是会有“伪装”的。


解决方案

void traverse(TreeNode* head){
	if(head == nullptr){
		return;
	}
	//前序遍历
	traverse(head->left);
	//中序遍历
	traverse(head->right);
	//后序遍历
}

应用示例

二叉树最大路径和

后序遍历、

int ans;
int max_lenth(TreeNode* head){
	if(head == NULL){
		return 0;
	}
	
	ans += max(max_length(head->left),max_length(head->right) ) ;

	return ans;
}

这是一个后续遍历写法。


前、中序遍历还原二叉树

前序遍历、

//通过前序遍历和中序遍历还原二叉树
TreeNode* rebuild_tree(TreeNode* head, 
						vector<int> vec1, int preb, int pree, 
						vector<int> vec2, int mids, int mide)
{
	if(mids == mide){
		return nullptr;
	}
	
	head = new TreeNode(vec1[preb]);
	
	//在中序遍历队列之中将左右分开
	int i = mids;
	for(;i<mide;i++){
		if(vec2[i] == vec1[preb]){
			break;
		}
	}
	
	
	//做一个赋值构造函数
	head->left = rebuild_tree(vec1, pres, i-1, vec2, mids, i-1);
	head->right = rebuild_tree(vec1, i+1, pree, vec2, i+1, mid2);

	return head;
}

判断两棵二叉树完全相同

前序遍历、

bool isSameTree(TreeNode* root1, TreeNode* root2){
	if(root1 == root2){return true;}
	if(root1 == nullptr || root2 == nullptr){return false;}
	if(root1->val != root->val){return false;}
	
	return isSameTree(root1->left, root2->left) && isSameTree(root1->right, root2->right);
	//这个 && 很巧妙哦
}

判断BST的合法性

对于这道题,我想说,不是把握了框架想出了解法,而是把握了框架看懂了代码。。。
反正你让我想,我就只会前序遍历全读到数组里然后判断。。。

#include<iostream>

int ans;

class TreeNode {
public:
	TreeNode(int val) {
		this->val = val;
		left = NULL;
		right = NULL;
	}

public:
	int val;
	TreeNode* left;
	TreeNode* right;
};

bool isValidBST(TreeNode* root, int max, int min) {

	if (root == nullptr) { return true; }

	std::cout << root->val << ' ' << max << ' ' << min << std::endl;

	if (root->val <= min) { return false; }
	if (root->val >= max) { return false; }

	return isValidBST(root->left,root->val,min) && isValidBST(root->right, max,root->val);
}

bool isValidBST(TreeNode* root) {
	return isValidBST(root, INT_MAX, INT_MIN);
}

int main() {
	TreeNode* a = new TreeNode(1);
	TreeNode* b = new TreeNode(2);
	TreeNode* c = new TreeNode(3);
	TreeNode* d = new TreeNode(4);
	TreeNode* e = new TreeNode(5);
	TreeNode* f = new TreeNode(6);
	TreeNode* g = new TreeNode(7);

	d->left = b;
	b->left = a;
	b->right = new TreeNode(0);
	d->right = f;
	f->left = e;
	f->right = g;

	std::cout << isValidBST(d);
	return 0;
}

BST 遍历框架

相比于二叉树,二叉搜索树的遍历可以做一些优化,没必要每个节点都去转一圈。

void BST(TreeNode* root, int target){
	if(root == nullptr){return;}
	
	if(root->val == target){
		//dosomething
	}
	
	if(root->val > target){
		BST(root->left, target);
	}
	else{
		BST(root->left, target);
	}
}

完全二叉树的节点数量

满二叉树的节点数量有公式,直接算。

int full_tree(TreeNode* root){
	int h = 0;
	while(root != null){
		root = root->left;
		++h;
	}
	
	return pow(2, h)-1;
}

普通二叉树就比较尴尬了:

int commen_tree(TreeNode* root){
	if(root == null){
		return 0;
	}
	return 1 + commen_tree(root->left) + commen_tree(root->right);
}

一棵完全二叉树,只有三种可能嘛,要么是一棵满二叉树(图一),要么是一堆满二叉树的结合(图二),要么是两个节点的斜树加上一堆的满二叉树结合(图三)。

所以嘛,直接拆:

int count_node(TreeNode* root){
	TreeNode* l = root, r = root;
	
	while(l != nullptr && r != nullptr){
		l = l->left;
		r = r->right;
		hl++;
	}
	
	//如果左右子树高度相同
	if(l == nullptr && r == nullptr){
		return full_tree(root);
	}
	
	else{	//那就是不相同咯
		return 1+count_node(root->left)+count_node(root->right);
	}
}

二叉树节点公共祖先问题

这个嘛,画个图,很自然就想到了后序遍历。

不过这也只是第一步啦,要把节点层层递交上去,所以在遍历的时候有以下几种情况:

1、某节点的所有子节点没有符合条件的
2、某节点就是那俩节点的祖先节点
3、某节点是其中一个节点的祖先节点

情况一很简单,向上递交 null。
情况二其实没那么简单,向上递交,然后呢?情况二本来是可以用两个节点来判断的,但是递交之后,只有一个节点是有值的,另一个节点注定是空。那就和情况三一样了。

所以情况三的处理间接也就是帮情况二在善后了。那么,情况三,将有值的节点递交上去,直到第一层,就OK啦。


TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q){
	if(root == nullptr || root == p || root ==q){
		return root;
	}
	
	TreeNode* left = lowestCommonAncestor(root->left, p, q);
	TreeNode* right = lowestCommonAncestor(root->right, p, q);
	
	if(left && right){
		return root;
	}
	
	if(!left && !right){
		return nullptr;
	}
	
	return right == nullptr?left:right;
}

以上是关于二叉树类问题框架的主要内容,如果未能解决你的问题,请参考以下文章

对二叉树类 C++ 函数的未定义引用

二叉树类的实现

二叉树类高频算法题golang实现

二叉树类高频算法题golang实现

用java实现二叉树的遍历算法

C站万字详解二叉树基本操作演示程序(附图)