算法分析與設計課程作業第十五週#1#2#3
這次選了兩道與期中考題目相似的題目以及一道動態規劃題來做。
98. Validate Binary Search Tree
Given a binary tree, determine if it is a valid binary search tree (BST).
Assume a BST is defined as follows:
The left subtree of a node contains only nodes with keys less than the node’s key.
The right subtree of a node contains only nodes with keys greater than the node’s key.
Both the left and right subtrees must also be binary search trees.
Example 1:
2
/ \
1 3
Binary tree [2,1,3], return true.
Example 2:
1
/ \
2 3
Binary tree [1,2,3], return false.
相似的期中考題目
輸入兩個二叉樹T1和T2,要求對T1和T2進行合併. 合併是指將二叉樹同一位置節點上的數求和,作爲合併後二叉樹相應位置節點的數值. 如果某個位置上只有一個二叉樹有節點,則合併後的二叉樹對應位置上的節點上的數值就等於這個節點上的數值.
例如:
T1和T2的結點數均不多於100000,每個結點的數值在1和1000之間.
請爲下面的Solution類實現解決上述問題的merge函數,函數的兩個參數T1和T2分別代表兩個二叉樹的根節點,函數返回值爲合併後的二叉樹的根節點.
思路
這道題有一個簡單的思路就是中序遍歷該樹,看能否得到一個有序的序列。
不過,期中考有道類似題(如上,都是對樹的操作)用了分而治之的方法,所以也想試試這道題能不能用類似的思路。
期中考題代碼:
class Solution {
public:
TreeNode* merge(TreeNode* T1, TreeNode* T2) {
if(T1 != NULL && T2 != NULL){
TreeNode * root = new TreeNode(T1->val + T2->val);
root->left = merge(T1->left, T2->left);
root->right = merge(T1->right, T2->right);
return root;
}
else if(T1 == NULL){
return T2;
}
else{
return T1;
}
}
};
由此,可類似地想到,判斷一棵樹是否爲二叉搜索樹,可分別判斷以其根節點的兩個子節點爲根節點的子樹是否爲二叉搜索樹以及是否左子節點值小於根節點值小於右子節點值。
不過,還有個問題就是,就算兩棵子樹爲二叉搜索樹,該樹仍未足夠成爲一棵二叉搜索樹,還有一個要求就是左子樹的右子節點(左子樹擁有最大值的節點)的值比樹的根節點值小,右子樹的左子節點(右子樹擁有最小值的節點)的值比樹的根節點值大。由此,代碼如下:
代碼
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool isValidBST(TreeNode* root,long long min = (long long)INT_MIN -1, long long max = (long long)INT_MAX + 1) {
if(root == NULL) return true;
if(root->left != NULL&& (root->left->val >= root->val || root->left->val <= min)){
return false;
}
if(root->right != NULL&& (root->right->val <= root->val || root->right->val >= max)){
return false;
}
return isValidBST(root->left, min, root->val)&&isValidBST(root->right, root->val, max);
}
};
337. House Robber III
The thief has found himself a new place for his thievery again. There is only one entrance to this area, called the “root.” Besides the root, each house has one and only one parent house. After a tour, the smart thief realized that “all houses in this place forms a binary tree”. It will automatically contact the police if two directly-linked houses were broken into on the same night.
Determine the maximum amount of money the thief can rob tonight without alerting the police.
Example 1:
3
/ \
2 3
\ \
3 1
Maximum amount of money the thief can rob = 3 + 3 + 1 = 7.
Example 2:
3
/ \
4 5
/ \ \
1 3 1
Maximum amount of money the thief can rob = 4 + 5 = 9.
相似的期中考題目
從數列A[0], A[1], A[2], …, A[N-1]中選若干個數,要求對於每個i(0<=i < N-1),A[i]和A[i+1]至少選一個數,求能選出的最小和.
1 <= N <= 100000, 1 <= A[i] <= 1000
請爲下面的Solution類實現解決上述問題的函數minSum,函數參數A是給出的數列,返回值爲所求的最小和.
class Solution {
public:
int minSum(vector& A) {
}
};
例1:A = {2, 5, 2},答案爲4.
例2:A = {2, 5, 4},答案爲5.
注意:你只需要提交Solution類的代碼,你在本地可以編寫main函數測試程序,但不需要提交main函數的代碼. 注意不要修改類和函數的名稱.
思路
相鄰的至多選一個(337)與至少選一個(期中考)其實是類似的,不過337題將序列擴展爲樹,就稍微複雜了點。
可以這樣考慮:對於一棵子樹,對於其根節點,要麼選,則根節點的直接子節點就不能選,所求爲根節點值加上兩個左右子節點分別的左右子節點(稱爲孫節點吧)形成的子樹所能搶劫的之和;要麼不選,則所求相當於求兩個左右子節點形成的子樹所能搶劫的之和(注意此時未必就代表要選根節點的兩個左右子節點)。
其實這也是一個動態規劃問題,相當於課本所講的記憶化搜索(因爲比較難用一個dp數組記錄得到的信息,只能交給遞歸)。
從這道題與期中考題目也可以得出其中一個解決動態規劃問題的思路,就是對某一個元素,考慮選與不選的情況,再進行比較,不過還是要具體問題具體分析。
代碼
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
int dfs(TreeNode* root, int &l, int &r){
if(root == NULL){
l = 0;
r = 0;
return 0;
}
int ll, lr, rl, rr;
l = dfs(root->left, ll, lr);
r = dfs(root->right, rl, rr);
return max(root->val + ll + lr + rl + rr, l + r);
}
int rob(TreeNode* root) {
int l, r;
return dfs(root, l, r);
}
};
221. Maximal Square
Given a 2D binary matrix filled with 0’s and 1’s, find the largest square containing only 1’s and return its area.
For example, given the following matrix:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 4.
思路
這可以說是一道相當簡潔的題目了。現在的問題是:用動態規劃的思想來做,dp數組用來表示什麼。
曾經想過dp[i][j]表示matrix[i][j]所在最大正方形的邊長,但這樣問題是難以計算狀態轉移,因爲這樣無法得知(儲存)其所在的最大正方形是哪個,就算能得知,也未必能根據dp[i][j]進行狀態轉移。
既然這樣,就用dp[i][j]表示以matrix[i][j]爲右下角的最大正方形的邊長, 這樣,所求即max{dp[i][j]},也比較容易計算狀態轉移(具體見下)。
代碼
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int m = matrix.size();
if(m == 0) return 0;
int n = matrix[0].size();
if(n == 0) return 0;
vector<vector<int>> dp(m, vector<int>(n, 0));//dp[i][j]表示以matrix[i][j]爲右下角的正方形的面積
int maximaledge = 0;
for(int i = 0; i < m; i++){
if(matrix[i][0] == '1'){
dp[i][0] = 1;
maximaledge = 1;
}
}
for(int j = 0; j < n; j++){
if(matrix[0][j] == '1'){
dp[0][j] = 1;
maximaledge = 1;
}
}
for(int i = 1; i < m; i++){
for(int j = 1; j < n; j++){
if(matrix[i][j] == '1'){
dp[i][j] = min(min(dp[i-1][j-1], dp[i-1][j]), dp[i][j-1]) + 1;
maximaledge = max(maximaledge, dp[i][j]);
}
}
}
return maximaledge * maximaledge;
}
};