您的位置:首页 > 产品设计 > UI/UE

leecode 解题总结:95. Unique Binary Search Trees II

2017-02-14 12:31 405 查看
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
/*
问题:
Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1...n.

For example,
Given n = 3, your program should return all 5 unique BST's shown below.

1         3     3      2      1
\       /     /      / \      \
3     2     1      1   3      2
/     /       \                 \
2     1         2                 3

分析:这道题目实际上就是构建二叉查找树。二叉查找树由于不能调整位置,因此输入的
数据顺序会对树的形状产生影响。可以每次尝试不同的输入顺序:
比如1,2,3或者1,3,2;或者2,1,3;或者3,1,2或者3,2,1
但是即使用不同的顺序,可能会产生重复,比如:2,1,3和2,3,1产生的树的形状是一样的。
根本原因在于选用的根节点恰好是中间的数据。
简单的方式就是:每次产生一棵树,就和之前产生的所有树比较,看是否重复,如果重复
就过滤。
判断两棵树是否相同,可以递归得判定。
先判定两棵树当前结点值是否相同,如果不相同,直接返回两棵树不同;
否则,递归判定当前结点的左右子树是否相同即可

对于不同的输入:这个需要通过排列生成

输入:
3
输出:
1 3 2,3 2 1,3 1 2,2 1 3,1 2 3

关键:
1 采用排列+构建二叉查找树+去重做
2 leecode解法:https://leetcode.com/problems/unique-binary-search-trees-ii/?tab=Solutions
由于二叉查找树是中序,因此,可以选定[1...n]中任意数i为根节点,
那么左子树为[1...i-1],右子树为[i+1...len]
如果发现左边=右边,说明当前要生成的就是一个结点,就把结点当做根节点压入【参见n为1的情况】
如果左边>右边,不可能,返回空插入

本质是不断从给定的序列中选定一个成为根节点,令左半段成为左子树,令右半段成为右子树。
牛逼,以后看到树的问题多从递归入手

//注意每颗子树中也会产生不同的根节点,因此返回的左子树也是一个数组,右子树也是一个数组,然后做笛卡尔积
vector<TreeNode*> buildTree(int start , int end )
*/

struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

class Solution {
public:
//注意每颗子树中也会产生不同的根节点,因此返回的左子树也是一个数组,右子树也是一个数组,然后做笛卡尔积
vector<TreeNode*> buildTree(int start , int end )
//TreeNode* buildTree(int start , int end,  vector<TreeNode*>& result)
{
vector<TreeNode*> result;
//递归出口,返回的NULL,实际上可能是
if(start > end)
{
result.push_back(NULL);
return result;
}
//只提供一个结点,则直接是根节点,后续会遍历到这里
if(start == end)
{
TreeNode* node = new TreeNode(start);
result.push_back(node);
return result;
}
vector<TreeNode*> lefts;
vector<TreeNode*> rights;
int leftSize;
int rightSize;
for(int i = start ; i <= end ; i++)
{
lefts = buildTree(start  , i - 1);
rights = buildTree(i + 1 , end);
leftSize = lefts.size();
rightSize = rights.size();
for(int j = 0 ; j < leftSize; j++)
{
for(int k = 0 ; k < rightSize ; k++)
{
TreeNode* root = new TreeNode(i);
root->left = lefts.at(j);
root->right = rights.at(k);
result.push_back(root);
}
}
}
return result;
}

vector<TreeNode*> generateTrees(int n) {
vector<TreeNode*> nodes;
if(n <= 0)
{
return nodes;
}
nodes = buildTree(1 , n);
return nodes;
}
};

