二叉树的基本算法

二叉树的遍历算法

二叉树的遍历主要分为三种:先序遍历,中序遍历和后序遍历。还有一种就是按照层次遍历。

按照惯例,左孩子优先于右孩子,那么:

先序遍历指的就是先访问本节点,再访问该节点的左孩子和右孩子;

中序遍历指的就是:先访问左孩子,再访问本节点,最后访问右孩子;

后序遍历指的就是:先访问左右孩子,最后访问本节点。

层次遍历:按照树的每一层(高度)进行遍历。

 

本文的实例代码基于JAVA编写

首先给出节点的数据结构

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
 
    TreeNode(int x) {
        val = x;
    }
}
二叉树的遍历
前序遍历

递归解法:

如果二叉树为空,空操作
如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树

List<Integer> list = new ArrayList<Integer>();    
public List<Integer> preorderTraversal(TreeNode root) {
    if (root != null) {
        list.add(root.val);
        if (root.left != null) {
            preorderTraversal(root.left);
        }
        if (root.right != null) {
            preorderTraversal(root.right);
        }
 
    }
    return list;
}


非递归

List<Integer> list = new ArrayList<Integer>();    
public List<Integer> preorderTraversal(TreeNode root) {
    Stack<TreeNode> stack = new Stack<>();
    while (root != null || !stack.isEmpty()) {
        while (root != null) {
            list.add(root.val);
            stack.add(root);
            root = root.left;
        }
        if (!stack.isEmpty()) {
            TreeNode node = stack.pop();
            root = node.right;
        }
    }
    return list;
}
中序遍历
题目参考LeetCode(94)

递归解法:

如果二叉树为空,空操作
如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树
List<Integer> list = new ArrayList<Integer>();    
public List<Integer> inorderTraversal(TreeNode root) {
    if (root != null) {
        if (root.left != null) {
            inorderTraversal(root.left);
        }
        list.add(root.val);
        if (root.right != null) {
            inorderTraversal(root.right);
        }
    }
    return list;
}
非递归解法:用栈先把根节点的所有左孩子都添加到栈内,然后输出栈顶元素,再处理栈顶元素的右子树。

List<Integer> list = new ArrayList<Integer>();    
public List<Integer> inorderTraversal(TreeNode root) {
    Stack<TreeNode> stack = new Stack<>();
    while (root != null || !stack.isEmpty()) {
        while (root != null) {
            stack.add(root);
            root = root.left;
        }
        if (!stack.isEmpty()) {
            root = stack.pop();
            list.add(root.val);
            root = root.right;
        }
    }
    return list;
}
后序遍历
题目参考LeetCode(145)

递归解法:

如果二叉树为空,空操作
如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点
List<Integer> list = new ArrayList<Integer>();
public List<Integer> postorderTraversal(TreeNode root) {
    if (root != null) {
        if (root.left != null) {
            postorderTraversal(root.left);
        }
        if (root.right != null) {
            postorderTraversal(root.right);
        }
        list.add(root.val);
    }
    return list;
}
非递归解法:双栈法。

List<Integer> list = new ArrayList<Integer>();    
public List<Integer> postorderTraversal(TreeNode root) {
    Stack<TreeNode> stack1 = new Stack<>();
    Stack<TreeNode> stack2 = new Stack<>();
    if (root != null) {
        stack1.add(root);
    }
    while (!stack1.isEmpty()) {
        TreeNode node = stack1.pop();
        stack2.add(node);
        if (node.left != null) {
            stack1.add(node.left);
        }
        if (node.right != null) {
            stack1.add(node.right);
        }
    }
    while (!stack2.isEmpty()) {
        list.add(stack2.pop().val);
    }
    return list;
}
层次遍历
思路:分层遍历二叉树(按层次从上到下,从左到右)迭代,相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点,访问,若左子节点或右子节点不为空,将其压入队列。

public static void levelTraversal(TreeNode root){
    if(root == null) {
        return;
    }
    Queue<TreeNode> queue = new LinkedList<>(); // 对列保存树节点
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode temp = queue.poll();
        System.out.print(temp.val + "->");
        if (temp.left != null) { // 添加左右子节点到对列
            queue.add(temp.left);
        }
        if (temp.right != null) {
            queue.add(temp.right);
        }
    }
}
变形:按层保存,题目参考LeetCode(102)

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> list = new ArrayList<List<Integer>>();
    Queue<TreeNode> queue = new LinkedList<>();
    if (root != null) {
        queue.add(root);
    }
    while (!queue.isEmpty()) {
        int size = queue.size();
        List<Integer> l = new ArrayList<>();
        while (size > 0) {
            TreeNode node = queue.poll();
            if (node.left != null) {
                queue.add(node.left);
                l.add(node.left.val);
            }
            if (node.right != null) {
                queue.add(node.right);
                l.add(node.right.val);
            }
            size--;
        }
        list.add(l);
    }
    return list;
}
基础算法
求二叉树中的节点个数
递归解法: O(n)O(n)

