第 14 章 指针三剑客之二:树

仅为个人刷题记录。

104. 二叉树的最大深度

太垃圾了,这题没写出来。

110. 平衡二叉树

写树题目感觉总是缺根弦。
利用一个辅助函数返回深度是否平衡
int helper(TreeNode* root)
在这个函数中,首先判空,root不存在直接返回-1
那么根节点的左右子树的深度可以直接递归的表示
int l = helper(root->left); int r = helper(root->right);
如果l ,r 为 -1 或者深度差大于1 也直接返回-1
if(l == -1 || r == -1 || abs(l - r) > 1)
反之返回深度
return max(l,r) + 1;
为什么是最大值加一,看看上面的深度题,对于一个根节点,深度都是左右子树的最大深度 + 自身。

543. 二叉树的直径

跟上面那道题差不多。有个细节要处理,就是用一个引用int型保存直径。一开始写的是直接l + r,但是这是不太对的,应该是d = max(l + r ,d);,直径不能直接等于深度和。

437. 路径总和 III

我不是写树题缺根弦,是递归缺根弦…
声明一个辅助函数rootSum 表示该root为起点往下探索有多少路径。

int rootSum(TreeNode* root,int targetSum)
    {
        if(!root)
        {
            return 0;
        }
        int ret = 0;

        if(targetSum == root->val)所以最后判断是否等于最后一个节点值就行,不要为0
        {
            ret++;
        }

        ret += rootSum(root->left ,targetSum - root->val);
  在此处才减掉root->val值,因为直接在第一次调用时减去,可能是空的
        ret += rootSum(root->right ,targetSum - root->val);

        return ret;
    }

在主函数中,不仅需要根节点的路径,还需要左右子树的路径。

int pathSum(TreeNode* root, int targetSum) 
    {
        if(!root)
        {
            return 0;
        }

        int ret = rootSum(root,targetSum);

        ret += pathSum(root->left,targetSum);
        ret += pathSum(root->right ,targetSum);

        return ret;
    }

101. 对称二叉树

首先判断root是否为空吧。为空直接true了。
不为空,递归判断他的左右节点是否为对称
bool isSymmetric(TreeNode* left,TreeNode* right)
对于任何一个 left 或者 right ,当他们为根节点的时候,判断的方法是
return isSymmetric(left->left,right->right) && isSymmetric(left->right,right->left);

在这里插入图片描述
对于每个需要判断的节点条件:同时为空为true,一者不为空false,两者数据不一致false。

1110. 删点成林

这个题非常好!!!
想到了如果在删除表中找到了数据,那么就将它的左右节点直接放入vector中。但是一直在想怎么跟上面断开联系。看了题解恍然大悟。
这题是先递归后判断
先递归到末尾,再从末尾一步步向上判断,这样就不要在乎与上面的联系了。注意这个节点还需要置空。
这个题跟上面的可以好好对比,一个是先递归一个是先判断。

105. 从前序与中序遍历序列构造二叉树

剑指同题,已经不知道是第几刷了。。。思路都有不是很会写代码。
注意一下递归边界

		if (preorder_left > preorder_right) 
        {
            return nullptr;
        }

637. 二叉树的层平均值

广搜树题。好耶。记模板吧。

99. 恢复二叉搜索树

慢慢分析,来!
对于二叉搜索树,中序遍历就是升序排列。首先就是中序遍历得到数据。那么很容易找到两个错位值。
比如 1,2,3,4,5,6,7. 如果2 6 互换错位。
1 6 3 4 5 2 7 只要通过当前nums[i + 1] < nums[i] 即可找到错位。需要两个变量index1 index2来记录位置。
当第一次找到不符合的6 3 先用index2 = i + 1;index1 = i 保留index1不变,后面再次找到不符合的 5 2 再次变更index2 = i + 1 互换以后一定是大的在前小的换到后。那么第一次遇到的就需要得到大数的位置,第二次遇到需要得到较小数的位置。
最后就是恢复。恢复通过递归一个个左右找,找到刚好与错位相同的数据,并互换。这里没有递归边界,所以要自定义一个交换次数,每次交换后减一,为0则return。

669. 修剪二叉搜索树

想到了最小值减掉左边最大值减掉右边。但是还是不准确。
对于每个根节点的值都需要判断。
如果root小于最小,整个左边都不要(包括自己),直接把右边部分放到root的位置,继续递归判断该root。大于反之。
如果在该范围内,那就递归判断root的左右节点。

208. 实现 Trie (前缀树)

不知道怎么记录。对于构建字典树,需要每个节点有一个指向下一个的指针和isEnd判断是否到末尾。
对于一个字符串word,插入就是不断判断当前字符是否已经是Node的下一个,不是则新建一个node node->next[ch - 'a'] = new Trie();
搜索则是不断判断下一个是否在内,为空直接false,到了末尾但是有可能只是这条支链上的前缀串,所以还要判断是否到了isEnd.

226. 翻转二叉树

写出来啦!!!
我的:

 TreeNode* invertTree(TreeNode* root) 
    {
        if(!root)
        {
            return root;
        }
        TreeNode* tmp = root->left;
        root->left = invertTree(root->right);
        root->right = invertTree(tmp);

        return root;        
        
    }

官方:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr) {
            return nullptr;
        }
        TreeNode* left = invertTree(root->left);
        TreeNode* right = invertTree(root->right);
        root->left = right;
        root->right = left;
        return root;
    }
};

