1. 程式人生 > >牛客演算法進階(二)

牛客演算法進階(二)

二叉樹套路系列(樹型dp)

指在樹上的動態規劃;思路是:考慮左邊的答案、考慮右邊的答案、考慮整體的答案

最大搜索二叉樹

給定一棵二叉樹的頭節點head,請返回最大搜索二叉子樹的大小

思路:

以節點node為頭的樹中,最大的搜尋二叉子樹只可能來自一下兩種情況。

1.如果來自node左子樹上的最大搜索二叉子樹是以node.left為頭的;來自node右子樹的最大搜索二叉樹是以node.right為頭的;node左子樹上的最大搜索二叉子樹的最大值小於node。value;node右子樹的最大搜索二叉子樹的最小值大於node.value,那麼以節點node為頭的整棵樹都是搜尋二叉樹。

2.如果不滿足第一中情況,說明節點node為頭的整顆樹不能連成搜尋二叉樹。這種情況下,以node為頭的樹上的最大搜索二叉子樹來自node的左子樹上的最大搜索二叉子樹和來自右子樹上的最大搜索二叉子樹之間,節點較多的那個。
過程如下:
           1,整個過程是二叉樹的後序遍歷。(左右根)
           2,遍歷到當前節點記為cur時,先遍歷cur的左子樹收集4個資訊,分別是左子樹上最大搜索二叉子樹的頭節點(IBST) ,節點數(ISize),最小值(IMin),最大值(IMax).
              再遍歷cur的右子樹收集4個資訊,分別是右子樹上最大搜索二叉子樹的頭節點(RBST) ,節點數(RSize),最小值(RMin),最大值(RMax).
            3, 根據步驟2收集的資訊,判斷是否滿足第一種情況,如果滿足第一種情況就返回cur,如果滿足第二種情況,就返回IBST和RBST中較大的一個。
           4,可以使用全域性變數的方式實現步驟2中收集節點數,最大和最小的情況。

程式碼:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Node
{
public:

	Node(int data)
	{
		value = data;
		left = nullptr;
		right = nullptr;
	}
	int value;
	Node *left;
	Node *right;
};

class ReturnType
{
public:
	ReturnType(int a, Node *b, int c, int d)
	{
		size = a;
		head = b;
		min = c;
		max = d;
	}
	int size;//以該節點為子樹的最大二叉搜尋樹所含的節點總數
	Node* head;//以該節點為子樹最大二叉搜尋樹的頭節點
	int min;//以該節點為子樹的最小值
	int max;//以該節點為子樹的最大值
};
//返回值中有4個數據,size即為所求的最大值
ReturnType process(Node *head)
{
	if (head == nullptr)
		return ReturnType(0, nullptr, INT_MAX, INT_MIN);
	Node *left = head->left;
	ReturnType leftSubTressInfo = process(left);
	Node *right = head->right;
	ReturnType rightSubTressInfo = process(right);

	int includeItSelf = 0;
	//if如果滿足,那麼以head為根子樹屬於二叉搜尋樹
	if (leftSubTressInfo.head == left && rightSubTressInfo.head == right
		&& head->value > leftSubTressInfo.max && head->value < rightSubTressInfo.min)
	{
		includeItSelf = leftSubTressInfo.size + 1 + rightSubTressInfo.size;
	}
	int p1 = leftSubTressInfo.size;//左子樹的最大二叉搜尋樹所含的節點總數
	int p2 = rightSubTressInfo.size;//右子樹的最大二叉搜尋樹所含的節點總數
	int maxSize = max(max(p1, p2), includeItSelf);
	Node *maxHead = p1 > p2 ? leftSubTressInfo.head : rightSubTressInfo.head;
	if (maxSize == includeItSelf)//說明上面的if是成立的
		maxHead = head;
	return ReturnType(maxSize, maxHead, min(min(leftSubTressInfo.min, rightSubTressInfo.min), head->value),
		max(max(leftSubTressInfo.max, rightSubTressInfo.max), head->value));
}
int main(void)
{
	Node *head = new Node(6);
	Node *node1 = new Node(1);
	Node *node2 = new Node(0);
	Node *node3 = new Node(5);
	Node *node4 = new Node(8);
	Node *node5 = new Node(7);
	head->left = node1;
	node1->left = node2;
	node1->right = node3;
	head->right = node4;
	node4->left = node5;
	ReturnType result = process(head);
	cout << result.size << endl;
	system("pause");
	return 0;
}

