最近正在学习动态规划,今天就在力扣上遇到了相应的题目
动态规划的思考过程:
递归方法——记忆化递归——动态规划
首先需要找到递推式
然后通过记忆储存避免递归的重复运算
最后把它写成动态规划的形式
题目如图:
实现代码如下:
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
}
//需要开一个三维的指针,dp[i][j][k];i表示2*i+1个结点的索引,指向其真二叉树的集合,j表示该索引下的第j种真二叉树
//二级指针有多个一维指针的内存
//三级指针开n个二级指针的内存
//当i等于0时,只有1棵树,即dp[0][0]=root;
//当i大于1时,对在根节点的基础上连接左右子树
//假设2*i+1等于n(即有n个结点),在(n-1)的总数下
//有(1,n-2),(3,n-4)等等左右子树的序列,遍历序列通过一个for循环实现
//也就是假设dp[3]时,有7个结点
//dp[3]的元素集合就应该是根节点加上
//左子树连接dp[1]与右子树连接dp[5]的组合
//左子树连接dp[3]与右子树连接dp[3]的组合
//左子树连接dp[5]与右子树连接dp[1]的组合,其中组合通过两个for循环实现
//按照这样的思路,成功建立动态规划
//为了动态内存,三级指针所指向的二级指针的内存是不确定的,采用next创建动态内存
//一步一步解决:
//新建一个根节点与左右子树的根节点连接
struct TreeNode* createTreeNode(struct TreeNode* LeftTree,struct TreeNode* RightTree){
struct TreeNode* root=(struct TreeNode*)malloc(sizeof(struct TreeNode));
root->val=0;
root->left=LeftTree;
root->right=RightTree;
return root;
}
//新建一个链表,链表的结点指向真二叉树的根
//结点定义
typedef struct node{
struct TreeNode* tree;
struct node* next;
}Node;
//新建一个指根结点
Node* createNode(struct TreeNode* root){
Node* newNode=(Node*)malloc(sizeof(Node));
newNode->tree=root;
newNode->next=NULL;
return newNode;
}
//将新组合的树加入dp列表的方法:
//dp[i]=p;p为Node*,指向指根链表的头结点
struct TreeNode** allPossibleFBT(int n, int* returnSize) {
if(n%2==0){//偶数结点不存在真二叉树
*returnSize=0;
return NULL;
}
int SIZE=(n-1)/2+1;//结点为奇数时,输入为n,则只要开从1到n中全部奇数个数的内存就够了(节省内存)
int* count=(int*)malloc(sizeof(int)*SIZE);
Node** dp=(Node**)malloc(sizeof(Node*)*SIZE);
for(int i=0;i<SIZE;i++){
dp[i]=NULL;
count[i]=0;
}
count[0]=1;
dp[0]=createNode(createTreeNode(NULL,NULL));//初始化dp列表
for(int i=1;i<SIZE;i++){//对于每一个奇数结点的情况
for(int j=0;j<i;j++){//左右子树的序列组合循环
for(Node* LeftTree=dp[j];LeftTree;LeftTree=LeftTree->next){//开始遍历指根链表的各棵左子树
struct TreeNode* LeftSubTree=LeftTree->tree;//指向树
for(Node* RightTree=dp[i-j-1];RightTree;RightTree=RightTree->next){//在左子树固定的情况下,遍历各棵右子树,达到遍历所有组合
struct TreeNode* RightSubTree=RightTree->tree;
struct TreeNode* NewTree=createTreeNode(LeftSubTree,RightSubTree);//连接左右子树,创建新树
Node* NewNode=createNode(NewTree);//创建新的指根节点,指向新树
NewNode->next=dp[i];//
dp[i]=NewNode;//将新指根节点添加到dp[i]中
count[i]++;//i个结点所对应的真二叉树计数+1
}
}
}
}
*returnSize=count[SIZE-1];
//最后将dp[n]这个指根链表里的数拿出来,塞到答案指针里即可
//同时记得将dp列表free,释放内存
struct TreeNode** ans=(struct TreeNode**)malloc(sizeof(struct TreeNode*)*count[SIZE-1]);
int pos=0;
for(Node* node=dp[SIZE-1];node;node=node->next){
ans[pos++]=node->tree;
}
for(int i=0;i<SIZE;i++){
while(dp[i]){
Node* temp=dp[i];
dp[i]=dp[i]->next;
free(temp);
}
}
free(count);
return ans;
}