官答真的很好理解,递归中先把每个左右子树取出来,再交换。

617. 合并二叉树

思路啥都有,不会写啊。

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) 
    {
        TreeNode* root = new TreeNode(0);
        if(!root1 && !root2)
        {
            return 0;
        }
        if(root1)
        {
            root->val += root1->val;
        }
        if(root2)
        {
            root->val += root2->val;
        }
        root->left = mergeTrees(root1 ? root1->left : 0,root2 ? root2->left : 0);
        root->right = mergeTrees(root1 ? root1->right : 0,root2 ? root2->right : 0);

return root;

    }
};

572. 另一棵树的子树

跟判断对称题很像,,但我没写出来。被几个递归式调用哪个绕晕了。
1、在主函数中我们可以判断出,如果root没了,直接false
2、递归的进行判断。通过调用辅助函数,判断当前root与subroot是否可以为真。或者,再次递归调用自己,此时参数以root->left 或者 root->right为root与subroot,这样来到最后再次调用辅助函数。
辅助函数
1、任一为空另一个不空直接false
2、同时为空 true。
3、两个值不相等false。
最后递归调用自身,还需要判断左右子树是否都为真。

513. 找树左下角的值

需要一个递归不断找最底层和最左。
递归边界就是当左右无节点也是叶子节点的时候,判断此时深度是否大于最大深度,是则把root值赋给maxdeepval。
如果有左子节点递归左边,右边则递归右边。有个问题,在递归左右子节点都需要当前deep值加一。注意是加一没事递归回溯别写++。这样右子节点值不会也到maxdeepval吗?
不会,主要进入maxdeepval的条件是 deep > maxdeep 同一层节点先递归到左边,右边的同深度不会再次进入该条件。

235. 二叉搜索树的最近公共祖先

剑指68同。

538. 把二叉搜索树转换为累加树

反向中序属实把我看呆了。
递归到右下角的是最大的那么只需要加上他自己。
对于其他点,不断加上上一个递归的值和自己就行。

530. 二叉搜索树的最小绝对差

感觉二叉搜索树就是正向反向中序遍历玩出花来…
差值最小一定是两个相邻的值,所以老样子,只不过这道题需要自己写辅助函数中序遍历。
辅助函数需要一个int& pre 来记录上一个节点,也就是相邻节点的值与当前值相减。

889. 根据前序和后序遍历构造二叉树

一开始楞了,前序后序怎么能构造二叉树。其实这题跟105前序中序差不多。
首先主函数里,一个哈希表把后序遍历val对应其index。

有点不同的是,preorder[pre_left]除了作为根节点不能直接映射要postorder,没有什么用。但是preorder[pre_left + 1]很有用。发现这个点的映射位置刚好在后序遍历的末尾。那么就可以通过左子树求得当前子树长度。这样递归就完成了。
注意这种题需要自己建立根节点。
TreeNode* root = new TreeNode(preorder[pre_root]);

106. 从中序与后序遍历序列构造二叉树

写过前两题这题已经没什么好说的了,会了。

94. 二叉树的中序遍历

递归无需多言,主要是栈方法不熟悉。

1 不断的把左节点放入直到为空
2 把根节点 = 当前栈顶,弹出栈顶,数值放入ans
3 根节点为右
看代码随想录吧。

145. 二叉树的后序遍历

栈方法:
这个可以直接根据前序遍历改造得来
前序->中左右
后序-> 左右中

中左右—栈先入右再入左---->中右左-------反转----->左右中

236. 二叉树的最近公共祖先

还是剑指68-II,看着笔记写出来了。

109. 有序链表转换二叉搜索树

思路都有,很清晰啊,但是不会写代码。
具体就是两个辅助函数。
一个求链表中点,熟悉的不能再熟悉了,快慢指针。注意一下循环边界while(fast != right && fast->next != right)
另一个就是建树
每次取到中点,左边剩下的就是左子树,右边同。
但是要注意,这题传入的参数直接是链表了,所以主函数调用方法是buildTree(head,nullptr);
builtree函数中边界直接判断左右是否是同一个节点
if(left == right)

897. 递增顺序搜索树

方法一:
先中序遍历把值存入数组,将数组的值建树。
中序遍历不说了,建树记一下。

哑结点声明
TreeNode* dummynode = new TreeNode(-1);
TreeNode* curnode = dummynode;
建树
for(int value : res)
{            
	curnode->right = new TreeNode(value);
	curnode = curnode->right;
}

既然没有左节点那就直接设置右节点不管左节点就好了啊…
最后返回return dummy->right;

方法二:算是我想了一大半的方法,但是还是不怎么会写树题代码。
在中序遍历的过程中进行更改指针方向
通过一个resNode来建树,resNode每次指向的恰好是curNode的前驱节点。

		resNode->right = node;
        node->left = nullptr;
        resNode = node;

待补充:为什么node->left = nullptr; ???

450. 删除二叉搜索树中的节点

在这里插入图片描述
至于代码部分:

if (key > root->val)    root->right = deleteNode(root->right, key);     // 去右子树删除
else if (key < root->val)    root->left = deleteNode(root->left, key);  // 去左子树删除

这里的root->right = …怎么理解?相当于进入root的右子树并找到这个key值执行后续,重构右子树。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值