动态规划——所有可能的真二叉树

最近正在学习动态规划,今天就在力扣上遇到了相应的题目

动态规划的思考过程:

递归方法——记忆化递归——动态规划

首先需要找到递推式

然后通过记忆储存避免递归的重复运算

最后把它写成动态规划的形式

题目链接:https://leetcode.cn/problems/all-possible-full-binary-trees/solutions/?envType=daily-question&envId=2024-04-02

题目如图:

实现代码如下:

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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值