如果二叉树为空,节点个数为0
如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 右子树节点个数 + 1
public static int getNodeNumRec(TreeNode root) {
    if (root == null) {
        return 0;
    }
    return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;
}
非递归解法:O(n)O(n)。基本思想同LevelOrderTraversal。即用一个Queue,在Java里面可以用LinkedList来模拟

public static int getNodeNum(TreeNode root) {
    if (root == null) {
        return 0;
    }
    Queue<TreeNode> queue =  new LinkedList<>(); // 用队列保存树节点,先进先出
    queue.add(root);
    int count = 1; // 节点数量
    while (!queue.isEmpty()) {
        TreeNode temp = queue.poll(); // 每次从对列中删除节点,并返回该节点信息
        if (temp.left != null) { // 添加左子孩子到对列
            queue.add(temp.left);
            count++;
        }
        if (temp.right != null) { // 添加右子孩子到对列
            queue.add(temp.right);
            count++;
        }
    }
    return count;
}
求二叉树的深度(高度)
题目参考LeetCode(104)

递归解法: O(n)O(n)

如果二叉树为空,二叉树的深度为0
如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
public int maxDepth(TreeNode root) {
    int d = 0;
    if (root == null) {
        return 0;
    }
    d = Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    return d;
}
非递归解法:O(n)O(n)。基本思想同LevelOrderTraversal。即用一个Queue,在Java里面可以用LinkedList来模拟。

public static int getDepth(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int currentLevelCount = 1; // 当前层的节点数量
    int nextLevelCount = 0; // 下一层节点数量
    int depth = 0; // 树的深度
 
    Queue<TreeNode> queue = new LinkedList<>(); // 对列保存树节点
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode temp = queue.remove(); // 移除节点
        currentLevelCount--; // 当前层节点数减1
        if (temp.left != null) { // 添加左节点并更新下一层节点个数
            queue.add(temp.left);
            nextLevelCount++;
        }
        if (temp.right != null) { // 添加右节点并更新下一层节点个数
            queue.add(temp.right);
            nextLevelCount++;
        }
        if (currentLevelCount == 0) { // 如果是该层的最后一个节点,树的深度加1
            depth++;
            currentLevelCount = nextLevelCount; // 更新当前层节点数量并且重置下一层节点数量
            nextLevelCount = 0;
        }
    }
    return depth;
}
求二叉树第k层的节点个数
递归解法: O(n)O(n)
思路:求以root为根的k层节点数目,等价于求以root左孩子为根的k-1层(因为少了root)节点数目 加上以root右孩子为根的k-1层(因为 少了root)节点数目。即:

如果二叉树为空或者k<1,返回0
如果二叉树不为空并且k==1,返回1
如果二叉树不为空且k>1,返回root左子树中k-1层的节点个数与root右子树k-1层节点个数之和

public static int getNodeNumKthLevelRec(TreeNode root, int k) {
    if (root == null || k < 1) {
        return 0;
    }
    if (k == 1) {
        return 1;
    }
    return getNodeNumKthLevelRec(root.left, k - 1) + getNodeNumKthLevelRec(root.right, k - 1);
}
求二叉树中叶子节点的个数
递归解法:

如果二叉树为空,返回0
如果二叉树是叶子节点,返回1
如果二叉树不是叶子节点,二叉树的叶子节点数 = 左子树叶子节点数 + 右子树叶子节点数
public static int getNodeNumLeafRec(TreeNode root) {
    if (root == null) {
        return 0;
    }
    if (root.left == null && root.right == null) {
        return 1;
    }
    return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right);
}
非递归解法:基于层次遍历进行求解,利用Queue进行。

public static int getNodeNumLeaf(TreeNode root){
    if (root == null) {
        return 0;
    }
    int leaf = 0; // 叶子节点个数
    Queue<TreeNode> queue = new LinkedList<>();
    queue.add(root);
    while (!queue.isEmpty()) {
        TreeNode temp = queue.poll();
        if (temp.left == null && temp.right == null) { // 叶子节点
            leaf++;
        }
        if (temp.left != null) {
            queue.add(temp.left);
        }
        if (temp.right != null) {
            queue.add(temp.right);
        }
    }
    return leaf;
}
判断两棵二叉树是否相同的树
递归解法:

如果两棵二叉树都为空,返回真
如果两棵二叉树一棵为空,另外一棵不为空,返回假
如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假
public static boolean isSameRec(TreeNode r1, TreeNode r2) {
    if (r1 == null && r2 == null) { // 都是空
        return true;
    } else if (r1 == null || r2 == null) { // 有一个为空,一个不为空
        return false;
    }
    if (r1.val != r2.val) { // 两个不为空,但是值不相同
        return false;
    }
    return isSameRec(r1.left, r2.left) && isSameRec(r1.right, r2.right); // 递归遍历左右子节点
}
非递归解法:利用Stack对两棵树对应位置上的节点进行判断是否相同。

public static boolean isSame(TreeNode r1, TreeNode r2){
    if (r1 == null && r2 == null) { // 都是空
        return true;
    } else if (r1 == null || r2 == null) { // 有一个为空,一个不为空
        return false;
    }
    Stack<TreeNode> stack1 = new Stack<>();
    Stack<TreeNode> stack2 = new Stack<>();
    stack1.add(r1);
    stack2.add(r2);
    while (!stack1.isEmpty() && !stack2.isEmpty()) {
        TreeNode temp1 = stack1.pop();
        TreeNode temp2 = stack2.pop();
        if (temp1 == null && temp2 == null) { // 两个元素都为空,因为添加的时候没有对空节点做判断
            continue;
        } else if (temp1 != null && temp2 != null && temp1.val == temp2.val) {
            stack1.push(temp1.left); // 相等则添加左右子节点判断
            stack1.push(temp1.right);
            stack2.push(temp2.left);
            stack2.push(temp2.right);
        } else {
            return false;
        }
    }
    return true;
}
判断二叉树是不是平衡二叉树
递归实现:借助前面实现好的求二叉树高度的函数

如果二叉树为空, 返回真
如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假
public static boolean isAVLTree(TreeNode root) {
    if (root == null) {
        return true;
    }
    if (Math.abs(getDepth(root.left) - getDepth(root.right)) > 1) { // 左右子树高度差大于1
        return false;
    }
    return isAVLTree(root.left) && isAVLTree(root.right); // 递归判断左右子树
}
求二叉树的镜像
递归实现:破坏原来的树,把原来的树改成其镜像

如果二叉树为空,返回空
如果二叉树不为空,求左子树和右子树的镜像,然后交换左右子树
public static TreeNode mirrorRec(TreeNode root) {
    if (root == null) {
        return root;
    }
    TreeNode left = mirrorRec(root.right); // 递归镜像左右子树
    TreeNode right = mirrorRec(root.left);
    root.left = left; // 更新根节点的左右子树为镜像后的树
    root.right = right;
    return root;
}
递归实现:不能破坏原来的树,返回一个新的镜像树

如果二叉树为空,返回空
如果二叉树不为空,求左子树和右子树的镜像,然后交换左右子树
public static TreeNode mirrorCopyRec(TreeNode root) {
    if (root == null) {
        return root;
    }
    TreeNode newRoot = new TreeNode(root.val); // 创建新节点,然后交换左右子树
    newRoot.left = mirrorCopyRec(root.right);
    newRoot.right = mirrorCopyRec(root.left);
    return newRoot;
}
判断两个二叉树是否互相镜像
递归解法:与比较两棵二叉树是否相同解法一致(题5),非递归解法省略。

比较r1的左子树的镜像是不是r2的右子树
比较r1的右子树的镜像是不是r2的左子树
public static boolean isMirrorRec(TreeNode r1, TreeNode r2) {
    if (r1 == null && r2 == null) {
        return true;
    } else if (r1 == null || r2 == null) {
        return false;
    }
    if (r1.val != r2.val) {
        return false;
    }
    // 递归比较r1的左子树的镜像是不是r2右子树
    // 和r1的右子树的镜像是不是r2的左子树
    return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);
}
判断是否为二分查找树BST
题目参考LeetCode(98)

递归解法:中序遍历的结果应该是递增的

public static boolean isValidBST(TreeNode root, int pre){
    if (root == null) {
        return true;
    }
    boolean left = isValidBST(root.left, pre);
    if (!left) {
        return false;
    }
    if(root.val <= pre) {
        return false;
    }
    pre = root.val;
    boolean right = isValidBST(root.right, pre);
    if(!right) {
        return false;
    }
    return true;
}
————————————————
版权声明:本文为CSDN博主「森林屿麓」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013967628/article/details/90341129

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
平衡二叉树是一种特殊的二叉搜索树,它具有以下特点: 1. 左子树和右子树的高度差不超过 1; 2. 左子树和右子树都是平衡二叉树。 常见的平衡二叉树有 AVL 树、红黑树等。 平衡二叉树基本算法验证包括以下部分: 1. 插入节点:在插入节点时,需要保证插入后树仍然是平衡的。具体操作是:首先按照二叉搜索树的插入方法将节点插入到树中,然后从插入节点开始向上逐层检查,如果检查到某个节点不平衡,就进行旋转操作,使得该节点重新平衡。旋转操作包括左旋、右旋、左右旋和右左旋四种,具体实现可参考 AVL 树、红黑树等平衡二叉树的旋转操作; 2. 删除节点:在删除节点时,同样需要保证删除后树仍然是平衡的。具体操作是:先按照二叉搜索树的删除方法将节点删除,然后从删除节点的父节点开始向上逐层检查,如果检查到某个节点不平衡,就进行旋转操作,使得该节点重新平衡; 3. 查找节点:在平衡二叉树中查找节点的方法与二叉搜索树相同,具体操作是从根节点开始,比较待查找节点与当前节点的大小关系,然后根据比较结果向左子树或右子树遍历,直到找到目标节点或遍历到叶子节点为止。 以上就是平衡二叉树基本算法验证的内容。需要注意的是,不同的平衡二叉树可能存在一些细微的差异,具体实现时需要结合具体的平衡二叉树来进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值