Leetcode二叉树专项训练1(easy:226.翻转二叉树,medium:114.将二叉树展开为链表、116.填充二叉树节点的右侧指针)


前言

很多经典算法中用到的回溯,动归,分治算法其实都是树的问题,例如【快速排序】的本质就是二叉树的前序遍历,而【归并排序】就是二叉树的后续遍历。而树的递归遍历是有框架可依的,因此进行二叉树题目的专项训练可以培养我们的算法思维,带着框架和清晰的逻辑去解决不断深化的题目。


提示:以下是本篇文章正文内容

一、二叉树是什么?

这张图非常清楚的介绍了二叉树中常用的概念,可以用来提醒一下自己

https://blog.csdn.net/qq_35644234/article/details/53013738
二叉树的遍历框架:

/* 二叉树遍历框架 */
void traverse(TreeNode root) {
    // 前序遍历
    traverse(root.left)
    // 中序遍历
    traverse(root.right)
    // 后序遍历
}

二、实战训练

1.226 翻转二叉树(easy)

题目:翻转一棵二叉树。

示例:

输入:

输出:
在这里插入图片描述

代码如下:

/**
 * 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:
    TreeNode* invertTree(TreeNode* root) {
        if(root==NULL) return root;//如果到叶节点就直接返回
        invertTree(root->left);
        invertTree(root->right);
        TreeNode* temp=root->left;//交换子节点的值
        root->left=root->right;
        root->right=temp;
        return root;
    }
};

二叉树题目的一个难点就是,如何把题目的要求细化成每个节点需要做的事情。

2.116 填充每个节点的下一个右侧节点指针(medium)

题目:给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

示例:
在这里插入图片描述

一开始我还是使用了上一题的框架:

class Solution{
public:
	Node* connect(Node* root){
		if(root==NULL) return root;
		root->left->next=root->right;
		connect(root->left);
		connect(root->right);
		return root;
	}
}

值得注意的是,这里并不能实现题目要求,虽然可以连接同一根节点的两个子节点,但是如果两个相邻节点并不属于一个父节点,是无法连接的。
因此,想要连接两个分属不同父节点的子节点,我们需要定义一个新的含有更多参数的函数来帮助我们。

class Solution {
public:
    Node* connect(Node* root) {
         if(root==NULL) return root;
         connectTwoNode(root->left,root->right);
         return root;
    }

    void connectTwoNode(Node* node1,Node* node2)
    {
        if(node1==NULL||node2==NULL) return;
        node1->next=node2;//进行连接

        connectTwoNode(node1->left,node1->right);//连接左节点的两个子节点
        connectTwoNode(node1->right,node2->left);//连接两个相邻的节点
        connectTwoNode(node2->left,node2->right);//连接右节点的两个子节点
    }
};

如果一个节点做不到,我们就给他增加节点来共同实现功能

3.114 二叉树展开为链表(medium)

题目:给定一个二叉树,原地将它展开为一个单链表

示例:

在这里插入图片描述

代码如下

class Solution {
public:
    void flatten(TreeNode* root) {
        if(root==NULL) return;
        
        //根据函数定义直接进行递归,必然可以将子节点拉成单链表,
        flatten(root->left);
        flatten(root->right);
        //保存现在的状态
        TreeNode* left=root->left;
        TreeNode* right=root->right;

        root->left=NULL;//注意这里将左节点置空,不然结果不是单链表而是二叉树
        root->right=left;
        TreeNode* p=root;//遍历得到右节点末端,注意不要直接使用root进行遍历,会丢失数据
        while(p->right!=NULL) p=p->right;
        p->right=right;
    }
};

这道题如果陷入如何让左右节点为根的二叉树拉成单链表,就非常难解决。做递归最重要的就是相信函数的定义并直接递归调用。我们要做到应该是解决边界的问题。

这就是递归的魅力,你说flatten函数是怎么把左右子树拉平的?不容易说清楚,但是只要知道flatten的定义如此,相信这个定义,让root做它该做的事情,然后flatten函数就会按照定义工作。


总结

递归算法的关键要明确函数的定义,相信这个定义,而不要跳进递归细节。

写二叉树的算法题,都是基于递归框架的,我们先要搞清楚root节点它自己要做什么,然后根据题目要求选择使用前序,中序,后续的递归框架。

二叉树题目的难点在于如何通过题目的要求思考出每一个节点需要做什么,这个只能通过多刷题进行练习了。

参考公众号:labuladong

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值