class Solution2 {
public:
void permutate(int n,int pos , vector<int>& result, vector< vector<int> >& results)
{
if(pos == n)
{
results.push_back(result);
return;
}
bool isOk;
for(int i = 1 ; i <= n ; i++)
{
isOk = true;
for(int j = 0 ; j < pos ; j++)
{
if(i == result.at(j))
{
isOk = false;
break;
}
}
if(isOk)
{
result.push_back(i);
permutate(n , pos + 1 , result , results);
result.pop_back();
}
}
}

TreeNode* insertNode(TreeNode* root , int value)
{
if(!root)
{
return NULL;
}
//如果给定值> 根节点值,如果右孩子为空,作为右孩子,否则以右孩子作为根节点,递归
if(value >= root->val)
{
if(NULL == root->right)
{
TreeNode* node = new TreeNode(value);
root->right = node;
}
else
{
insertNode(root->right , value);
}
}
else
{
if(NULL == root->left)
{
TreeNode* node = new TreeNode(value);
root->left = node;
}
else
{
insertNode(root->left , value);
}
}
return root;
}

bool isSameTree(TreeNode* root1 , TreeNode* root2)
{
//如果两个当前结点都为空,则相同
if(NULL == root1 && NULL ==  root2)
{
return true;
}
else if(NULL == root1)
{
return false;
}
else if(NULL == root2)
{
return false;
}
if(root1->val != root2->val)
{
return false;
}
return isSameTree(root1->left , root2->left) && isSameTree(root1->right ,root2->right);
}

//判断当前二叉查找树是否和集合中的二叉查找树重复
bool isRepeated(vector<TreeNode*>& nodes , TreeNode* root)
{
if(nodes.empty())
{
return false;
}
int size = nodes.size();
for(int i = 0 ; i < size ; i++)
{
if(isSameTree(nodes.at(i) , root))
{
return true;
}
}
return false;
}

vector<TreeNode*> generateTrees2(int n) {
vector<TreeNode*> nodes;
if(n <= 0)
{
return nodes;
}
vector< vector<int> > results;
vector<int> result;
permutate(n , 0 , result , results);
int size = results.size();
//尝试不同的数据输入,来构建二叉树
int len;
for(int i = 0 ; i < size ; i++)
{
len = results.at(i).size();
TreeNode* root = NULL;
for(int j = 0 ; j < len ; j++ )
{
if(j)
{
root = insertNode(root , results.at(i).at(j)  );
}
//输入根节点
else
{
root = new TreeNode( results.at(i).at(j) );
}
}

//构建完了二叉查找树,下面判断是否重复
if(nodes.empty())
{
nodes.push_back(root);
}
//如果
else
{
//如果不重复
if(!isRepeated(nodes , root))
{
nodes.push_back(root);
}
}

}
return nodes;
}
};

//根左右
void preOrderVisit(TreeNode* root , vector<int>& result)
{
if(NULL == root)
{
return;
}
if(root)
{
result.push_back(root->val);
}
preOrderVisit(root->left, result);
preOrderVisit(root->right , result);
}

//打印每颗二叉查找树,用前序方式
void print(vector<TreeNode*>& result)
{
if(result.empty())
{
cout << "no result" << endl;
return;
}
int size = result.size();
int len;
for(int i = 0 ; i < size ; i++)
{
vector<int> datas;
preOrderVisit(result.at(i) , datas);
len = datas.size();
for(int j = 0 ; j < len ; j++)
{
cout << datas.at(j) << " ";
}
cout << ",";
}
cout << endl;
}

//如果删除一棵树,只能先递归删除其左右子树,如果遇到一个结点没有左右孩子,就删除
void deleteTree(TreeNode* node)
{
//如果当前结点为空,无需删除,直接返回
if(!node)
{
return;
}
else if(NULL == node->left && NULL == node->right)
{
delete node;
node = NULL;
}
if(node)
{
deleteTree(node->left);
deleteTree(node->right);
}
}

void deleteTrees(vector<TreeNode*>& nodes)
{
if(nodes.empty())
{
return;
}
int size = nodes.size();
for(int i = 0 ; i < size ; i++)
{
deleteTree(nodes.at(i));
}
}

void process()
{
vector<int> nums;
int value;
int num;
Solution solution;
vector<TreeNode*> result;
while(cin >> num )
{
result = solution.generateTrees(num);
print(result);
deleteTrees(result);
}
}

int main(int argc , char* argv[])
{
process();
getchar();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: