文章目录
- 二叉树
- 1 二叉树基本操作
- 2 剑指 Offer 算法题
- 2.1 题目列表
- 2.2 实战
- [剑指 Offer 27. 二叉树的镜像](https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/)
- [剑指 Offer 28. 对称的二叉树](https://leetcode-cn.com/problems/dui-cheng-de-er-cha-shu-lcof/)
- [剑指 Offer 26. 树的子结构](https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/)
- 其他类似的递归操作
- [剑指 Offer 55 - I. 二叉树的深度](https://leetcode-cn.com/problems/er-cha-shu-de-shen-du-lcof/)
- [剑指 Offer 55 - II. 平衡二叉树](https://leetcode-cn.com/problems/ping-heng-er-cha-shu-lcof/)
- [剑指 Offer 32 - I. 从上到下打印二叉树](https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-lcof/)
- [剑指 Offer 32 - II. 从上到下打印二叉树 II](https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-ii-lcof/)
- [剑指 Offer 32 - III. 从上到下打印二叉树 III](https://leetcode-cn.com/problems/cong-shang-dao-xia-da-yin-er-cha-shu-iii-lcof/)
- [剑指 Offer 07. 重建二叉树](https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/)
- [剑指 Offer 37. 序列化二叉树](https://leetcode-cn.com/problems/xu-lie-hua-er-cha-shu-lcof/)
- [剑指Offer 36:二叉搜索树与双向链表](https://cuijiahua.com/blog/2017/12/basis_26.html)
- [剑指Offer 54:二叉搜索树的第k个结点](https://cuijiahua.com/blog/2018/01/basis_62.html)
- [剑指 Offer 68 - I. 二叉搜索树的最近公共祖先](https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/)
- [剑指 Offer 68 - II. 二叉树的最近公共祖先](https://leetcode-cn.com/problems/er-cha-shu-de-zui-jin-gong-gong-zu-xian-lcof/)(注意此时的返回扩展到了二叉树)
- [剑指 Offer 34. 二叉树中和为某一值的路径](https://leetcode-cn.com/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof/)————回溯思想
- 3 其他
二叉树
1 二叉树基本操作
1.1 二叉树定义
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; };
}
1.2 前、中、后序遍历
前中后的区别是,父节点输出的位置顺序
前:父左右
中:左父右
后:左右父
力扣题库:二叉树深度优先遍历
- 0144.二叉树的前序遍历
- 0145.二叉树的后序遍历
- 0094.二叉树的中序遍历
1.2.1 递归形式
//将二叉树以前序遍历的形式存在列表里,中序后序遍历代码一致,只是add的位置变换
ArrayList<Integer> list = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root) {
if(root==null){
return list;
}else{
list.add(root.val);
if(root.left!=null){
preorderTraversal(root.left);
}
if(root.right!=null){
preorderTraversal(root.right);
}
}
return list;
}
1.2.2 非递归,迭代形式
迭代法前序遍历,要保持遍历的深度,所以需要用到一个stack来维持
//注意栈是先进后出,所以需要先把右子节点入栈
public static List<Integer> preOrder2(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode2> stack = new Stack<>();
if(root == null){
return list;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
list.add(node.val);
if(node.right!=null){
stack.push(node.right);
}
if(node.left!= null){
stack.push(node.left);
}
}
return list;
}
迭代法后续遍历,同前序遍历,只需要把顺序反转一下
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode2> stack = new Stack<>();
if(root == null){
return list;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
list.add(node.val);
//注意这里左右子树的新增顺序也发生了变化
if(node.left!= null){
stack.push(node.left);
}
if(node.right!=null){
stack.push(node.right);
}
}
//这句反转的代码参考自leetCode官方,只是反转的操作会使效率降低
Collections.reverse(list);
return list;
}
中序遍历,分析
- 1.我们必须实现一直深度的入左子节点
- 2.中间节点在我们入左子节点的时候就相当于已经入栈
- 3.从左子节点(中间节点)如何跳到右子节点,所以这里需要增加一个
临时节点p
,用于保存当前的节点
public List<Integer> inorderTraversal(TreeNode2 root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode2> stack = new Stack<>();
//用临时节点p来保存中间节点信息
TreeNode2 p = root;
while(!stack.isEmpty() || p!=null){
//当前节点如果不为空,那么一直入栈,并且指向自己的左节点,实现中间节点和左子节点的入栈操作
while(p != null){
stack.push(p);
p = p.left;
}
//左子节点入栈完毕,开始出站。第一个出栈的就是最底层的那一个左子节点,入list
p = stack.pop();
res.add(p.val);
/**
1. 如果当前节点没有右子节点(叶子结点就没有),那么此时p=null
2. 如果当前节点不是叶子节点,实现了对右子节点的跳转输出
3. 所以此时的循环条件是,p非空说明没有遍历完,栈为空则说明出栈完毕,可以结束遍历
*/
p = p.right;
}
return res;
}
1.3 遍历方式
一来先判空
if(root==null){
return null;
}
遍历树的两种方式:递归和辅助栈(队列)
递归:典型应用
|-- 判断对称二叉树、相同二叉树、翻转二叉树、二叉树子树、合并二叉树、平衡二叉树
2.相同二叉树(同理)
public boolean isSameTree(TreeNode p, TreeNode q) {
//双指针,判断
if(p==null && q==null){
return true;
}
if(p == null || q == null){
return false;
}
return (p.val == q.val) && isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
4.合并二叉树
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if(t1==null || t2 ==null){
return t1==null?t2:t1;
}else{
t1.val = t1.val+t2.val;
t1.left = mergeTrees(t1.left,t2.left);
t1.right = mergeTrees(t1.right,t2.right);
return t1;
}
}
辅助栈(队列)
//辅助栈
public void test(){
if(root==null){
return null;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.push(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
if(node.left!=null){
queue.push(node.left);
}
if(node.right!=null){
queue.push(node.right);
}
}
}
2 剑指 Offer 算法题
2.1 题目列表
递归
遍历序列化和反序列化
剑指 Offer 32 - I. 从上到下打印二叉树【广度优先搜索BFS——队列】
剑指 Offer 32 - II. 从上到下打印二叉树 II
剑指 Offer 32 - III. 从上到下打印二叉树III
二叉搜索树
2.2 实战
剑指 Offer 27. 二叉树的镜像
//将所有左右节点进行互换
//递归
public TreeNode mirrorTree(TreeNode root) {
if(root==null){
return null;
}
TreeNode left = mirrorTree(root.left);
TreeNode right = mirrorTree(root.r);
root.left = right;
root.right = left;
return root;
}
//栈
public TreeNode mirrorTree2(TreeNode root) {
if(root==null){
return null;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.push(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if(node.left!=null){
queue.push(node.left);
}
if(node.right!=null){
queue.push(node.right);
}
}
return root;
}
剑指 Offer 28. 对称的二叉树
//用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
//递归法
public boolean isSymmetric(TreeNode root) {
return root==null || symmetric(root.left,root.right);
}
public boolean symmetric(TreeNode p,TreeNode q){
if(p==null && q==null) return true;
if(p==null || q==null || p.val!=q.val) return false;
return symmetric(p.left,q.right) && symmetric(p.right,q.left);
}
//辅助栈
public boolean symmetric(TreeNode p,TreeNode q) {
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(p);
queue.offer(q);
while(!queue.isEmpty()){
TreeNode leftNode = queue.poll();
TreeNode rightNode = queue.poll();
// 1.入队列时就已经维持了对照位置,所以可以直接判断相等关系
if(leftNode==null && rightNode==null) continue;
if(leftNode==null || rightNode==null ||leftNode.val!=rightNode.val){
return false;
}
// 2.入队列
queue.offer(leftNode.left);
queue.offer(rightNode.right);
queue.offer(leftNode.right);
queue.offer(rightNode.left);
}
return true;
}
剑指 Offer 26. 树的子结构
//输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
//1.递归
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A==null || B==null) return false;
//这一步很有讲究 这样其实只是判断了三种情况,并没有递归下去。注意递归的精髓
//return isSame(A,B) || isSame(A.left,B) || isSame(A.right,B);
//isSame()只是一步判断是否相等的操作
return isSame(A,B) || isSame(A.left,B) || isSame(A.right,B);
}
public boolean isSame(TreeNode p,TreeNode q){
// 2.分情况讨论
// 当B为空,说明已经遍历完了,return true
// 当A为空说明越过了A的叶子节点还没有找到,return false
if(q==null) return true;
if(p==null|| p.val!=q.val) return false;
return isSame(p.left,q.left) && isSame(p.right,q.right);
}
其他类似的递归操作
2.相同二叉树(同理)
public boolean isSameTree(TreeNode p, TreeNode q) {
//双指针,判断
if(p==null && q==null){
return true;
}
if(p == null || q == null){
return false;
}
return (p.val == q.val) && isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
4.合并二叉树
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if(t1==null || t2 ==null){
return t1==null?t2:t1;
}else{
t1.val = t1.val+t2.val;
t1.left = mergeTrees(t1.left,t2.left);
t1.right = mergeTrees(t1.right,t2.right);
return t1;
}
}
5.平衡二叉树
理解:左旋:右子树太长了,把它匀到左边。右旋同理。
1.先将根节点设置为新节点,将原左子树还是设为新的左子树
2.再把右子树的左子树设为新的右子树
3.把新节点作为右子树的 left,右子节点作为新的根节点
双旋转:并不是子树直接左旋右旋就能实现平衡
左旋时,右子节点的左子树高度 > 右子节点的右子树高度,要先对子树进行右旋
判断是否为平衡二叉树
public boolean isBalanced2(TreeNode root) {
if(root==null){
return true;
}else{
int leftHeight = depth(root.left);
int rightHeight = depth(root.right);
if(leftHeight-rightHeight>1 || rightHeight-leftHeight>1){
return false;
}else{
return isBalanced(root.left)&&isBalanced(root.right);
}
}
}
BST——> AVL
剑指 Offer 55 - I. 二叉树的深度
// 1.递归+1的深度
// 2.辅助栈
int res=1;
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
return res+Math.max(maxDepth(root.left),maxDepth(root.right));
}
public int maxDepth(TreeNode root){
int count = 0;
if(root==null) return count;
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
// 要维护深度
int size = queue.size();
while(size>0){
TreeNode node = queue.poll();
if(node.left!=null){
queue.offer(node.left);
}
if(node.right!=null){
queue.offer(node.right);
}
size--;
}
count++;
}
return count;
}
剑指 Offer 55 - II. 平衡二叉树
//输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
//1.递归
public boolean isBalanced(TreeNode root) {
if(root==null) return true;
int leftDepth = depth(root.left);
int rightDepth = depth(root.right);
return Math.abs(leftDepth-rightDepth)<=1 &&
isBalanced(root.left) && isBalanced(root.right);
}
public int depth(TreeNode node){
// 基线,为空
if(node==null) return 0;
int left = depth(node.left);
int right = depth(node.right);
return Math.max(left,right)+1;
}
剑指 Offer 32 - I. 从上到下打印二叉树
public int[] levelOrder(TreeNode root) {
if(root==null) return null;
Queue<TreeNode> queue = new LinkedList<>();
ArrayList<Integer> list = new ArrayList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
list.add(node.val);
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
int[] ans = new int[list.size()];
int i = 0;
for (Integer integer : list) {
ans[i++]=(integer);
}
return ans;
}
剑指 Offer 32 - II. 从上到下打印二叉树 II
public List<List<Integer>> levelOrder(TreeNode root) {
if(root==null) return new ArrayList<>();
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> list = new ArrayList<>();
queue.offer(root);
while(!queue.isEmpty()){
int size = queue.size();
ArrayList<Integer> temp = new ArrayList<>();
while(size>0){
TreeNode node = queue.poll();
temp.add(node.val);
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
size--;
}
list.add(temp);
}
return list;
}
剑指 Offer 32 - III. 从上到下打印二叉树 III
//请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root != null) queue.add(root);
while(!queue.isEmpty()) {
LinkedList<Integer> tmp = new LinkedList<>();
for(int i = queue.size(); i > 0; i--) {
TreeNode node = queue.poll();
//充分利用LinkedList是链表的特征。可以双向进行添加
if(res.size() % 2 == 0) tmp.addLast(node.val); // 偶数层 -> 队列头部
else tmp.addFirst(node.val); // 奇数层 -> 队列尾部
if(node.left != null) queue.add(node.left);
if(node.right != null) queue.add(node.right);
}
res.add(tmp);
}
return res;
}
剑指 Offer 07. 重建二叉树
int[] preorder;
Map<Integer,Integer> map;
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
map = new HashMap<>();//1.为方便根据值来get到索引,将数据存到map中
for (int i = 0; i < preorder.length; i++) {
map.put(inorder[i],i);
}
return build(0,0,preorder.length-1);//第一个参数preOrder用,第二个和第三个是inorder用来确定左子树右子树的边界
}
public TreeNode build(int rootIndex,int left,int right){
if(left>right) return null;//1.递归的基线
TreeNode root = new TreeNode(preorder[rootIndex]);
int split = map.get(preorder[rootIndex]);
root.left = build(rootIndex+1,left,split-1);
root.right = build(rootIndex+split-left+1,split+1,right);//如果直接写成split+1,会出现数组越界
return root;// 如:[1 2 3],[3 2 1]和[1 2 3],[2 3 1]这两种情况
}
剑指 Offer 37. 序列化二叉树
public String serialize(TreeNode root) {
// 前中后序遍历都会缺失,无法完全复原,所以以层序遍历的形式来
if(root==null)return "[]";
Queue<TreeNode> queue = new LinkedList<>();
StringBuilder str = new StringBuilder("[");
queue.offer(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
if(node != null) {
str.append(node.val + ",");
queue.add(node.left);
queue.add(node.right);
}
else str.append("null,");
}
str.deleteCharAt(str.length() - 1);
str.append("]");
return str.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.equals("[]")) return null;
// 1.将字符数组还原成数值数组
String[] list = data.substring(1,data.length()-1).split(",");
TreeNode root = new TreeNode(Integer.parseInt(list[0]));
// 老老实实用队列呗
Queue<TreeNode> queue= new LinkedList<>();
int i = 1;
queue.offer(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
// 构建左子树
if(!list[i].equals("null")){
node.left = new TreeNode(Integer.parseInt(list[i]));
queue.offer(node.left);
}
i++;
// 构建右子树
if(!list[i].equals("null")){
node.right = new TreeNode(Integer.parseInt(list[i]));
queue.offer(node.right);
}
i++;
}
return root;
}
剑指Offer 36:二叉搜索树与双向链表
强化一下递归子树的操作,假装理解为递归的操作已经完全ok了,直接使用结果
// 中序遍历递归写法
public void inOrder(TreeNode root){
if(root==null) return ;
// 递归左子树,那么就可以直接理解为,左边已经好了
inOrder(root.left);
System.out.println(root);
// 递归右子树,也可以理解为直接好了
inOrder(root.right);
}
// head用于记录头结点
// pre用于记录当前节点的左子节点
TreeNode pre,head;
public TreeNode treeToDoublyList(TreeNode root) {
if(root==null) return null;
// 1.变换双向链表的操作
dfs(root);
// 2.头尾指针进行指向,此时head已经固定,尾节点就是pre
pre.right = head;
head.left = pre;
return head;
}
public void dfs(TreeNode cur){
if(cur==null) return;
// 1.左子树ok
dfs(cur.left);
// 2.操作中间节点,
if(pre==null){//一直递归左子树,到达左子叶子节点
head = cur;//固定头结点
}else{
pre.right = cur;
}
cur.left = pre;
pre = cur;
// 3.右子树ok
dfs(cur.right);
}
剑指Offer 54:二叉搜索树的第k个结点
// 1.中序遍历反着来,形成倒序数组,计数--为0直接返回
int count,res;
public int kthLargest(TreeNode root, int k) {
this.count = k;
dfs(root);
return res;
}
public void dfs(TreeNode node){
if(node==null || count==0) return;
dfs(node.right);
if(--count==0){
res = node.val;
return ;
}
dfs(node.left);
}
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return null;
if(root.val>p.val && root.val>q.val) return lowestCommonAncestor(root.left,p,q);
else if(root.val<p.val && root.val<q.val) return lowestCommonAncestor(root.right,p,q);
else return root;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//1.保证p是节点大的那个
if(p.val<q.val){
TreeNode temp = p;
p = q;
q = temp;
}
while(root!=null){
if(root.val>p.val) root = root.left;//比大的还大
else if(root.val<q.val) root = root.right;//比小的还小
else break;
}
return root;
}
剑指 Offer 68 - II. 二叉树的最近公共祖先(注意此时的返回扩展到了二叉树)
//方法·一:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
// 判断是否在左子树
TreeNode left = lowestCommonAncestor(root.left, p, q);
// 判断是否在右子树
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left == null) return right;
if (right == null) return left;
else return root;
}
//方法二:自己想
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(isChild(p,q)) return p;
else if(isChild(q,p)) return q;
// 1.当两节点在root的同一侧但是不为子树关系时。返回的应该是root的子节点而不是root
else if(isChild(root.left,p)&& isChild(root.left,q)){
return lowestCommonAncestor(root.left,p,q);
}else if(isChild(root.right,p)&& isChild(root.right,q)){
return lowestCommonAncestor(root.right,p,q);
}else return root;
}
//判断node是否为p的父节点
public boolean isChild(TreeNode node,TreeNode p){
if(node==null) return false;
if(node.val==p.val ) return true;
return isChild(node.left,p)||isChild(node.right,p);
}
剑指 Offer 34. 二叉树中和为某一值的路径————回溯思想
回溯是一种算法思想,可以用递归实现。从问题的某一种可能出发, 搜索从这种情况出发所能达到的所有可能, 当这一条路走到” 尽头 “的时候, 再倒回出发点, 从另一个可能出发, 继续搜索. 这种不断” 回溯 “寻找解的方法, 称作” 回溯法 “。——八皇后问题
递归是一种算法结构,递归会出现在子程序中自己调用自己或间接地自己调用自己。最直接的递归应用就是计算连续数的阶乘,计算规律:n!=(n-1)!*n。
//输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。
LinkedList<List<Integer>> res = new LinkedList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int target) {
recur(root,target);
return res;
}
public void recur(TreeNode node, int target) {
if(node==null) return;
// 先将当前节点加入路径
path.add(node.val);
target-=node.val;
// 1.找到了,入结果
if(target==0 && node.left==null&&node.right==null){
// 要保留原来的路径没有被改变,所以新建
res.add(new LinkedList<>(path));
}
// 2.没找到递归
recur(node.left,target);
recur(node.right,target);
// 3.找不到,回溯回到了上一层的recur,继续找其他节点。
// 很强!很难理解!
path.removeLast();
}
3 其他
3.二叉树的递归解决问题
|-- 最大深度、最小深度、最大宽度、节点个数、二叉树的直径
二叉树的最小深度
public int minDepth(TreeNode root) {
if(root == null)
return 0;
int left = minDepth(root.left);
int right = minDepth(root.right);
//因为我们不知道left还是right的长度是0,所以此时用左加右加1!
return (left == 0 || right == 0) ? left + right + 1 : Math.min(left, right) + 1;
}
二叉树的最大宽度:
每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。
public int widthOfBinaryTree(TreeNode root) {
Queue< PositionTreeNode> queue = new LinkedList();
int dep = 0, left = 0, ans = 0;
queue.offer(new PositionTreeNode(root,0,0));
while(!queue.isEmpty()){
PositionTreeNode treeNode = queue.poll();
//根据满二叉树的特点来标记位置
if(treeNode.node!=null){
queue.offer(new PositionTreeNode(treeNode.node.left,treeNode.depth+1,treeNode.pos*2));
queue.offer(new PositionTreeNode(treeNode.node.right,treeNode.depth+1,treeNode.pos*2+1));
//刚进入下一层时,需要重置最左端节点的位置。
//第一个dep不等于node.dep就说明是第一个进入下一层的节点
//也就是此节点的pos位置就是最左端的节点位置
if(dep!=treeNode.depth){
dep = treeNode.depth;
left = treeNode.pos;
}
ans = Math.max(ans,treeNode.pos-left+1);
}
}
return ans;
}
class PositionTreeNode {
TreeNode node;
int depth, pos;
PositionTreeNode(TreeNode n, int d, int p) {
node = n;
depth = d;
pos = p;
}
}
二叉树的节点个数:
public int getNodeNumRec(TreeNode root) {
if (root == null) {
return 0;
}
return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;
}
4.完全二叉树(叶子节点都在左边)的节点个数
二叉树的基本概念和类型:https://www.cnblogs.com/sunshineliulu/p/7775063.html
public int countNodes(TreeNode root) {
if(root==null){
return 0;
}
int leftDepth = nodeCount(root.left);
int rightDepth = nodeCount(root.right);
if(leftDepth ==rightDepth){
return (1<<leftDepth) + countNodes(root.right);
}else{
return (1<<rightDepth) + countNodes(root.left);
}
}
public int nodeCount(TreeNode node){
int depth=0;
while(node!=null){
depth++;
node = node.left;
}
return depth;
}
二叉树的直径:任意两个结点路径长度中的最大值
int num = 0;
public int diameterOfBinaryTree(TreeNode root){
depth(root);
return num;
}
public int depth(TreeNode node){
if(node==null){
return 0;
}else{
int leftdepth = depth(node.left);
int rightdepth = depth(node.right);
num = Math.max(num,leftdepth+rightdepth);//保存的最大的左子树与右子树深度之和就是最大的直径
return Math.max(leftdepth,rightdepth)+1;//子树的深度就是左子树、右子树中最大值+1
}
}
|-- 二叉树路径和、最大路径和、第k大的节点、两节点的最大距离
1.某路径和:
public boolean hasPathSum2(TreeNode root, int sum) {
if (root == null) {
return false;
} else {
//广度遍历,保存每一层的和
//问题在于原地更新新的和值,所以此时不用ArrayList反而用LInkedList
LinkedList<TreeNode> queue = new LinkedList<>();
LinkedList<Integer> list = new LinkedList<>();
queue.offer(root);
list.offer(root.val);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
int num = list.poll();
//如果此处一直poll下面不判断返回,最后所有都会被poll出去没有元素
if (node.left == null && node.right == null) {
if (num == sum) {
return true;
}
continue;
}
if (node.left != null) {
queue.offer(node.left);
list.offer(node.left.val + num);
}
if (node.right != null) {
queue.offer(node.right);
list.offer(node.right.val + num);
}
}
return false;
}
}
2.第k大节点:二叉搜索树中序遍历出来是有序数组
public int kthLargest2(TreeNode root, int k) {
ArrayList<Integer> list = new ArrayList<>();
inOrder2(root,list);
return list.get(k-1);
}
public void inOrder2(TreeNode node,ArrayList<Integer> list){
if(node==null){
return;
}else{
inOrder2(node.right,list);
list.add(node.val);
inOrder2(node.left,list);
}
}
|-- 公共祖先
1.二叉树的公共祖先:思想在判断两个节点是否在同一棵子树
public TreeNode lowestCommonAncestor236(TreeNode root, TreeNode p, TreeNode q) {
if(root==null || root==p |root==q){
return root;
}
TreeNode left = lowestCommonAncestor236(root.left,p,q);
TreeNode right = lowestCommonAncestor236(root.right,p,q);
if(left == null && right == null) return null;
if(left==null){
return right;
}
if(right==null){
return left;
}
return root;
}
2.二叉树的公共祖先
public TreeNode lowestCommonAncestor235(TreeNode root, TreeNode p, TreeNode q) {
if(p.val<root.val && q.val<root.val){
return lowestCommonAncestor235(root.left,p,q);
}
if(p.val>root.val && q.val>root.val){
return lowestCommonAncestor235(root.right,p,q);
}
return root;
}
|-- N叉树的前、中、后序遍历
N叉树的定义:
class NTreeNode{
int val;
List<NTreeNode> children;
public NTreeNode(int _val, List<NTreeNode> _children) {
val = _val;
children = _children;
}
}
1.前序遍历
//1.递归法
public List<Integer> postorder(NTreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}else{
myposorder(root,res);
return res;
}
}
private void myposorder(NTreeNode node,List<Integer> res){
if(node==null){
return;
}else{
for(NTreeNode nodes:node.children){
myposorder(nodes,res);
}
res.add(node.val);
}
}
//2.迭代法
public List<Integer> postorder2(NTreeNode root) {
List<Integer> list = new ArrayList<>();
if(root==null){
return list;
}
LinkedList<NTreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
NTreeNode node = queue.poll();
list.add(node.val);
for (NTreeNode nodes : node.children) {
queue.offer(nodes);
}
}
Collections.reverse(list);
return list;
}