二叉樹上的最遠距離

二叉樹中,一個節點可以往上走和往下走,那麼從節點A總能走到節點B。節點A走到節點B的距離為:A走到B最短路徑上的節點個數。求一棵二叉樹上的最遠距離。

思路:

求每個節點為頭的最大距離,有三種可能性:

1.以x為頭的整顆二叉樹的最遠距離來自於x的左子樹的最遠距離

2.以x為頭的整顆二叉樹的最遠距離來自於x的右子樹的最遠距離

3.如果經過x的最大距離就是左樹的最深的深度+1+右樹的最深的深度

程式碼:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
class Node
{
public:

	Node(int data)
	{
		value = data;
		left = nullptr;
		right = nullptr;
	}
	int value;
	Node *left;
	Node *right;
};

class ReturnType
{
public:
	ReturnType(int m, int h)
	{
		maxDistance = m;
		height = h;
	}
	int maxDistance;//最遠距離
	int height;//高度
};
ReturnType process(Node *head)
{
	if (head == nullptr)
		return ReturnType(0, 0);
	ReturnType leftReturnType = process(head->left);
	ReturnType rightReturnType = process(head->right);
	int includeHeadDistance = leftReturnType.height + 1 + rightReturnType.height;
	int p1 = leftReturnType.maxDistance;
	int p2 = rightReturnType.maxDistance;
	int resultDistance = max(max(p1, p2), includeHeadDistance);
	int hitself = max(leftReturnType.height, leftReturnType.height) + 1;
	return ReturnType(resultDistance, hitself);
}
int main(void)
{
	Node *head = new Node(6);
	Node *node1 = new Node(1);
	Node *node2 = new Node(0);
	Node *node3 = new Node(5);
	Node *node4 = new Node(8);
	Node *node5 = new Node(7);
	head->left = node1;
	node1->left = node2;
	node1->right = node3;
	head->right = node4;
	node4->left = node5;
	ReturnType result = process(head);
	cout << result.maxDistance << endl;
	system("pause");
	return 0;
}

員工活躍度問題

一個公司的上下節關係是一棵多叉樹,這個公司要舉辦晚會,你作為組織者已經摸清了大家的心理:一個員工的直 接上級如果到場,這個員工肯定不會來。每個員工都有一個活躍度的值,決定誰來你會給這個員工發邀請函,怎麼 讓舞會的氣氛最活躍?返回最大的活躍值。
舉例:
給定一個矩陣來表述這種關係
matrix = { 1,6 1,5 1,4 }
這個矩陣的含義是:
matrix[0] = {1 , 6},表示0這個員工的直接上級為1,0這個員工自己的活躍度為6
matrix[1] = {1 , 5},表示1這個員工的直接上級為1(他自己是這個公司的最大boss),1這個員工自己的活躍度 為5
matrix[2] = {1 , 4},表示2這個員工的直接上級為1,2這個員工自己的活躍度為4
為了讓晚會活躍度最大,應該讓1不來,0和2來。最後返回活躍度為10

思路:

對於這顆多叉樹中的每個節點均有兩種情況:

1.該節點來參加晚會,那麼它的直接下級節點均不來參加。此時該節點的最大活躍值就是所有直接節點不來的活躍值相加。

2.該節點不來參加晚會,那麼它的直接下級節點可以來也可以不來。此時該節點的最大活躍值是其直接節點中來或不來的活躍值中的較大值相加。

