关于树的一些算法题---持续更新

树的定义为:

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}
1、求一棵树的高度:

    public int height(TreeNode root) {
        if(root == null) return 0;
        return Math.max(height(root.left), height(root.right)) + 1;
    }
2、判断一棵树是否为平衡树:

    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        return Math.abs(height(root.left) - height(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
    }
3、删除二叉搜索树中的某个结点:

public TreeNode deleteNode(TreeNode root, int key) {
    if(root == null){
        return null;
    }
 
    if(key < root.val) {
        root.left = deleteNode(root.left, key);
    } else if (key > root.val) {
        root.right = deleteNode(root.right, key);
    } else {
        if(root.left == null) {
            return root.right;
        } else if(root.right == null) {
            return root.left;
        }  // 上面两个判断处理了待删除结点是叶子结点或者只有一个孩子结点的情况
        
        // 下面处理待删除结点有两个孩子结点的情况
        TreeNode minNode = findMin(root.right);  // 首先找到右子树中最小的结点
        root.val = minNode.val;                  // 替换该节点,替换后是满足二叉搜索树的性质
        root.right = deleteNode(root.right, root.val); // 删除替换结点
    }
    return root;
}
 
private TreeNode findMin(TreeNode node){
    while(node.left != null) {
        node = node.left;
    }
    return node;
}
 4、计算二叉树结点的个数:

    public int countNodes(TreeNode root) {
        if(root == null) return 0;
        return 1 + countNodes(root.left) + countNodes(root.right);
    }
 5、将一棵BST中的每个节点值加上所有大于该节点值的值:

class Solution {
    private int sum = 0;
    public TreeNode convertBST(TreeNode root) {
        if(root != null) {
            convertBST(root.right);
            sum += root.val;
            root.val = sum;
            convertBST(root.left);
        }
        return root;
    }
}
很明显,上面是先遍历右子树,再遍历根节点,最后在遍历左子树。与我们经常使用的先序、中序、后序都不太相同,在遍历时,左、根、右结点可根据自己的需求随意的变换,可不要被先序、中序、后序给思维僵化了,认为树的遍历方式只有这几种了。

6、打印二叉树。

 打印二叉树,格式如下:

从上面的的示例就可以看出本题打印二叉树的格式了。

分析:既然要打印二叉树,显然是需要遍历一遍二叉树才行的。常见遍历二叉树的方法有:前序遍历,中序遍历,后续遍历以及层序遍历。按照此题的特点应该选择层序遍历是比较符合要求的。首先,我们求出树的高度h,这样就能知道最终返回结果的行数了。并且也知道每行的列数col=(1 << h)-1。接下来的任务就是在每一列合适的位置插入二叉树的结点值了。观测发现,若输入的二叉树是一棵满二叉树,第一行的首元素在 col / 2 处,第二行的首元素在 col / 4处,第三行的首元素在 col / 8 处.....;第一行只有一个结点,第二行有两个结点,结点之间的间隔为 (2^(height - 1)-1),第三行之间的间隔为(2^(height - 2)-1),第四行之间的间隔为(2^(height - 3)-1)......,最后一行的间隔为1。这样我们就解决了满二叉树打印的问题了;那么对于不是满二叉树的一般二叉树来说怎么办呢? 好办!假设它是满二叉树就可以了,对于null结点,我们假设它有两个null的左右孩子,这样它不就成了一棵满二叉树了吗。

有了上述思路,我们就可以开始写代码了:

class Solution {
    public List<List<String>> printTree(TreeNode root) {
        int height = height(root);  // 求树的高低,也是最终结果的行数
        int col = (1 << height) - 1; // 每一行的列数
        List<List<String>> res = new ArrayList<List<String>>(height);
        Deque<TreeNode> queue = new LinkedList<>();  // 按层序遍历时需要使用到队列
        queue.offer(root);
        int h = 0;
        int space = 1 << height; // 每一行两个元素之间的间隔
        int cur = col / 2; // 每一行元素的起始位置
        while(height-- != 0) {  // 迭代的总次数就是树的高度
            int bak = cur;
            ArrayList<String> strs = new ArrayList<>(Collections.nCopies(col, "")); // 初始化为""
            int n = queue.size();
            for(int i = 0; i < n; ++i) {
                TreeNode node = queue.poll();
                if(node != null) {
                    queue.offer(node.left);
                    queue.offer(node.right);
                    strs.set(cur, String.valueOf(node.val));
                } else {  // 如果结点为null,假象它有两个为null的孩子结点
                    queue.offer(null);
                    queue.offer(null);
                }
                cur += space;
            }
            space >>= 1;  // 更新下一层的间隔
            cur = bak >> 1;  // 更新下一层的起始位置
            res.add(strs);
        }
        return res;
    }
    private int height(TreeNode root) {
        if(root == null) return 0;
        return Math.max(height(root.left), height(root.right)) + 1;
    }
}
7、二叉树的最大路径和(LeetCode124)

在二叉树中找一条路径和最大的路径,放回这个最大值。路径定义为为二叉树中任意两结点之间唯一的的路径(树中任意两个结点只有唯一的一条路径相互连接)不一定要经过根节点:

显然每条路径都存在一个最高点,最大路径和的路径也一定是从该路径的最高点,向左右孩子一直向下延伸,那么可以定义一个函数pathSumDown表示从该节点往下最大的路径值,那么最后包含该最高节点的的最大路径值就是:node.val + pathSumDown(node.left) + pathSumDown(node.right)了。

public class Solution {
    private int maxValue = Integer.MIN_VALUE;
    
    public int maxPathSum(TreeNode root) {
        if(root == null) return 0;
        maxPathDown(root);
        return maxValue;
    }
    
    private int maxPathDown(TreeNode node) {
        if (node == null) return 0;
        int left = Math.max(0, maxPathDown(node.left));
        int right = Math.max(0, maxPathDown(node.right));
        maxValue = Math.max(maxValue, left + right + node.val);
        return Math.max(left, right) + node.val;
    }
}
如果题目在扩展一下:比如必须包括根节点的最大路径和。普遍的我们都求了,这种特殊情况不是so easy吗。直接利用上面的pathSumDown就ok了:

public class Solution {
    public int maxPathSum(TreeNode root) {
        if(root == null) return 0;
        return root.val + Math.max(0, maxPathDown(root.left)) + Math.max(0, maxPathDown(root.right));
    }
    
    private int maxPathDown(TreeNode node) {
        if (node == null) return 0;
        int left = Math.max(0, maxPathDown(node.left));
        int right = Math.max(0, maxPathDown(node.right));
        return Math.max(left, right) + node.val;
    }
}
8、求根节点到所有叶子结点的路径和:

    public List<Integer> getAllRootToLeafSum(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        help(root, list, 0);
        return list;
    }
    private  void help(TreeNode root, ArrayList<Integer> list, int num) {
        if(root == null) return ;
        num += root.val;
        if(root.left == null && root.right == null) list.add(num);
        help(root.left, list, num);
        help(root.right, list, num);
        num -= root.val;
    }
9、求根节点到所有叶子结点的路径积:

    public List<Integer> getAllRootToLeafSum(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        help(root, list, 1);
        return list;
    }
    private  void help(TreeNode root, ArrayList<Integer> list, int num) {
        if(root == null) return ;
        num *= root.val;
        if(root.left == null && root.right == null) list.add(num);
        help(root.left, list, num);
        help(root.right, list, num);
        num /= root.val;
    }
10、在一棵BST中,找到比给定key值大的最小entry,(JDK7,TreeMap中的源码) 

    /**
     * Gets the entry corresponding to the specified key; if no such entry
     * exists, returns the entry for the least key greater than the specified
     * key; if no such entry exists (i.e., the greatest key in the Tree is less
     * than the specified key), returns {@code null}.
     */
    final Entry<K,V> getCeilingEntry(K key) {
        Entry<K,V> p = root;
        while (p != null) {
            int cmp = compare(key, p.key);
            if (cmp < 0) {
                if (p.left != null)
                    p = p.left;
                else
                    return p;
            } else if (cmp > 0) {
                if (p.right != null) {
                    p = p.right;
                } else {
                    Entry<K,V> parent = p.parent;
                    Entry<K,V> ch = p;
                    while (parent != null && ch == parent.right) {
                        ch = parent;
                        parent = parent.parent;
                    }
                    return parent;
                }
            } else
                return p;
        }
        return null;
    }
11、在一棵BST中,找到比给定key值小的最大entry,(JDK7,TreeMap中的源码) 

    /**
     * Gets the entry corresponding to the specified key; if no such entry
     * exists, returns the entry for the greatest key less than the specified
     * key; if no such entry exists, returns {@code null}.
     */
    final Entry<K,V> getFloorEntry(K key) {
        Entry<K,V> p = root;
        while (p != null) {
            int cmp = compare(key, p.key);
            if (cmp > 0) {
                if (p.right != null)
                    p = p.right;
                else
                    return p;
            } else if (cmp < 0) {
                if (p.left != null) {
                    p = p.left;
                } else {
                    Entry<K,V> parent = p.parent;
                    Entry<K,V> ch = p;
                    while (parent != null && ch == parent.left) {
                        ch = parent;
                        parent = parent.parent;
                    }
                    return parent;
                }
            } else
                return p;
 
        }
        return null;

————————————————
版权声明:本文为CSDN博主「CLthinking」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_22158743/article/details/88749788

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值