树状DP 笔记

前言

树状DP 要比 在数组上或在矩阵上的 DP 要简单,因为,在树的结构上,总是从子节点推到父节点的。

树状DP 难点是 由可能性得到的返回值类型。

 

在一棵树中,找结点值的最大和最小

可以对树进行一次遍历,在树中找到最大最小值。

我们也可以用树状DP来完成。当前结点的最值 由当前结点的值,和左孩子的最值 和 右孩子的最值决定。

struct Node{
	int value;
	Node *left = NULL;
	Node *right = NULL;
	Node(int data){
		value = data;
	}
};
struct ReturnData
{
	int max, min;
	ReturnData(int Min, int Max){
		min = Min;
		max = Max;
	}
};


ReturnData process(Node *head)
{
	if (head == NULL){
		return ReturnData(INT_MAX, INT_MIN);
	}//if
	ReturnData leftData = process(head->left);
	ReturnData rightData = process(head->right);

	return ReturnData(
		min(min(leftData.min, rightData.min),head->value),
		max(max(leftData.max, rightData.max), head->value));
}

void PrintTree(Node *root, int nLayer)
{
	if (root == NULL){
		return;
	}

	PrintTree(root->right, nLayer + 3);
	for (int i = 0; i < nLayer; i++){
		printf(" ");
	}
	printf("%d\n", root->value);
	PrintTree(root->left, nLayer + 3);
}

void DeteleNode(Node* head)
{
	if (head == NULL){
		return;
	}
	DeteleNode(head->left);
	DeteleNode(head->right);
	delete head;
}

int main()
{
	Node* head = new Node(6);
	head->left = new Node(1);
	head->left->left = new Node(1);
	head->left->right = new Node(3);
	head->right = new Node(12);
	head->right->left = new Node(10);
	head->right->left->left = new Node(4);
	head->right->left->left->left = new Node(2);
	head->right->left->left->right = new Node(5);
	head->right->left->right = new Node(14);
	head->right->left->right->left = new Node(11);
	head->right->left->right->right = new Node(15);
	head->right->right = new Node(13);
	head->right->right->left = new Node(20);
	head->right->right->right = new Node(16);

	PrintTree(head,0);
	ReturnData result = process(head);
	cout << result.max << " " << result.min << endl;

	DeteleNode(head);
	return 0;
}

 

 

在一棵树中,求其中的最大二叉搜索子树

子树的概念:在一个根结点下,下边的后代结点都要有。

拓扑结构:在一个根结点下,下边的后代结点不一定都有。

题解:从小到大找子树是否为二叉搜索子树。在当前结点中,看看结点左孩子的最大二叉搜索子树 , 和结点的右孩子的最大二叉搜索子树,取最大值。如果当前结点左孩子的最大二叉搜索子树的头结点是它本身,当前结点右孩子的最大二叉搜索子树的头结点是它本身,说明可能要产生更大的二叉搜索子树了。

struct Node{
	int value;
	Node *left = NULL;
	Node *right = NULL;
	Node(int data){
		value = data;
	}
};
struct ReturnType
{
	int size;
	Node *head;
	int min, max;
	ReturnType(int a, Node *b, int c, int d){
		size = a;
		head = b;
		min = c;
		max = d;
	}
};
ReturnType process(Node *head)
{
	if (head == NULL){
		return ReturnType(0, NULL, INT_MAX, INT_MIN);
	}
	Node *left = head->left;
	ReturnType leftSubTreeInfo = process(left);
	Node *right = head->right;
	ReturnType rightSubTreeInfo = process(right);

	int includeltSelf = 0;
	if (leftSubTreeInfo.head == left
		&& rightSubTreeInfo.head == right
		&& head->value > leftSubTreeInfo.max
		&& head->value < rightSubTreeInfo.min
		){
		includeltSelf = leftSubTreeInfo.size + rightSubTreeInfo.size + 1;
	}//if

	int p1 = leftSubTreeInfo.size;
	int p2 = rightSubTreeInfo.size;
	int maxSize = max(max(p1, p2), includeltSelf);

	Node* maxHead = p1 > p2 ? leftSubTreeInfo.head : rightSubTreeInfo.head;
	if (maxSize == includeltSelf){
		maxHead = head;
	}//if

	return ReturnType(maxSize, maxHead,
		min(min(leftSubTreeInfo.min, rightSubTreeInfo.min), head->value),
		max(max(leftSubTreeInfo.max, rightSubTreeInfo.max), head->value));
}

void PrintTree(Node *root, int nLayer)
{
	if (root == NULL){
		return;
	}

	PrintTree(root->right, nLayer + 3);
	for (int i = 0; i < nLayer; i++){
		printf(" ");
	}
	printf("%d\n", root->value);
	PrintTree(root->left, nLayer + 3);
}

void DeteleNode(Node* head)
{
	if (head == NULL){
		return;
	}
	DeteleNode(head->left);
	DeteleNode(head->right);
	delete head;
}

int main()
{
	Node* head = new Node(6);
	head->left = new Node(1);
	head->left->left = new Node(0);
	head->left->right = new Node(3);
	head->right = new Node(12);
	head->right->left = new Node(10);
	head->right->left->left = new Node(4);
	head->right->left->left->left = new Node(2);
	head->right->left->left->right = new Node(5);
	head->right->left->right = new Node(14);
	head->right->left->right->left = new Node(11);
	head->right->left->right->right = new Node(15);
	head->right->right = new Node(13);
	head->right->right->left = new Node(20);
	head->right->right->right = new Node(16);

	PrintTree(head, 0);
	ReturnType bst = process(head);
	cout << endl << endl;
	PrintTree(bst.head, 0);

	DeteleNode(head);
	return 0;
}

求一个树上的最远距离

节点A 走到 节点B 的距离为:A 走到 B最短路径上的节点个数。

题解:返回类型为由该节点和该节点的所有子孙节点构成的子树 的 最远距离, 和该结点的高度。

struct Node{
	int value;
	Node *left = NULL;
	Node *right = NULL;
	Node(int data){
		value = data;
	}
};
struct ReturnType
{
	int maxDistance;
	int h;
	ReturnType(int a, int b) :maxDistance(a), h(b){}
};

ReturnType process(Node *head)
{
	if (head == NULL){
		return ReturnType(0,0);
	}//if
	ReturnType leftReturnType = process(head->left);
	ReturnType rightReturnType = process(head->right);
	int includeHeadDistance = leftReturnType.h + rightReturnType.h + 1;
	int p1 = leftReturnType.maxDistance;
	int p2 = rightReturnType.maxDistance;
	int resultDistance = max(max(p1, p2), includeHeadDistance);
	int curHeight = max(leftReturnType.h, rightReturnType.h) + 1;
	
	return ReturnType(resultDistance,curHeight);
}

void PrintTree(Node *root, int nLayer)
{
	if (root == NULL){
		return;
	}

	PrintTree(root->right, nLayer + 3);
	for (int i = 0; i < nLayer; i++){
		printf(" ");
	}
	printf("%d\n", root->value);
	PrintTree(root->left, nLayer + 3);
}

void DeteleNode(Node* head)
{
	if (head == NULL){
		return;
	}
	DeteleNode(head->left);
	DeteleNode(head->right);
	delete head;
}

int main()
{
	Node* head = new Node(6);
	head->left = new Node(1);
	head->left->left = new Node(1);
	head->left->right = new Node(3);
	head->right = new Node(12);
	head->right->left = new Node(10);
	head->right->left->left = new Node(4);
	head->right->left->left->left = new Node(2);
	head->right->left->left->right = new Node(5);
	head->right->left->right = new Node(14);
	head->right->left->right->left = new Node(11);
	head->right->left->right->right = new Node(15);
	head->right->right = new Node(13);
	head->right->right->left = new Node(20);
	head->right->right->right = new Node(16);

	PrintTree(head,0);
	ReturnType result = process(head);
	cout << result.maxDistance << " " << result.h << endl;

	DeteleNode(head);
	return 0;
}

最大高兴度

题目:

公司开party ,有这样一个规定,一个员工来了,那么它的直接上级和直接下级不可以来,每个员工对参加party 都有各自的高兴度。给你一个树状的员工职位表,要你决定谁来,谁不来,并且总高兴度要最大。

题解:

每个节点上有两种可能性,1.该节点来了,以这个节点为根的子树的最大高兴度。

2.该节点没来了,以这个节点为根的子树的最大高兴度。

所以我们的返回数据定义为了

struct ReturnType
{
	int come_MaxHappy;
	int notCome_MaxHappy;
	ReturnType(int a = 0, int b = 0) :come_MaxHappy(a), notCome_MaxHappy(b){}
};

再由这个返回数据 推出 它的祖先节点的返回数据。

 

struct Node{
	int happyFactor;
	vector<Node*> nexts;
	Node(int a){
		happyFactor = a;
	}
};
struct ReturnType
{
	int come_MaxHappy;
	int notCome_MaxHappy;
	ReturnType(int a = 0, int b = 0) :come_MaxHappy(a), notCome_MaxHappy(b){}
};

ReturnType process(Node *head)
{
	int come_MaxHappy = 0;
	int notCome_MaxHappy = 0;
	Node* next = NULL;
	ReturnType nextData;

	for (int i = 0; i < head->nexts.size(); i++){
		next = head->nexts[i];
		nextData = process(next);
		come_MaxHappy += nextData.notCome_MaxHappy;
		notCome_MaxHappy += max(nextData.come_MaxHappy, nextData.notCome_MaxHappy);
	}//for
	return ReturnType(come_MaxHappy,notCome_MaxHappy);
}

 

判断一棵树是否为平衡二叉树

题解:可能性,返回一个树是否是平衡的,以及该树的高度。它的父节点拿到这些信息后,如果它的子树是不平衡的,那么,整棵树一定不平衡。如果它的子树是平衡的, 那么我们用 右子树的高度减去 左子树的高度...

struct Node{
	int value;
	Node *left = NULL;
	Node *right = NULL;
	Node(int a){
		value = a;
	}
};
struct ReturnType
{
	bool isBalance;
	int h;
	ReturnType(bool a, int b){
		isBalance = a;
		h = b;
	}
};

ReturnType process(Node *head)
{
	if (head == NULL){
		return ReturnType(true, 0);
	}
	ReturnType leftData = process(head->left);
	if (leftData.isBalance == false){
		return ReturnType(false, 0);
	}//if

	ReturnType rightData = process(head->right);
	if (rightData.isBalance == false){
		return ReturnType(false, 0);
	}//if
	if (abs(rightData.h - leftData.h) > 1){
		return ReturnType(false, 0);
	}//if
	return ReturnType(true, max(leftData.h, rightData.h) + 1);
}

void PrintTree(Node *root, int nLayer)
{
	if (root == NULL){
		return;
	}

	PrintTree(root->right, nLayer + 3);
	for (int i = 0; i < nLayer; i++){
		printf(" ");
	}
	printf("%d\n", root->value);
	PrintTree(root->left, nLayer + 3);
}

void DeteleNode(Node* head)
{
	if (head == NULL){
		return;
	}
	DeteleNode(head->left);
	DeteleNode(head->right);
	delete head;
}

int main()
{
	Node* head = new Node(6);
	head->left = new Node(1);
	head->left->left = new Node(1);
	head->left->right = new Node(3);
	head->right = new Node(12);
	head->right->left = new Node(10);
	head->right->left->left = new Node(4);
	head->right->left->left->left = new Node(2);
	head->right->left->left->right = new Node(5);
	head->right->left->right = new Node(14);
	head->right->left->right->left = new Node(11);
	head->right->left->right->right = new Node(15);
	head->right->right = new Node(13);
	head->right->right->left = new Node(20);
	head->right->right->right = new Node(16);

	PrintTree(head, 0);
	ReturnType result = process(head);
	cout << endl << endl;
	cout << result.isBalance << endl;

	DeteleNode(head);
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值