程式碼

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
void process(vector<vector<int>> &matrix, vector<vector<int>> &dp,
	vector<bool> &visited, int root);

int maxHappy(vector<vector<int>> &matrix)
{
	int m = matrix.size();
	vector<vector<int>> dp(m, vector<int>(2));
	vector<bool> visited(m, false);
	int root = 0;
	//尋找多叉樹的根
	for (int i = 0; i < m;++i)
		if (i == matrix[i][0])
		{
			root = i;
			break;
		}
	process(matrix, dp, visited, root);
	return max(dp[root][0], dp[root][1]);
}
void process(vector<vector<int>> &matrix, vector<vector<int>> &dp,
	vector<bool> &visited, int root)
{
	visited[root] = true;
	//dp[root][1]表示root去,dp[root][0]表示root不去
	dp[root][1] = matrix[root][1];//如果root去,matrix[root][1]是root的活躍度
	dp[root][0] = 0;//如果root不去,那麼活躍度自然為0
	for (int i = 0; i < matrix.size(); ++i)
	{
		//若i的直接領導是root,且還未訪問過i
		if (matrix[i][0] == root && !visited[i])
		{
			process(matrix, dp, visited, i);
			dp[root][1] += dp[i][0];//root去,那麼它的直接下屬都不能去
			dp[root][0] += max(dp[i][1], dp[i][0]);//root去,那麼它的直接下屬可以去也可以不去
		}
	}
}
int main(void)
{
	vector<vector<int>> v = { { 1, 8 },{ 1, 9 },{ 1, 10 },{0, 3},{0, 11} };
	int result = maxHappy(v);
	cout << result << endl;
	system("pause");
	return 0;
}

判斷平衡二叉樹

思路

1.左邊如果不是平衡二叉樹,直接返回false;如果是返回深度和true(代表是)

2.右邊如果不是平衡二叉樹,直接返回false;如果是返回深度和true(代表是)

3.判斷左右子樹高度差是否大於1,如果是不是平衡二叉樹

程式碼:

#include<iostream>
#include<vector>
#include <cmath>
#include<algorithm>
using namespace std;
void process(vector<vector<int>> &matrix, vector<vector<int>> &dp,
	vector<bool> &visited, int root);
class Node
{
public:
	Node(int data)
	{
		value = data;
		left = nullptr;
		right = nullptr;
	}
	int value;
	Node *left;
	Node *right;
};
class ReturnType
{
public:
	ReturnType(int l, bool is)
	{
		level = l;
		isB = is;
	}
	int level;
	bool isB;
};
ReturnType process(Node *head, int level) {
	if (head == nullptr) 
	{
		return ReturnType(level, true);
	}
	ReturnType leftSubTreeInfo = process(head->left, level + 1);
	if (!leftSubTreeInfo.isB) 
	{
		return ReturnType(level, false);
	}
	ReturnType rightSubTreeInfo = process(head->right, level + 1);
	if (!rightSubTreeInfo.isB) 
	{
		return ReturnType(level, false);
	}
	if (abs(rightSubTreeInfo.level - leftSubTreeInfo.level) > 1) {
		return  ReturnType(level, false);
	}
	return ReturnType(max(leftSubTreeInfo.level, rightSubTreeInfo.level), true);
}
int main(void)
{
	Node *head = new Node(6);
	Node *node1 = new Node(1);
	Node *node2 = new Node(0);
	Node *node3 = new Node(5);
	Node *node4 = new Node(8);
	Node *node5 = new Node(7);
	Node *node6 = new Node(9);
	head->left = node1;
	node1->left = node2;
	node1->right = node3;
	head->right = node4;
	node3->right = node6;
	ReturnType result = process(head,0);
	if (result.isB)
		cout << "is" << endl;
	else
		cout << "no" << endl;
	system("pause");
	return 0;
}