树分为两种遍历方式,分别是深度优先遍历和广度优先遍历。这里我们先看关于深度优先的三道题,分别是前序(中左右)中序(左中右)后序(左右中),二叉树的定义和链表类似,多写,刚开始记不住就背下来。
144.二叉树的前序遍历
分析:前序遍历,使用栈和数组来实现
(1)前序遍历也就是中左右,先放中间节点,再放左右子节点
(2)栈是先进后出,所以放入元素的顺序应该是先右再左,这样出来的元素才能是正常的
(3)流程:栈中右root结点,进入循环后弹出,然后分别加入root的右节点和左节点(右节点先加入),再下次循环中先将左节点弹出,随后再判断是否需要加入左节点的左右节点。
/**
* Definition for a binary tree node.
* public class TreeNode {
* //可以看一下树的定义
* //值 左右节点 构造函数
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
public IList<int> PreorderTraversal(TreeNode root) {
//声明数组和栈
Stack<TreeNode> st1 = new Stack<TreeNode>();
List<int> list1 = new List<int>();
//当根节点为空 就返回空数组
if(root == null)return list1;
//首先我们需要将根节点放入栈中
st1.Push(root);
//当栈中元素不为空
while(st1.Count != 0)
{
//定义一个临时变量去接收弹出的元素
var temp = st1.Pop();
//在定义好的数组中添加该元素
list1.Add(temp.val);
//当右边不为空的时候 我们放入右边的元素
if(temp.right != null)
{
st1.Push(temp.right);
}
//当左边不为空的时候 我们放入左边的元素
if(temp.left != null)
{
st1.Push(temp.left);
}
}
return list1;
}
}
递归写法
分析:(1)List声明记得要写在外部,因为是递归,不能重复创建List
(2)递归三要素:递归参数,结束条件,递归逻辑
(3)无限搜索左子节点 直到为空返回,无限搜索右子节点 知道为空返回,搜索一次往List添加一次不为空的值,最终返回到根节点的左右节点,返回List
public class Solution {
List<int> list1 = new List<int>();
public IList<int> PreorderTraversal(TreeNode root) {
if(root == null) return list1;
list1.Add(root.val);
PreorderTraversal(root.left);
PreorderTraversal(root.right);
return list1;
}
}
94.二叉树的中序遍历
分析:中序遍历(左中右)这里使用递归的方式来实现,为什么和前序遍历的写法不太一样?因为前序遍历先处理根节点,而中序需要先处理左节点,递归我觉得更好实现。下题后序遍历会发现和前序很类似。
(1)递归就是可以不断地访问左节点,直到它为空在返回上一层,这样我们就实现了中序遍历
(2)(结合如下图和代码 代入思考)流程:根据root节点,先判断左节点是否为空,不为空就递归调用,直到传入节点的左节点(红left)为空,就添加传入的节点(红left),随后判断右节点是否为空,不为空就递归调用。之后返回上一层,那刚才传入的节点(红left)就是上一层的左节点,所以添加上一层的节点,之后在判断右节点,然后再返回上一层,直到最后添加root根节点就算结束。
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
//list需要声明在外部 因为这里是递归调用这个方法 不能重复声明list
List<int> list1 = new List<int>();
public IList<int> InorderTraversal(TreeNode root) {
if(root == null)
return list1;
if(root != null)
{
//判断左节点是否为空
if(root.left!=null)
//传入该节点的左节点
//不为空就开始递归调用 直到为空 再往下执行
InorderTraversal(root.left);
//传入的节点的左节点为空 就向list中添加传入的节点
list1.Add(root.val);
//判断右节点是否为空
if(root.right!=null)
//不为空就添加右节点
InorderTraversal(root.right);
}
return list1;
}
}
分析:前序(中左右)和后序(左右中)我推荐都使用迭代法,也就是第一题中的写法。
(1)中左右其实可以调换一下左右节点的判断顺序,就可以变成中右左,再将数组整个翻转,就可以变成左右中,也就是后序遍历
中左右-----中右左-----左右中
(2)那么只要写出前序前序遍历,不难写出后序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
public IList<int> PostorderTraversal(TreeNode root) {
Stack<TreeNode> st1 = new Stack<TreeNode>();
List<int> list1 = new List<int>();
if(root == null)
return list1;
st1.Push(root);
while(st1.Count > 0)
{
var temp = st1.Pop();
list1.Add(temp.val);
//这里调换出栈的左右顺序
if(temp.left != null)
st1.Push(temp.left);
if(temp.right != null)
st1.Push(temp.right);
}
//list翻转即可
list1.Reverse(0,list1.Count);
return list1;
}
}
接下来看广度优先遍历题目,也就是层序遍历
102.二叉树的层序遍历
分析:首先层序遍历要求一层一层的结果,所以返回的数组有多个。这里还需要使用队列,因为队列先进先出,我们按层搜索元素使用队列更适合。
(1)有一个要点就是需要一个临时变量记录一下队列中元素的数量,这样我们才能在队列中判断,一层的元素是否都出队完成了,才能判断下一层的元素。
(2)流程:将元素不断地放入队列,同时记录一下该层元素的数量,出队后再入队它的左右节点,通过记录数量就可以控制for循环,然后将各层的数组添加到总数组中即可。
/**
* Definition for a binary tree node.
* public class TreeNode {
* public int val;
* public TreeNode left;
* public TreeNode right;
* public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
public class Solution {
public IList<IList<int>> LevelOrder(TreeNode root) {
//根据返回值要求声明对应的变量
IList<IList<int>> list1 = new List<IList<int>>();
Queue<TreeNode> queue1 = new Queue<TreeNode>();
if(root == null) return list1;
//先将根节点入队
queue1.Enqueue(root);
//记得写while 不是if
while(queue1.Count != 0)
{
//这个是很重要的一点 也就是记录这层队列中元素的数量
//
int count = queue1.Count;
List<int> list2 = new List<int>();
//这样就可以根据这层元素的数量进行for循环
for(int i = 0; i < count; i++)
{
//将队列中的元素出队
TreeNode temp = queue1.Dequeue();
//添加到新数组中
list2.Add(temp.val);
//之后判断它的左右节点 分别加入到队列
if(temp.left != null)
{
queue1.Enqueue(temp.left);
}
if(temp.right != null)
{
queue1.Enqueue(temp.right);
}
}
//最后将每一层的数组都添加到总数组中
list1.Add(list2);
}
return list1;
}
}
还有几个非常类似的题目,只需要明白上述题目,稍作调整即可,一起来看一下
它要从下往上返回值,其实就是翻转总数组即可。
list1.Reverse();
它要求返回一层中最后一个值,每层的数量我们记录过了,在for循环中,当i等于最后一个值的时候,我们添加到数组即可。
if(i == count -1)
result.Add(temp.val);
它要求返回一层的平均值,那么就可以通过一个临时变量,当队列元素出队的时候,我们就把它的值加到临时变量中(我这里只截了需要使用sum的地方)
while(queue.Count != 0)
{
int size = queue.Count;
double sum = 0;
for(int i = 0;i < size; i++)
{
TreeNode temp = queue.Dequeue();
sum += temp.val;
if(temp.left != null)
queue.Enqueue(temp.left);
if(temp.right != null)
queue.Enqueue(temp.right);
}
sum /= size;
list1.Add(sum);
}
看到这种题目第一反应就是需要一个临时变量记录值
while(queue.Count != 0)
{
int size = queue.Count;
//比较 就需要有参照物对吧 所以我们以队列的头节点作为参照物即可
int head = queue.Peek().val;
for(int i = 0;i < size; i++)
{
TreeNode temp = queue.Dequeue();
//用出队元素和队列头节点进行比较 最大的就记录下来
if(head <= temp.val)
head = temp.val;
if(temp.left != null)
queue.Enqueue(temp.left);
if(temp.right != null)
queue.Enqueue(temp.right);
}
//只添加大的即可
list1.Add(head);
}
分析:这个题最后要求用’#‘连接标志着每一层的结束,应该指针代表的就是’#',那本题其实核心就是i等于最后一个元素或者队列长度为0的时候,那就直接让出队的元素指向null;其余情况都直接让它指向队列的头部(也就是下一个元素)因为一个元素出队,那么下一个元素就成为了队首(这里我只截了核心代码)
while(queue.Count > 0)
{
int size = queue.Count;
for(int i = 0;i < size; i++)
{
Node temp = queue.Dequeue();
if(size == 0 || i == size - 1)
{
temp.next = null;
}
else
{
temp.next = queue.Peek();
}
if(temp.left != null)
queue.Enqueue(temp.left);
if(temp.right != null)
queue.Enqueue(temp.right);
}
}
这道题建议大家用ACM模式完成
226.翻转二叉树
分析:翻转二叉树,采用的是递归,将他的所有子节点都进行翻转,就实现了树的翻转
public class 二叉树 : MonoBehaviour {
void Start () {
//二叉树的实例化
TreeNode t1 = new TreeNode(4);
TreeNode t2 = new TreeNode(2);
TreeNode t3 = new TreeNode(7);
TreeNode t4 = new TreeNode(1);
TreeNode t5 = new TreeNode(3);
TreeNode t6 = new TreeNode(6);
TreeNode t7 = new TreeNode(9);
//子节点声明
t1.left = t2;
t1.right = t3;
t2.left = t4;
t2.right = t5;
t3.left = t6;
t3.right = t7;
//翻转并打印
ReverseTree(t1);
Print(t1);
}
public class TreeNode
{
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int val)
{
this.val = val;
}
}
//翻转方法 先递归左子节点 再递归右子节点 然后翻转即可
public TreeNode ReverseTree(TreeNode root)
{
if(root == null)
{
return null;
}
ReverseTree(root.left);
ReverseTree(root.right);
var temp = root.left;
root.left = root.right;
root.right = temp;
return root;
}
//打印方法
//有点类似层序遍历写法 就是队列弹出元素后加入左右子节点即可
public void Print(TreeNode root)
{
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
while(queue.Count > 0)
{
var temp = queue.Dequeue();
Debug.Log(temp.val);
if(temp.left != null)
{
queue.Enqueue(temp.left);
}
if (temp.right != null)
{
queue.Enqueue(temp.right);
}
}
}
}
以下两题可以使用递归实现
589.N叉树的前序遍历
分析:这道题类似前序遍历,要求按前序(中左右)的方式返回值
/*
// Definition for a Node.
public class Node {
public int val;
public IList<Node> children;
public Node() {}
public Node(int _val) {
val = _val;
}
public Node(int _val,IList<Node> _children) {
val = _val;
children = _children;
}
}
*/
public class Solution {
//这里是需要两个方法的 为什么一个方法不能完成呢?
//因为递归我们需要输入参数 而子节点都是List 所以一个方法返回节点 输入却是列表
public IList<int> Preorder(Node root) {
var list1 = new List<int>();
if(root == null) return list1;
//先加入根节点
list1.Add(root.val);
//关键点就是传入List 因为从始至终我们只需要一个List
Preorder(list1,root);
return list1;
}
public void Preorder(IList<int> list1, Node root)
{
//遍历所有节点 递归调用并向List中添加元素 直到传入的root为空就会返回上一层
foreach(var v in root.children)
{
list1.Add(v.val);
Preorder(list1,v);
}
}
}
590.N叉树的后序遍历
分析:那前面已经讲过前序遍历—后序遍历,就是中左右—中右左—左右中,所以这里也是一样的
public class Solution {
public IList<int> Postorder(Node root) {
var list1 = new List<int>();
if(root == null) return list1;
list1.Add(root.val);
Postorder(list1,root);
//翻转:中右左---左右中
list1.Reverse(0,list1.Count);
return list1;
}
public void Postorder(IList<int> list1, Node root)
{
//foreach是只能从前往后进行遍历 所以使用for循环即可
//就从中左右 变成了 中右左
for(int i = root.children.Count - 1; i >= 0; i--)
{
list1.Add(root.children[i].val);
Postorder(list1,root.children[i]);
}
}
}
104.二叉树的最大深度
分析:首先要分清楚高度和深度的区别,建议去看一下代码随想录的讲解
(1)高度(后序遍历):从下往上去计数,也就是从子节点计数为1,父节点为2,再往上为3…
(2)深度(前序遍历):从上往下去计数,也就是从根节点计数为1,子节点为2,再往下为3…
本题其实使用的是后序遍历
public class Solution {
public int MaxDepth(TreeNode root)
{
//递归到子节点开始计数 也就是当一个节点的子节点等于0 那就返回0到这个节点
//然后这个节点再往上返回的时候 要和另一个节点比较得最大值后再+1
//为什么要+1?比如你住在5楼,那你距离1楼有4层,+1算上自己才是你的高度
if(root == null) return 0;
int leftDepth = MaxDepth(root.left);
int rightDepth = MaxDepth(root.right);
return Math.Max(leftDepth, rightDepth) + 1;
/* 也可以精简为如下代码
if(root == null) return 0;
return Math.Max(MaxDepth(root.left), MaxDepth(root.right)) + 1;
*/
}
}
101.对称二叉树
分析:要求一棵树是对称的,这里使用递归来实现,那就要考虑递归三要素
(1)递归参数:那比较的目的是对称,所以传入的节点自然是左右节点
(2)递归逻辑:就是左子树的左节点和右子树的右节点进行判断。还需要判断什么情况下就是false,就是不对称。
·左节点为空,右节点不为空,false
·左节点不为空,右节点为空,false
·左节点值 不等于 右节点值 ,false
·左节点为空,右节点也为空,true
(3)结束条件:当最后返回值都为true
public class Solution {
public bool IsSymmetric(TreeNode root) {
return Compare(root.left,root.right);
}
//因为要递归 需要另写一个方法 传入左节点和右节点
public bool Compare(TreeNode left,TreeNode right)
{
//上述说过的四种情况
if(left == null && right != null)return false;
else if(left != null && right == null)return false;
else if(left == null && right == null)return true;
else if(left.val != right.val)return false;
//这里可以做简写 这样更好理解而已
//就是比较左子树的左节点和右子树的右节点
var temp = Compare(left.left,right.right);
//一样
var temp2 = Compare(left.right,right.left);
//返回的值都为true才能是对称的
return temp && temp2;
}
}
111.二叉树的最小深度
分析:这道题和最大深度有点出入,关键就是多考虑两种情况。叶子节点就是左右节点都为空的节点(图中9,15,7都是,本题要求返回最小值)
(1)解题还是从下往上依次返回节点,每层+1
(2)我们最终返回值肯定是取最小值,那有一个问题,例如下图中2的左子节点为null,右子节点不为null,那很明显2并不是叶子节点,但是返回的时候最小值一定会被这个值影响,所以我们要考虑两种情况:就是当左子节点为null,右子节点不为空的时候,应该用右子节点的最小高度 + 1;另一种就是就是当右子节点为null,左子节点不为空的时候,应该用左子节点的最小高度 + 1。
(3)有点懵对吧,我们结合代码和图一起看一下:传入根节点2,当left = null时,最终返回值为1;right则一直往下递归,直到传入节点6,那此时6的左右子节点都为null,(返回值)0+1,则6这层的返回值是1。往回递归,传入5这里值就是2,传入4这里值就是3,传入3这里值就是4,此时的right就是4,也就是真正的叶子节点的高度。那最后取最小值 Math.Min(left,right) + 1,如果不处理根节点的左子节点,此时返回的最小值一定是这个为null节点的值,所以当左子节点为null,右子节点不为空返回的最小值应该是 right + 1,因为左子节点 = null 已经报废了,可以这样理解,它影响到了我们取最小值,应该以同层的右子节点的值为准。右为空,左不为空,同理。
public class Solution {
public int MinDepth(TreeNode root) {
return FindMin(root);
}
public int FindMin(TreeNode root)
{
if(root == null) return 0;
int left = FindMin(root.left);
int right = FindMin(root.right);
if(root.left == null && root.right != null) return right + 1;
if(root.right == null && root.left != null) return left + 1;
return Math.Min(left,right) + 1;
}
}
222.完全二叉树的节点个数
分析:就是得出节点个数,只要能遍历所有节点的方法都ok,这里用了递归和层序遍历
//递归
public int CountNodes(TreeNode root) {
if(root == null) return 0;
return CountNodes(root.left) + CountNodes(root.right) + 1;
}
//层序遍历
public int CountNodes(TreeNode root) {
if(root == null) return 0;
int count = 1;
var queue = new Queue<TreeNode>();
queue.Enqueue(root);
while(queue.Count != 0)
{
var temp = queue.Dequeue();
if(temp.left != null)
{
queue.Enqueue(temp.left);
++count;
}
if(temp.right != null)
{
queue.Enqueue(temp.right);
++count;
}
}
return count;
}
110.平衡二叉树
分析:平衡二叉树就是左右子树的高度相差大于1。本题使用递归,具体看代码。
public class Solution {
public bool IsBalanced(TreeNode root) {
//root为空返回true是题目要求
if(root == null) return true;
//我们设定当左右子树高度差大于1时 就返回-1 那-1说明就不是平衡二叉树
return GetHight(root) == -1 ? false : true;
}
public int GetHight(TreeNode root)
{
if(root == null) return 0;
//左子树的高度
int leftH = GetHight(root.left);
//默认当它等于-1 就返回-1 不是平衡二叉树
//下面的判断条件我们来写它什么时候等于-1
if(leftH == -1) return -1;
//右子树高度
int rightH = GetHight(root.right);
//同理
if(rightH == -1) return -1;
//那就是当左右子树的高度差绝对值大于1时 说明不是平衡二叉树
//返回-1
if(Math.Abs(leftH - rightH) > 1)return -1;
//否则就说明都符合平衡二叉树 那就返回每层的最大高度
return Math.Max(leftH,rightH) + 1;
}
}
257.二叉树的所有路径
分析:前序遍历节点,当遇到叶子节点的时候向IList中添加
public class Solution {
public IList<string> BinaryTreePaths(TreeNode root) {
//IList是返回值 List用来存放值
IList<string> result = new List<string>();
List<int> list = new List<int>();
//所以都要传过去
Path(root, list, result);
return result;
}
public void Path(TreeNode root, List<int> list, IList<string> result) {
//节点为空就返回
if(root == null) return;
//每次遍历的元素不为空都添加到List中
list.Add(root.val);
//遇到叶子节点
//先将List中已有的节点拼接箭头 然后添加到IList中
if(root.left == null && root.right == null) {
result.Add(string.Join("->", list));
}
else {
//没有遇到叶子节点
//递归调用
Path(root.left, list, result);
Path(root.right, list, result);
}
//那遇到叶子节点向IList添加完结果后 最后执行到这一行 往回return 依次清除最后一个元素
list.RemoveAt(list.Count - 1);
}
}
404.左叶子之和
分析:题目要求返回左子叶之和,注意是当这个节点的左右节点都为空的时候,才返回哦。那我们判断:当一个节点的左节点不为空 同时 节点的左节点的左右节点都为空,那说明就是我们要的节点(有点绕 直接看代码)
public class Solution
{
//记得sum写在外面 因为GetSum会递归调用
int sum = 0;
public int SumOfLeftLeaves(TreeNode root)
{
return GetSum(root);
}
public int GetSum(TreeNode root)
{
if(root == null)return 0;
//传入节点的左节点不为空 传入节点的左节点的左节点 和 传入节点的左节点的右节点 为空
//那就说明该节点的值是我们需要的 就加到sum中
//注意这里看清楚 容易写混
if(root.left != null && root.left.left == null && root.left.right == null)
{
sum += root.left.val;
}
GetSum(root.left);
GetSum(root.right);
return sum;
}
}
513.找树左下角的值
分析:这道题目要求我们找最底层 最左边的值,那自然就能联想到需要全局变量去记录最大深度和最左侧的值,我分别用递归和层序遍历的方式实现
递归(1)使用前序遍历,求叶子节点(节点的左节点为空,节点的右节点为空),同时记录最大深度,那谁的深度值最大,就以谁的叶子节点为准
(2)流程:每次递归都会对深度+1,那当找到叶子节点,同时深度是大于全局变量的话,说明这个节点深度最大,那就返回该值
迭代(1)最常写的层序遍历的方式,多加一个全局变量,记录每次最左侧的值,如果还有下一层,那就会去把最左侧的值更新掉
//递归
public class Solution {
//记录最大深度 对比使用的 因为maxDep也不可能小于0
//如果最大深度为负数 说明没有符合的节点
public int maxDep = -1;
//记录值
public int result = 0;
public int FindBottomLeftValue(TreeNode root) {
//传入根节点和0
FindDeep(root,0);
return result;
}
public void FindDeep(TreeNode root,int depth)
{
//判断叶子节点
//如果一个值深度最大 但它是右节点 那也算符合题意
//因为即使是右节点 但最大深度只有它一个值 那它就是最左侧的值
if(root.left == null && root.right == null)
{
//深度大于已经记录的深度
if(depth > maxDep)
{
//更新深度 更新值
maxDep = depth;
result = root.val;
}
}
//递归要对深度+1
if(root.left != null)
{
FindDeep(root.left,depth + 1);
}
if(root.right != null)
{
FindDeep(root.right,depth + 1);
}
}
}
//迭代
public class Solution {
public int FindBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new Queue<TreeNode>();
queue.Enqueue(root);
int result = 0;
while(queue.Count != 0)
{
int count = queue.Count;
for(int i = 0; i < count; i++)
{
var temp = queue.Dequeue();
if(i == 0)
{
result = temp.val;
}
if(temp.left != null)
{
queue.Enqueue(temp.left);
}
if(temp.right != null)
{
queue.Enqueue(temp.right);
}
}
}
return result;
}
}
112.路径总和
分析:题目要求找到一个路径,相加起来等于目标值,那这里我们使用递归解决,核心就是每次把节点的值减去,在找到根节点后,值为0说明符合题意。
public class Solution {
public bool HasPathSum(TreeNode root, int targetSum) {
//注意题目要求返回的是bool值
if(root == null) return false;
//一个临时变量 每当传入一个节点 就减去一次
int count = targetSum - root.val;
//找到叶子节点
if(root.left == null && root.right == null)
{
//最后传入叶子节点 变量等于0 符合题意
return count == 0;
}
//递归调用传入临时变量 也就是传入一个节点 目标值减去一个节点的值 这样也更好理解
var left = HasPathSum(root.left,count);
var right = HasPathSum(root.right,count);
//左右都递归 只要满足一个就是符合题意 所以用||
return left || right;
}
}
106.从中序与后序遍历序列构造二叉树(该题还不够理解 请略过)
分析:本题的思路就是将一个数组分别拆分为左右两个数组,代表除了头节点外的左右两边数组。(1)后序遍历是左右中,所以后序数组的最后一个元素就是头节点,那都是以左开始,所以左数组也很容易就确定了
(2)分别对中序数组和后序数组进行切割,分成左右两个数组,就按照(1)的规律递归调用切割数组
(3)代码中有两个小数点 我也不知道是什么写法,搜了半天也没有,我也是看到别人这样写,默认他就是for循环吧,但是声明数组长度为0我很费劲,如果有懂的大神可以解答一下
public class Solution {
public TreeNode BuildTree(int[] inorder, int[] postorder) {
if(inorder.Length == 0 && postorder.Length == 0) return null;
//先找头节点 就是后序数组的最后一个元素
int headVal = postorder[postorder.Length - 1];
TreeNode newTree = new TreeNode(headVal);
//中序左右数组
int[] inorderLeft = new int[0];int[] inorderRight = new int[0];
//后序左右数组
int[] postorderLeft = new int[0];int[] postorderRight = new int[0];
//索引位 标识从第几位开始切割
//那中序数组(左中右)在找到中元素时 就是切割数组的地方
int tempIndex = 0;
for(int i = 0; i < inorder.Length; i++)
{
if(inorder[i] == headVal)
{
tempIndex = i;
break;
}
}
//当索引不为0 说明有左子树
if (tempIndex != 0)
{
//给数组赋值
//中序左数组
inorderLeft = inorder[0..tempIndex];
//后序左数组
postorderLeft = postorder[0..tempIndex];
}
if (tempIndex != inorder.Length - 1)
{
//中序右数组
inorderRight = inorder[(tempIndex + 1)..inorder.Length];
//后序右数组
postorderRight = postorder[tempIndex..(postorder.Length - 1)];
}
newTree.left = BuildTree(inorderLeft,postorderLeft);
newTree.right = BuildTree(inorderRight,postorderRight);
return newTree;
}
}
654.最大二叉树
分析:和上一题类似,就是分割成两个数组,递归调用即可
public class Solution {
public TreeNode ConstructMaximumBinaryTree(int[] nums) {
//使用递归就需要有终止条件
if(nums == null || nums.Length == 0) return null;
//找到最大值
int maxIndex = 0;
int maxValue = 0;
for(int i = 0; i < nums.Length; i++)
{
if(nums[i] > maxValue)
{
maxValue = nums[i];
maxIndex = i;
}
}
//创建节点
TreeNode root = new TreeNode(nums[maxIndex]);
//递归调用
root.left = ConstructMaximumBinaryTree(CopyArray(nums,0,maxIndex - 1));
root.right = ConstructMaximumBinaryTree(CopyArray(nums,maxIndex + 1,nums.Length - 1));
return root;
}
public int[] CopyArray(int[] nums,int left,int right)
{
//递归就要有终止条件
if(left > right) return null;
//通过创建新数组并返回
int length = right - left + 1;
int[] newNums = new int[length];
for(int i = left; i <= right; i++)
{
//i-left 是因为新数组的索引都是从0开始的 right不需要减去 因为新数组长度已经定好了
newNums[i - left] = nums[i];
}
return newNums;
}
}
617.合并二叉树
分析:创建一个新节点,然后将这两个树的值递归合并给新节点就好了
public class Solution {
public TreeNode MergeTrees(TreeNode root1, TreeNode root2) {
if(root1 == null)return root2;
if(root2 == null)return root1;
TreeNode root3 = new TreeNode();
root3.val = root1.val + root2.val;
root3.left = MergeTrees(root1.left, root2.left);
root3.right = MergeTrees(root1.right, root2.right);
return root3;
}
}
700.二叉搜索树中的搜索
分析:二叉搜索树就是 左子树的值都会小于根节点 右子树的值都大于根节点,本题使用递归或者迭代都可以实现,迭代写起来也很简单,不需要使用栈和队列,因为二叉搜索树是已经排序好的,我们直接迭代就好了。
public class Solution {
//递归
public class Solution {
public TreeNode SearchBST(TreeNode root, int val)
{
if (root == null || root.val == val) return root;
//搜索左右两边的时候 记得return
if (root.val > val) return SearchBST(root.left, val);
if (root.val < val) return SearchBST(root.right, val);
//如果都没找到 那就说明没有目标节点
return null;
}
}
//迭代
public TreeNode SearchBST(TreeNode root, int val) {
while(root != null)
{
if(root.val == val) return root;
else if(root.val < val) root = root.right;
else if(root.val > val) root = root.left;
}
return null;
}
}
92.验证二叉搜索树
分析:题目要求左子树的值全小于根节点,右子树的值全大于根节点,那就可以使用中序遍历的方式,将所有的值放到List中,如果List是有序的,说明是二叉搜索树。
public class Solution {
List<int> list = new List<int>();
public bool IsValidBST(TreeNode root)
{
Find(root);
//这里就是判断List是否有序
for(int i = 1;i < list.Count; i++)
{
if(list[i] > list[i - 1])
{
continue;
}
else
{
return false;
}
}
return true;
}
//中序遍历 将所有的值都放入List中即可
public void Find(TreeNode root)
{
if(root == null) return;
if(root.left != null)
Find(root.left);
list.Add(root.val);
if(root.right != null)
Find(root.right);
}
}
530.二叉搜索树的最小绝对差
分析:和上题类似,本题还是根据二叉搜索树的特性,通过中序遍历将所有的元素放入队列中就是有序的,再通过两两比较,取最小的差值返回即可
public class Solution {
List<int> list1 = new List<int>();
public int GetMinimumDifference(TreeNode root) {
//temp采用int的最大值 方便后续进行比较
int temp = int.MaxValue;
Find(root);
for(int i = 1; i < list1.Count; i++)
{
//最大值和很多差值比较 选择一个最小的
temp = Math.Min(temp,list1[i] - list1[i - 1]);
}
return temp;
}
public void Find(TreeNode root)
{
//中序遍历放入List中
if(root == null) return;
if(root != null)
{
Find(root.left);
}
list1.Add(root.val);
if(root != null)
{
Find(root.right);
}
}
}