102. 二叉树的层序遍历
思路:使用queue来辅助实现,queue时先进先出,所以符合我们要的层序顺序
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list=new ArrayList<>();
if(root==null)
return list;
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
int size=queue.size();
List<Integer> path=new ArrayList<>();
while(size>0){
TreeNode cur=queue.poll();
path.add(cur.val);
if(cur.left!=null)
queue.add(cur.left);
if(cur.right!=null)
queue.add(cur.right);
size--;
}
list.add(new ArrayList<>(path));
}
return list;
}
107. 二叉树的层序遍历 II
思路:跟上面一题类似,只是结果要反过来。可以多用一个List(reverse)暂存结果,然后最后用for倒序取出该List中的元素放到最终要返回的集合List(res)中。
public List<List<Integer>> levelOrderBottom(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
if(root==null)
return res;
queue.add(root);
List<List<Integer>> reverse=new ArrayList<>();
while(!queue.isEmpty()){
int size=queue.size();
List<Integer> list=new ArrayList<>();
while(size>0){
TreeNode cur=queue.poll();
list.add(cur.val);
if(cur.left!=null)
queue.add(cur.left);
if(cur.right!=null)
queue.add(cur.right);
size--;
}
reverse.add(list);
}
//倒序遍历,取出元素
for(int i=reverse.size()-1;i>=0;i--){
res.add(reverse.get(i));
}
return res;
}
199. 二叉树的右视图
思路:注意,可能右子树为空,左子树有值,此时站在右边看的话看到的值是左子树的哦!其实就是获取每层queue中最后一个元素就是离我们最近的值!也就是当size=1时,我们要将结果加入集合中!
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
if(root==null)
return res;
queue.add(root);
while(!queue.isEmpty()){
int size=queue.size();
while(size>0){
TreeNode cur=queue.poll();
if(cur.left!=null)
queue.add(cur.left);
if(cur.right!=null)
queue.add(cur.right);
//判断size是否为1
if(size==1)
res.add(cur.val);
size--;
}
}
return res;
}
637. 二叉树的层平均值
思路:跟前面其实差不多,只是现在变成获取每一层的平均值,所以要记录每一层个数(len),然后总和(sum)。在每一层结束时进行平均值计算,然后加入集合中
public List<Double> averageOfLevels(TreeNode root) {
List<Double> res=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
if(root==null)
return res;
queue.add(root);
while(!queue.isEmpty()){
int size=queue.size();
int len=size;
Double sum=0.0;
while(size>0){
TreeNode cur=queue.poll();
sum+=cur.val;
if(cur.left!=null)
queue.add(cur.left);
if(cur.right!=null)
queue.add(cur.right);
size--;
}
res.add(sum/len);
}
return res;
}
思路:注意Node的属性为:
- int val
- List< Node > children
现在是n叉树,所以在把当前节点的子节点加入queue时,不能单纯用left和right了,要根据children的大小来遍历子节点!
public List<List<Integer>> levelOrder(Node root) {
List<List<Integer>> result=new ArrayList<>();
if(root==null)
return result;
Queue<Node> queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
List<Integer> list=new ArrayList<>();
int size=queue.size();
while(size>0){
Node node=queue.poll();
//注意:获得存放子节点集合的大小,得知有几个子节点
int childSize=node.children.size();
list.add(node.val);
for(int i=0;i<childSize;i++){
if(node.children.get(i)!=null)
queue.add(node.children.get(i));
}
size--;
}
result.add(list);
}
return result;
}
515. 在每个树行中找最大值
思路:思路就不多说了,跟前面差不多!
public List<Integer> largestValues(TreeNode root) {
List<Integer> res=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
if(root==null)
return res;
queue.add(root);
while(!queue.isEmpty()){
int size=queue.size();
int max=Integer.MIN_VALUE;
while(size>0){
TreeNode cur=queue.poll();
if(cur.left!=null)
queue.add(cur.left);
if(cur.right!=null)
queue.add(cur.right);
max=Math.max(cur.val,max);
size--;
}
res.add(max);
}
return res;
116. 填充每个节点的下一个右侧节点指针(提供两种解答)
思路:可以使用层次或递归的方式解决这题!
层次遍历:大部分步骤都是跟之前一样的,只是现在每一层要进行遍历时,需要有一个节点pre记录上一个节点!(在while循环外面声明pre。)
- 每一层开始的第一个节点,此时pre是null的所以不会进行操作
- 之后从queue中获取到的每一个节点,pre的next指针会指向当前节点!
- 将当前节点的子节点加入queue中
- 然后pre=当前节点
层次遍历:
public Node connect(Node root) {
Queue<Node> queue=new LinkedList<>();
if(root==null)
return root;
Node node=root;
queue.add(node);
while(!queue.isEmpty()){
int size=queue.size();
Node pre=null;
while(size>0){
Node cur=queue.poll();
if(pre!=null){
pre.next=cur;
}
if(cur.left!=null)
queue.add(cur.left);
if(cur.right!=null)
queue.add(cur.right);
pre=cur;
size--;
}
}
return root;
}
思路:递归的思路是怎样的呢?稍微有点抽象。首先了解:完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点
- 先处理当前节点(cur)
- 左节点next处理:如果cur.left不为null(cur一定有2子节点),则将左节点的next指向cur.right
- 右节点next处理:如果cur.next不为null,表示有节点(根据题目,该节点也一定有2子节点才能确保叶子节点都在同一层)!将next指向当cur.next.left
- 处理左节点
- 处理右节点
递归:
public Node connect(Node root) {
if(root==null)
return root;
if(root.left!=null){
//当前节点的左边连起来
root.left.next=root.right;
//当前节点的右边尝试连起来
if(root.next!=null){
root.right.next=root.next.left;
}
}
connect(root.left);
connect(root.right);
return root;
}
117. 填充每个节点的下一个右侧节点指针 II
思路:这题跟上面116的区别在于!不是完美二叉树,所以叶子节点不一定都在同一层!父节点也不一定有2个子节点!
所以判断条件变多了,用递归处理比较方便。并且注意⚠️:这里要先递归右边!!而不是先左边!因为要把next给连接好!
public Node connect(Node root) {
if(root==null)
return root;
//设置左节点的next指针
if(root.left!=null && root.right!=null)
root.left.next=root.right;
else if(root.left!=null && root.right==null)
root.left.next=getNext(root.next);
//设置右节点的next指针
if(root.right!=null)
root.right.next=getNext(root.next);
//把右边的next关系都搞定好
connect(root.right);
connect(root.left);
return root;
}
//找离我最近的那个节点!
public Node getNext(Node root){
if(root==null)
return root;
if(root.left!=null)
return root.left;
if(root.right!=null)
return root.right;
if(root.next!=null)
return getNext(root.next);
return null;
}
226. 翻转二叉树
思路:用递归实现的。
public TreeNode invertTree(TreeNode root) {
//递归终止条件
if(root==null)
return root;
//准备交换
TreeNode tmp=root.left;
//递归得到交换好的节点,赋值给左节点
root.left=invertTree(root.right);
//递归得到交换好的节点,赋值给右节点
root.right=invertTree(tmp);
return root;
}
101. 对称二叉树
思路:对称判断,如果当前两个节点相同才继续往下比较:是对称比较所以要注意不是每个节点的右子节点or左子节点进行比较!
public boolean isSymmetric(TreeNode root) {
if(root==null)
return true;
return backTraversal(root.left,root.right);
}
public boolean backTraversal(TreeNode node1,TreeNode node2){
if(node1==null && node2==null)
return true;
if(node1!=null && node2==null)
return false;
if(node1==null && node2!=null)
return false;
if(node1.val!=node2.val)
return false;
//注意选取的节点一个是left一个是right
boolean b1=backTraversal(node1.left,node2.right);
boolean b2=backTraversal(node1.right,node2.left);
return b1&&b2;
}
104. 二叉树的最大深度
思路:
- 二叉树节点的深度:从根节点到该节点的最长简单路径边的条数或者节点数
- 二叉树节点的高度:从该节点到叶子节点的最长简单路径边的条数或者节点数
- 使用前序求的就是深度,使用后序求的是高度。
使用后序,并且求的范围是从根节点到叶子节点的高度,其实就是求二叉树的最大深度(根节点到叶子节点)
public int maxDepth(TreeNode root) {
if(root==null)
return 0;
int left=maxDepth(root.left);
int right=maxDepth(root.right);
//+1是加上当前节点的高度
return Math.max(left,right)+1;
}
559. N 叉树的最大深度
思路:也是求最大深度的,但它是n叉节点!所以在获取子节点传回来的高度时要注意一下获取方式会不同
public int maxDepth(Node root) {
if(root==null)
return 0;
int max=0;
for(Node child : root.children){
//递归子节点
max=Math.max(max,maxDepth(child));
}
//得到从子节点传回来的高度,现在要加上当前节点的高度
return max+1;
}
111. 二叉树的最小深度
思路:最小深度是从根节点到最近叶子节点的最短路径上的节点数量,这题如果用后序遍历,要注意有陷阱,不能是上面的写法直接将Math.max改为min!这样就中招了。因为找到的不是叶子节点
看清题目,是到叶子节点:没有左右子节点的才叫叶子节点!
public int minDepth(TreeNode root) {
//终止条件
if(root==null)
return 0;
//左
int left=minDepth(root.left);
//右
int right=minDepth(root.right);
//中
// 当一个左子树为空,右不为空,这时的节点并不是叶子节点
if(root.left==null && root.right!=null)
return right+1;
// 当一个右子树为空,左不为空,这时的节点并不是叶子节点
if(root.left!=null && root.right==null)
return left+1;
//左子树和右子树都为空,表示的节点为叶子节点。
return Math.min(left,right)+1;
}
简化一下:
public int minDepth(TreeNode root) {
//终止条件
if(root==null)
return 0;
//左
int left=minDepth(root.left);
//右
int right=minDepth(root.right);
//中
// 当一个左子树为空,右不为空(可能为空但不重要),这时的节点并不是(最低点)叶子节点
if(root.left==null)
return right+1;
// 当一个右子树为空,左不为空,这时的节点并不是(最低点)叶子节点
if(root.right==null)
return left+1;
//左子树和右子树都不为空,表示的节点不是(最低点)叶子节点
return Math.min(left,right)+1;
}
222. 完全二叉树的节点个数
思路:求节点数,也是要遍历所有节点,所以其实跟求深度差不多的写法,只是return时不一样:
- 深度是只获取一边的结果
- 而节点个数是两边都要!这样才能计算所有节点个数
public int countNodes(TreeNode root) {
if(root==null)
return 0;
int left=countNodes(root.left);
int right=countNodes(root.right);
//加上当前节点
return left+right+1;
}
110. 平衡二叉树
思路:根据题意,是每个节点的左右子树高度差不超过1,所以用递归处理。同时需要一个计算高度的方法。
public boolean isBalanced(TreeNode root) {
if(root==null)
return true;
if(Math.abs(getHeight(root.left)-getHeight(root.right))>1)
return false;
return isBalanced(root.left)&& isBalanced(root.right);
}
//计算高度
public int getHeight(TreeNode root){
if(root==null)
return 0;
int left=getHeight(root.left);
int right=getHeight(root.right);
return Math.max(left,right)+1;
}
257. 二叉树的所有路径(回溯)
思路:按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。 回溯思想!回溯:递归+回退。
- 需要有一个集合暂时记录目前走过的节点,用
list集合,名字叫path - 经过的每一个节点都要记录起来所以一开始就
path.add - 判断是否找到符合的结果之一,题目要求走到叶子节点才算终止,所以判断条件就是当前节点是否为叶子节点!如果是表示走完一条路径可以加入最终集合了!
- 进行递归+回退,这里会判断下一个要走的节点是否为null,如果不为
null才会进行递归!所以一开始加入节点时,path不会加入null
List<String> list;
List<Integer> path;
public List<String> binaryTreePaths(TreeNode root) {
list=new ArrayList<>();
path=new ArrayList<>();
backTrack(root,list,path);
return list;
}
public void backTrack(TreeNode root,List<String> list,List<Integer> path){
path.add(root.val);
if(root.left==null && root.right==null){
StringBuilder sb=new StringBuilder();
for(int i=0;i<path.size()-1;i++){
sb.append(path.get(i));
sb.append("->");
}
sb.append(path.get(path.size()-1));
list.add(sb.toString());
return;
}
//这里会过滤掉节点为null的情况
if(root.left!=null){
backTrack(root.left,list,path);
path.remove(path.size()-1);
}
//这里会过滤掉节点为null的情况
if(root.right!=null){
backTrack(root.right,list,path);
path.remove(path.size()-1);
}
}
404. 左叶子之和
思路:获取每一个左叶子的值
- 先判断
当前节点.left如果不为空时表示有可能为左叶子 - 进一步判断:
当前节点.left.left和当前节点.left.right都为空时,当前节点.left就是我们要找的左叶子!
public int sumOfLeftLeaves(TreeNode root) {
if(root==null)
return 0;
int leftSum=0;
leftSum+=sumOfLeftLeaves(root.left);
leftSum+=sumOfLeftLeaves(root.right);
if(root.left!=null && root.left.left==null && root.left.right==null)
leftSum+=root.left.val;
return leftSum;
}
513. 找树左下角的值
思路:要找最后一行,最左边的节点。提供递归版和层序版。 递归版:
- 找最后一行就是找到最大深度,那么如何找到最左边的节点,那就是先从左边找起来。
- 每次来到最新的当前最高深度时,此时只会记录第一个遇到的节点(如果我们先从左边找,那记录到的就是当前最左边)
int leftSum=0;
int maxDep=0;
public int findBottomLeftValue(TreeNode root) {
if(root==null)
return leftSum;
backTrack(root,1);
return leftSum;
}
public void backTrack(TreeNode root,int curDep){
if(root.left==null && root.right==null){
//第一次来到目前最深层(最底层)
if(maxDep<curDep){
//记录一个节点就是最左边
leftSum=root.val;
maxDep=curDep;
}
return;
}
//先从左边找起!这样记录到的第一个leftSum才是最左边
if(root.left!=null)
backTrack(root.left,curDep+1);
if(root.right!=null)
backTrack(root.right,curDep+1);
}
层序遍历版:
- 因为是一层层遍历的,所以记录每一层第一个节点!这样到最后一行时,就自然记录到了最后一行、最左边的节点
public int findBottomLeftValue(TreeNode root) {
if(root==null)
return 0;
int leftNum=0;
Queue<TreeNode> queue=new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
TreeNode node=queue.peek();
//记录新一层的第一个节点
leftNum=node.val;
int size=queue.size();
while(size>0){
node=queue.poll();
if(node.left!=null)
queue.add(node.left);
if(node.right!=null)
queue.add(node.right);
size--;
}
}
return leftNum;
}
112. 路径总和
思路:涉及到回溯,但这里跟之前不一样,有返回值!因为我只要找到一个答案即可返回!不需要遍历所有
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root==null)
return false;
return backTrack(root,targetSum);
}
public boolean backTrack(TreeNode root, int targetSum){
if(root.left==null && root.right==null){
if(root.val==targetSum){
return true;
}
return false;
}
if(root.left!=null){
if(hasPathSum(root.left,targetSum-root.val))
return true;
}
if(root.right!=null){
if(hasPathSum(root.right,targetSum-root.val))
return true;
}
return false;
}
106. 从中序与后序遍历序列构造二叉树
思路:根据中序(中左右)和后序(左右中)的规律,可得知中间节点的位置!
- 可以得知在后序时,中间点都是最后一个,中序时先左边再中节点
- 所以通过for循环遍历中序,每个元素跟后序最后一个元素(中节点)比较,相同表示在中序中找到中节点的位置,记录下标跳出
- 创建中节点root
- 递归找出左右节点
- 注意⚠️:这里用到了
Arrays.CopyOfRange(数组,起始位置,结束位置),实际范围不含结束位置
- 注意⚠️:这里用到了
public TreeNode buildTree(int[] inorder, int[] postorder) {
//终止条件
if(inorder.length==0 || postorder.length==0)
return null;
//left mid right
//left right mid
int index=0;
for(int i=0;i<inorder.length;i++){
if(inorder[i]==postorder[postorder.length-1]){
index=i;
break;
}
}
TreeNode root=new TreeNode(postorder[postorder.length-1]);
root.left=buildTree(Arrays.copyOfRange(inorder,0,index),Arrays.copyOfRange(postorder,0,index));
root.right=buildTree(Arrays.copyOfRange(inorder,index+1,inorder.length),Arrays.copyOfRange(postorder,index,postorder.length-1));
return root;
}
这一题跟他很像,步骤差不多只需要根据中节点位置判断即可:105. 从前序与中序遍历序列构造二叉树
654. 最大二叉树
思路:这题其实也一样,只是变成,每个数组中最大的值才是中节点,以中节点为分界分出左边树和右边数。每个树也是按照上面的逻辑来找出各自的中节点。
public TreeNode constructMaximumBinaryTree(int[] nums) {
if(nums.length==0)
return null;
int idx=0;
int max=nums[0];
for(int i=1;i<nums.length;i++){
if(nums[i]>max){
idx=i;
max=nums[i];
}
}
TreeNode root=new TreeNode(nums[idx]);
root.left=constructMaximumBinaryTree(Arrays.copyOfRange(nums,0,idx));
root.right=constructMaximumBinaryTree(Arrays.copyOfRange(nums,idx+1,nums.length));
return root;
}
617. 合并二叉树
思路:先判断如果有一个节点为null,则返回另一个节点即可。否则表示两个节点都有值,要进行相加。
- 先处理中节点
- 再处理左右
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1==null)
return root2;
if(root2==null)
return root1;
TreeNode root=new TreeNode(root1.val+root2.val);
root.left=mergeTrees(root1.left,root2.left);
root.right=mergeTrees(root1.right,root2.right);
return root;
}
700. 二叉搜索树中的搜索
思路:二叉搜索树,有一个特性,左边节点<中节点<右边节点。节点的左边子树恒小于中节点,右子树恒大于该节点。
所以要有目的的去选择往哪边搜寻!如果val大于中节点则往右找,反之往左找。
public TreeNode searchBST(TreeNode root, int val) {
//终止条件
if(root==null)
return root;
//往哪边遍历
if(root.val<val)
return searchBST(root.right,val);
if(root.val>val)
return searchBST(root.left,val);
//找到val,返回节点
else
return root;
}
98. 验证二叉搜索树
思路:这题要明白树的特性(上一题有讲到:左边节点<中节点<右边节点)。
因此用中序遍历,可以得到一个升序的数组,通过中序遍历来判断每个节点是否符合二叉搜索树的特性。
这里注意,需要一个节点记录上一个节点pre!
TreeNode pre=null;
public boolean isValidBST(TreeNode root) {
if(root==null)
return true;
return backTraversal(root);
}
public boolean backTraversal(TreeNode root){
//终止条件
if(root==null)
return true;
//左
boolean bl= backTraversal(root.left);
//中
if(pre!=null){
if(pre.val>=root.val){
return false;
}
}
pre=root;
//右
boolean br=backTraversal(root.right);
return bl && br;
}
530. 二叉搜索树的最小绝对差
思路:再复习一遍特性:左边节点<中节点<右边节点,用中序遍历可以得到升序数组。
这里要求最小绝对差,那肯定是从数字相邻的两个数可以得到最小差,升序数组就是符合这样的条件。所以用中序遍历!
class Solution {
TreeNode pre=null;
int result=Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
backTraversal(root);
return result;
}
public void backTraversal(TreeNode root){
//终止条件
if(root==null)
return;
//左
backTraversal(root.left);
//中
if(pre!=null){
result=Math.min(result,root.val-pre.val);
}
pre=root;
//右
backTraversal(root.right);
}
}
501. 二叉搜索树中的众数
思路:要求的是众数而且有可能有多个,所以需要一个集合来存储结果,创建时还不确定有几个结果,所以先用list来存放,最后再转换成数组。
此外还需要一个变量count记录:当前数字出现的数量、maxCount记录:遍历过程中遇到的最大众数(相同数字出现的数量,最大是出现几次)
因为二叉搜索树的特性,所以有相同值的话,肯定是相连的,所以用中序遍历! 中节点的处理要注意下:
- 需要判断当前数字是否和上一个节点相同,如果相同则count+1,否则count重置为1
- 判断count和maxCount,如果相同则list加入当前数字,否则清空list然后加入当前数字,然后更新maxCount
class Solution {
TreeNode pre=null;
int count=0;
int maxCount=0;
List<Integer> list=new ArrayList<>();
public int[] findMode(TreeNode root) {
if(root==null)
return new int[0];
backTraversal(root);
return list.stream().mapToInt(Integer::intValue).toArray();
}
public void backTraversal(TreeNode root){
if(root==null)
return;
//左
backTraversal(root.left);
//中
if(pre==null || root.val!=pre.val)
count=1;
else if(pre.val==root.val)
count++;
if(count==maxCount)
list.add(root.val);
else if(count>maxCount){
list.clear();
list.add(root.val);
maxCount=count;
}
pre=root;
//右
backTraversal(root.right);
}
}
236. 二叉树的最近公共祖先
思路:要找到p和q的公共祖先,找到即可返回,所以这里的backTraversal方法要有返回值!
既然要找到这两个节点的共同祖先,则需要先找到这两个节点在哪里,一找到就返回该节点给递归上一层,交给中间节点处理。
所以这里要使用后序遍历,中间节点要进行判断处理:
- 如果左右节点都不为空,表示找到两个节点,此时的中节点就是他们的最近祖先!
- 左节点为空,右节点不为空,表示只找到一个节点,返回右节点
- 反之返回左节点
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return backTraversal(root,p,q);
}
public TreeNode backTraversal(TreeNode root,TreeNode p,TreeNode q){
if(root==null || root==p || root==q){
return root;
}
//左
TreeNode left=backTraversal(root.left,p,q);
//右
TreeNode right=backTraversal(root.right,p,q);
//中
if(left!=null && right!=null){
return root;
}
if(left==null && right!=null){
return right;
}
else
return left;
}
}
235. 二叉搜索树的最近公共祖先
思路:二叉搜索树是有序的,所以要找到两个节点的最近祖先,它一定是介于两个节点之间的值!因此要往哪边找祖先,可以通过比较大小得知:
- 如果两个节点均小于当前节点,则表示祖先要往左边去找。
- 如果两个节点均大于当前节点,则表示祖先要往右边去找。
- 如果不符合上述判断则表示当前节点是介于两个节点之间!当前节点就是要找的祖先!
- 记得要有终止条件
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//终止条件
if(root==null)
return root;
if(root.val>p.val && root.val>q.val){
return lowestCommonAncestor(root.left,p,q);
}
if(root.val<p.val && root.val<q.val){
return lowestCommonAncestor(root.right,p,q);
}
else
return root;
}
}
701. 二叉搜索树中的插入操作
思路:注意因为要插入节点,所以已有节点要衔接住这个新节点!题目说可以随便插入,所以在符合二叉搜索树的特性下,将新节点加入到树的末尾即可!
- 如果当前节点大于要插入节点值,则新节点肯定是插入到当前节点的左边,由于插入了元素,所以要记得衔接住插入的元素。
- 反之,插入到当前节点的右边,
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
return backTraversal(root,val);
}
public TreeNode backTraversal(TreeNode root,int val){
if(root==null){
//创建新节点
return new TreeNode(val);
}
if(root.val>val){
//衔接新节点
root.left=backTraversal(root.left,val);
}
else if(root.val<val){
//衔接新节点
root.right=backTraversal(root.right,val);
}
//最终返回插入好节点的树
return root;
}
}
思路:
450. 删除二叉搜索树中的节点
思路:跟上一题插入元素一样类似的步骤,节点依旧需要衔接住修改后的节点。
找到要删除的节点时需要注意!删除的当前节点可能:
- 拥有左和右两个子节点!此时需要返回新的中节点(从左右子节点中选一个替补上去),这里选择由右子节点补上去作为新的中间节点,原来的左子节点则衔接在新节点的右边最下方即可!
- 只拥有左子节点,则返回它即可
- 只拥有右子节点,返回它即可
- 没有子节点,返回null即可
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
return backTraversal(root,key);
}
public TreeNode backTraversal(TreeNode root,int key){
//终止条件
if(root==null)
return root;
//中
if(root.val==key){
//left && right==null
if(root.left==null && root.right==null)
return null;
//left==null right!=null
else if(root.left==null && root.right!=null)
return root.right;
//left!=null right==null
else if(root.left!=null && root.right==null)
return root.left;
//left && right !=null
else{
TreeNode preLeft=root.left;
root=root.right;
TreeNode node=root;
while(node.left!=null){
node=node.left;
}
node.left=preLeft;
return root;
}
}
//左
if(root.val>key){
root.left=backTraversal(root.left,key);
}
//右
else if(root.val<key)
root.right=backTraversal(root.right,key);
return root;
}
}
669. 修剪二叉搜索树
思路:二叉搜索树的特性,所以可以根据当前节点是超过最大/最小边界来判断要返回哪边的节点合适!
如何处理当前节点:
- 当前节点小于最小边界,表示该节点和左子节点均不符合边界要求,所以要往右子节点找符合边界要求的节点返回
- 当前节点大于最大边界,表示该节点和右子节点均不符合边界要求,所以要往左子节点找符合边界要求的节点返回
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
return backTraversal(root,low,high);
}
public TreeNode backTraversal(TreeNode root,int low,int high){
if(root==null)
return root;
//中
//表示当前节点的左边均不符合最小边界的要求,所以要往右边找,才能不超过区间
if(root.val<low){
return backTraversal(root.right,low,high);
}
//表示当前节点的右边均不符合最大边界的要求,所以要往左边找,才能不超过区间
if(root.val>high){
return backTraversal(root.left,low,high);
}
//左
root.left=backTraversal(root.left,low,high);
//右
root.right=backTraversal(root.right,low,high);
return root;
}
}
108. 将有序数组转换为二叉搜索树
思路:每一次都是取数组中间的值,作为中节点。数组左边所有值为节点的左子树所有值,右边为右子树所有值。依此类推生成每一个节点,直到数组为空!
因为二叉搜索树特性左节点<中节点<右节点,所以取数组中间的值后,其左边恒小于它,右边恒大于它!符合该特性。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
if(nums.length==0)
return null;
int idx=nums.length/2;
TreeNode root=new TreeNode(nums[idx]);
root.left=sortedArrayToBST(Arrays.copyOfRange(nums,0,idx));
root.right=sortedArrayToBST(Arrays.copyOfRange(nums,idx+1,nums.length));
return root;
}
}
538. 把二叉搜索树转换为累加树
思路:这题没什么难度,搞清楚遍历顺序就行,如何遍历?根据累加顺序:发现是先加右节点,再加中节点,再加左节点。所以能得知遍历顺序应该是右中左!
class Solution {
TreeNode pre=null;
public TreeNode convertBST(TreeNode root) {
if(root==null)
return root;
//右
convertBST(root.right);
//中
if(pre!=null){
root.val+=pre.val;
}
pre=root;
//左
convertBST(root.left);
return root;
}
}