void traverse(TreeNode root){
//root需要做什么?
//其他的不需要root操作,抛给递归
traverse(root.left);
traverse(root.right);
}
void plusOne(TreeNode root){
if(root == null){
return ;
}
root.val+=1;
plusOne(root.left);
plusOne(root.right);
}
boolean isSameTree(TreeNode root1,TreeNode root2){
//都为空的话,显然就是相同的
if(roo1 == null && root2 == null){
return true;
}
//一个非空,一个空,显然就不同
if(root1 == null || root2 == null){
return false;
}
//两个都不是空的,假如说val值不一样就证明不同,就不用继续遍历检测了
if(root1.val != root2.val){
return false;
}
//root1和root2该比的都把它给比完
return isSameTree(root1.left,root2.left) && isSameTree(root1.right,root2.right);
}
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
public class IsValidBST {
public boolean isValidBST(TreeNode root) {
return this.isValidBST(root,null,null);
}
/**
* max和min相当于给子树上的所有节点添加了一个min和max的边界,约束root的左子树节点值不超过root的值
* 右子树的节点值不小于root的值,也就符合BST定义
* @param root
* @param min
* @param max
* @return
*/
private boolean isValidBST(TreeNode root,TreeNode min,TreeNode max){//保证root.val在(min,max)之间
//一行一行代码来解释
if(root == null){//如果检索到空节点,那么就证明这条路径上的所有节点都是符合BST定义的,我们认为这一次的搜索是合法的
return true;
}
//min节点代表一个下界,也就是当前检索的节点不得小于等于这个节点的值
if(min!= null && root.val<= min.val){
return false;
}
//max节点代表一个上界,也就是当前检索的节点不得大于等于这个节点的值
if(max!=null && root.val >= max.val){
return false;
}
//这一句非常关键:
//1.规定了遍历方向,从root开始,检索root.left和root.right
//2.当检索root.left的时候,也就是检查它的左子树,那么对于这个节点而言和它的左子树而言,其上边的所有的值都不能够超过root.val,因此max设置为root
// 至于min的话,对于左子树而言,左子树的最小值是否有要求呢?首先它是root的左子树,root要保证它的值不会违反整棵树都是BST这个原则,所以限制root的(l,r)都会继承下去
//3.当检索root.right的时候,也就是检查它的右子树,那么对于这个节点而言和它的右子树而言,其上边的所有的值都不能够低于root.val,因此min设置为root
// 至于max同理,需要继承,因为要求整棵树都得是合理的BST
return isValidBST(root.left,min,root) && isValidBST(root.right,root,max);
}
}
root和一个整数值val,你需要在BST中找到节点值等于val的节点,返回以该节点为根的子树,如果节点不存在,则会返回null public TreeNode searchBST(TreeNode root, int val) {
if(root == null){
return root;
}
//BST的特性,左小右大
if(root.val == val){
return root;
}
if(root.val < val){
//如果要找的值是比root要大的,那么就搜索右子树,因为较大的值在右子树
return searchBST(root.right,val);
}
return searchBST(root.left,val);
}
void BST(TreeNode root,int target){
if(root.val == target){
//找到目标,进行操作
}
if(root.val < target){
BST(root.right,target);
}
if(root.val > target){
BST(root.left,target);
}
}
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
对数据结构的操作无非遍历与访问,遍历就是找,访问就是改,具体到这个问题就是先找到插入的位置,然后进行插入的操作。那么如何来确定插入的位置呢?
遍历BST,找到合适的位置:从root节点开始,按照左小右大的规则找位置,我们为了简化问题,就不做那种从已有节点中强行插入的操作了,我们要的最终效果是找到一个空的位置,按照规则将节点连接上就好了。
同时我们需要注意到的是,由于树节点的限制,它没有给到我们关于父节点的信息,那么连接这个事情交给谁做呢?交给递归即可
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null){
//遍历到合适的位置了,进行插入,注意将节点new出来后是交给递归来连接的
return new TreeNode(val);
}
//题干中说没有这种样例,但是如果有的话就直接返回就行了
if(root.val == val){
return root;
}
//val的值要大,`左小右大,那么我应该将这个节点连接到右子树上`
if(root.val < val){
root.right = insertIntoBST(root.right,val);
}
if(root.val > val){
root.left = insertIntoBST(root.left,val);
}
//完毕之后将链接好的树给返回回去就好了
return root;
}
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
套路是一样的,和插入的操作类似,先找到,然后再改,只是这个改会稍微有些不同
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-etST7mdS-1661225232323)(./image/BST-01.png)]
其实这个问题就是,到底要让哪个节点来接替自己?我们根据左小右大的规则,这时候这个节点的左子节点可以上来,右子节点可以上来,但是真的有这么简单吗?如果随意地让一个节点来接替,那么很有可能就会出现新接替的节点镇不住整棵BST的情况,我们希望能够找到一个节点,这个节点比左子树的所有节点都要大,但是同时要比右子树的所有节点都要小,符合要求的节点就是左子树中最大的节点或者右子树中最大的节点
那么如何来找到到子树中的最小的节点或者最大的节点呢?根据BST的性质,左小右大,一直向左遍历直到叶子节点,那就肯定就是最小的,要找最大的同理
或许可能会问,诶这个右子树中的任意一个节点不是也比左子树的所有节点都要大吗,那我在右边瞎选一个就不行吗,注意,这里要求的是两个同时成立,万一你选出来的这个节点比右子树的一个节点要大咋办?
思路理清楚之后,来看看代码
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null){
return root;
}
if(root.val == key){//遍历查找得到了val为key的节点,进行删除
//情况1:这个节点没有子节点
if(root.left == null && root.right == null){
return null;
}
//情况2:这个节点只有一个子节点
if(root.left == null){//这里省略了root.right != null,因为前一个分支条件语句覆盖掉了
//这时候只需要把右子树给接替上来就好了
return root.right;
}
if(root.right == null){
return root.left;
}
//情况3,左右子树都不是空,这时候我们需要找到一个节点,这个节点可以是左子树最大的节点,可以是右子树最小的节点
TreeNode min = getMin(root.right);//这里找的是右子树的最小节点
//然后进行删除,这里根据BST的数据结构特点来进行操作,注意,无论是最大的节点还是最小的节点,我们发现它都是叶子节点,那么我们剩下就是要删除叶子节点嘛
//我们就是在编写删除操作,于是删除这个事情我们把它交给递归就好了
root.val = min.val;
root.right = deleteNode(root.right,root.val);
}
//否则的话就继续遍历,这里的话key比root的值要大,那么就要遍历值更大的右子树
else if(root.val < key){
root.right = deleteNode(root.right,key);
}else if(root.val > key){
root.left = deleteNode(root.left,key);
}
return root;
}
private TreeNode getMin(TreeNode root){
//向左遍历
while(root.left !=null){
root = root.left;
}
return root;
}
private TreeNode getMax(TreeNode root){
//向右遍历
while(root.right !=null){
root = root.right;
}
return root;
}
}
public TreeNode deleteNode(TreeNode root, int key) {
if(root == null){
return root;
}
if(root.val == key){//遍历查找得到了val为key的节点,进行删除
//情况1:这个节点没有子节点
if(root.left == null && root.right == null){
return null;
}
//情况2:这个节点只有一个子节点
if(root.left == null){//这里省略了root.right != null,因为前一个分支条件语句覆盖掉了
//这时候只需要把右子树给接替上来就好了
return root.right;
}
if(root.right == null){
return root.left;
}
//情况3,左右子树都不是空,这时候我们需要找到一个节点,这个节点可以是左子树最大的节点,可以是右子树最小的节点
TreeNode max = getMax(root.left);//这里找的是左子树的最大节点
//然后进行删除,这里根据BST的数据结构特点来进行操作,注意,无论是最大的节点还是最小的节点,我们发现它都是叶子节点,那么我们剩下就是要删除叶子节点嘛
//我们就是在编写删除操作,于是删除这个事情我们把它交给递归就好了
root.val = max.val;
root.left = deleteNode(root.left,root.val);
}
//否则的话就继续遍历,这里的话key比root的值要大,那么就要遍历值更大的右子树
else if(root.val < key){
root.right = deleteNode(root.right,key);
}else if(root.val > key){
root.left = deleteNode(root.left,key);
}
return root;
}
private TreeNode getMin(TreeNode root){
//向左遍历
while(root.left !=null){
root = root.left;
}
return root;
}
private TreeNode getMax(TreeNode root){
//向左遍历
while(root.right !=null){
root = root.right;
}
return root;
}
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
[普通二叉树的节点计算]
public int countNodes(TreeNode root){
if(root == null){
return 0;
}
return 1+countNodes(root.right)+countNodes(root.left);
}
public int countNodes(TreeNode root){
int h = 0;
//计算树的高度
while(root !=null){
root = root.left;
h++;
}
return (int)Math.pow(2,h-1);
}
满二叉树的节点个数计算方法: 2 h − 1 2^h-1 2h−1,其中h是树的高度
public int countNodes(TreeNode root) {
TreeNode l = root;
TreeNode r = root;
int hl=0,hr=0;
while(l!=null){
l = l.left;
hl++;
}
while(r!=null){
r = r.right;
hr++;
}
if(hr == hl){
int h = hr;
return (int)Math.pow(2,hr)-1;
}
else {
return 1 + countNodes(root.left) + countNodes(root.right);
}
}
hl == hr而立即返回而不会递归下去一棵完全二叉树的两棵子树,至少有一棵会是满二叉树请实现两个函数,分别用来序列化和反序列化二叉树。
你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示:输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
private static final String SEP =",";
private static final String NULL = "#";
private StringBuilder sb;
private void traverse(TreeNode root,StringBuilder stringBuilder){
if(root == null){
stringBuilder.append(NULL).append(SEP);
return;
}
stringBuilder.append(root.val).append(SEP);
traverse(root.left,stringBuilder);
traverse(root.right,stringBuilder);
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
private static final String SEP =",";
private static final String NULL = "#";
private StringBuilder sb;
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
sb = new StringBuilder();
serialize(root,sb);
return sb.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
LinkedList<String> lists = new LinkedList<>();
//1.将字符串转换为列表
String[] nodes = data.split(",");//此处得到的nodes列表就是二叉树的前序遍历结果
//2.如何通过二叉树的前序遍历结果来还原一棵二叉树
for (String node : nodes) {
lists.addLast(node);
}
TreeNode root = deserialize(lists);
return root;
}
private void serialize(TreeNode root,StringBuilder stringBuilder){
if(root == null){
stringBuilder.append(NULL).append(SEP);
return;
}
stringBuilder.append(root.val).append(SEP);
serialize(root.left,stringBuilder);
serialize(root.right,stringBuilder);
}
private TreeNode deserialize(LinkedList<String> nodes){
if(nodes.isEmpty()){
return null;
}
String first = nodes.removeFirst();
if(NULL.equals(first)){
return null;
}
TreeNode root = new TreeNode(Integer.parseInt(first));
root.left = deserialize(nodes);
root.right = deserialize(nodes);
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
private static final String SEP =",";
private static final String NULL = "#";
private StringBuilder sb;
private void serialize(TreeNode root,StringBuilder sb){
if(root == null){
sb.append(NULL).append(SEP);
return;
}
serialize(root.left,sb);
serialize(root.right,sb);
sb.append(root.val).append(SEP);
}
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
sb = new StringBuilder();
serialize(root,sb);
return sb.toString();
}
private TreeNode deserialize(LinkedList<String> lists){
if(lists.isEmpty()){
return null;
}
//寻找root节点,然后跟着走,后序遍历序列中,根节点位于列表的最末端
String last = lists.removeLast();
//然后根据左右根的序列顺序特点反推
if(NULL.equals(last)){
return null;
}
//然后先把根节点给造出来
TreeNode node = new TreeNode(Integer.parseInt(last));
//左右根
node.right = deserialize(lists);
node.left = deserialize(lists);
return node;
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
String[] nodes = data.split(",");
LinkedList<String> list = new LinkedList<>();
for (String node : nodes) {
list.addLast(node);
}
TreeNode root = deserialize(list);
return root;
}
private static final String SEP =",";
private static final String NULL = "#";
private StringBuilder sb;
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
//层级遍历
if(root == null){
return "";
}
sb = new StringBuilder();
//初始化队列,root入队,开始BFS
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode head = queue.poll();
if(head == null){
sb.append(NULL).append(SEP);
continue;
}
sb.append(head.val).append(SEP);
//检查队头元素,由于是需要记录空节点,所以我们需要将空节点给入队
queue.offer(head.left);
queue.offer(head.right);
}
return sb.toString();
}
/**
* root被夹在两颗子树的中间,也就是在nodes列表的中间,是否能够一下子确定下来哪个是根节点与列表的长度有关
* 当是偶数的时候,除于2的就行
* 否则的话还需要借助前序遍历和后续遍历中的任意一个序列的信息来完成
* 在这里我们选用一个BFS的方式来进行确定一棵二叉树
* @return
*/
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.isEmpty()){
return null;
}
String[] nodes = data.split(",");
Queue<TreeNode> queue = new LinkedList<>();
TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
queue.offer(root);
for(int i = 1;i<nodes.length;){
TreeNode head = queue.poll();
String left = nodes[i++];
if(!NULL.equals(left)){
head.left = new TreeNode(Integer.parseInt(left));
queue.offer(head.left);
}else{
head.left = null;
}
String right = nodes[i++];
if(!NULL.equals(right)){
head.right = new TreeNode(Integer.parseInt(right));
queue.offer(head.right);
}else{
head.right =null;
}
}
return root;
}
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//1.假如说就是一根空树,那么都不用找到了
if(null == root){
return null;
}
//2.如果root本身就是p或者q,比如root就是p节点,如果q存在于以root为根的树中,显然root就是最近公共祖先
//就算q不存在以root为根的树中,按照情况3,我们也要返回那个存在的节点给它用
if(root == q || root == p){
return root;
}
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
//1.这时候从底部开始向上回溯,一直回溯到这里,发现这时候刚好p和q异侧不用讲,这时候root肯定就是最近公共祖先
if(left!=null && right!=null){
return root;
}
//2.假如说我遍历到的这个节点,两个都是空的
if(left == null && right == null){
return null;
}
//3.情况3
return left == null ?right : left;
}