算法通关村第八关——树经典算法青铜挑战笔记(双指针、路径、反转)

本篇内容主要从递归角度出发,解决树经典算法问题,这些经典问题分为3类,分别是双指针问题:判断树是否相同、是否对称以及树的合并,路径问题:查找二叉树的全部路径、查找是否存在路径和为指定值的路径,反转问题:反转二叉树。这些问题深度巩固树的递归算法,易形成解决该类问题思路,且该类问题的解决思路有共通之处,值得认真复盘总结!

1.二叉树双指针

实际上就是定义两个变量来解决树种问题。

1.1判断两棵树是否相同

两棵树如何判断是否相同?很显然,当p,q两个树都是空,则他们相同;当p,q其中一个为空,另一个非空,则它们不相同;当p,q两个树都不空时,pq左右子树相同且pq值相同,则pq两棵树相同。

厘清这个逻辑之后,我们发现这就是一个简单的递归问题,递归出口已经明细:当p,q两个树都是空,则他们相同;当p,q其中一个为空,另一个非空,则它们不相同;递归逻辑:当p,q两个树都不空时,pq左右子树相同且pq值相同,则pq两棵树相同(后序遍历);当p,q两个树都不空时,pq左子树相同,pq值相同,pq右子树相同,则pq两棵树相同(中序遍历);当p,q两个树都不空时,pq值相同且pq左右子树相同,则pq两棵树相同(前序遍历)

厘清思路,直接上代码!

    public static boolean myIsSameTree(TreeNode p, TreeNode q) {
        if(p == null && q == null) return true;
        if(p == null || q == null) return false;
        if(p.val != q.val) return false;
        return myIsSameTree(p.left, q.left) && myIsSameTree(p.right, q.right);
    }

1.2判断对称二叉树

对称二叉树,就是左右子树镜像相等,是不是这个问题又转变成了1.1判断两个树是否相同了,不过变化点在于,这里镜像相等需要的是p树的左孩子和q树的右孩子相等。

那么递归出口和递归逻辑依旧如1.1中所描述的那样,直接上代码!

    public static boolean isSymmetric(TreeNode root) {
        if(root == null) return true;
        return check(root.left, root.right);
    }
    
    public static boolean check(TreeNode p, TreeNode q) {
        if(p == null && q == null) return true;
        if(p == null || q == null) return false;
        if(p.val != q.val) return false;
        return check(p.left, q.right) && check(p.right, q.left);
    }

1.3合并二叉树

二叉树的合并问题,需要理解清楚以下几个情况:

1.树节点合并树节点:节点值相加

2.树节点合并空:新建一个树节点

3.空合并空:仍为空

很明显,递归出口已经找到:树节点合并空:新建一个树节点;空合并空:仍为空。递归逻辑:先合并根节点,再合并左右子树(前序遍历)!

厘清思路,直接上代码!

    public static TreeNode myMergeTrees(TreeNode t1, TreeNode t2) {
        if(t1 == null && t2 == null) return null;
        if(t1 == null || t2 == null) return t1 == null ? t2 : t1;
        TreeNode newNode = new TreeNode(t1.val + t2.val);
        newNode.left = myMergeTrees(t1.left, t2.left);
        newNode.right = myMergeTrees(t1.right, t2.right);
        return newNode;
    }

2.路径专题

2.1二叉树的所有路径

如何求二叉树的一条路径呢?当我们遍历到叶子节点的时候,记录遍历顺序,就可以得到一条树的路径。

那么,如何求二叉树的所有路径呢?我们需要记录当前访问的路径信息,并且能够恢复回到上一个树节点的路径信息。如何解决?答:这涉及了信息恢复,当然使用递归压栈的时候保存当前环境信息和局部变量,出栈的时候恢复当前环境信息和局部变量,并且根据这个逻辑,路径信息应该保存在局部变量中,且该局部变量应该能随递归调用发生变化,显然应该使用形参保存路径信息!

递归出口:树为空,则直接返回;树为叶子节点,使用”全局变量“保存路径信息。

递归逻辑:记录当前节点的路径信息,接着找左子树的路径,接着找右子树的路径(前/后序遍历)

厘清逻辑,直接上代码!

    public static List<String> binaryTreePaths(TreeNode root) {
        ArrayList<String> res = new ArrayList<>();
        dfs(root, "", res);
        return res;
    }    

    private static void dfs(TreeNode root, String path, List<String> res){
        if(root == null) return;
        if(root.left == null && root.right == null){
            path += root.val;
            res.add(path);
            return;
        }
        dfs(root.left, path + root.val + "->", res);
        dfs(root.right, path + root.val + "->", res);
    }

2.2路径总和

对于该问题而言,如果树为空,则不存在这样的路径;如果树仅有一个根节点,判断根节点的值是否等于target,不等于则不存在这样的路径;如果是非空树,则寻找左右子树中路径和为targe-val的路径。

那么,该递归问题的递归出口已经清晰:如果树为空,则不存在这样的路径;如果树仅有一个根节点,判断根节点的值是否等于target,不等于则不存在这样的路径。递归逻辑:如果是非空树,则寻找左右子树中,是否存在路径和为targe-val的路径(前/后序遍历)。

厘清思路,直接上代码!

    public static boolean myHasPathSum(TreeNode root, int sum) {
        //空树不满足路径和
        if(root == null) return false;
        //叶子节点
        if(root.left == null && root.right == null){
            return sum == root.val;
        }
        //左子树路径和为sum-val右子树路径和为sum-val
        return myHasPathSum(root.left, sum - root.val)
                || myHasPathSum(root.right, sum- root.val);
    }

3.翻转问题

对于树的翻转,需要将左右子树进行翻转。

如果从下至上看,下层子树翻转后,只需要将上层父节点翻转即可(后序遍历)!厘清思路,直接上代码!

    public static TreeNode invertTree_2(TreeNode root) {
        if (root == null) {
            return null;
        }
        TreeNode left = invertTree_2(root.left);
        TreeNode right = invertTree_2(root.right);
        root.left = right;
        root.right = left;
        return root;
    }

如果从上至下看,顶层树节点进行翻转,然后子树在进行翻转(前序遍历)!厘清思路,直接上代码!

    public static TreeNode myInvertTree_1(TreeNode root) {
        if(root == null) return null;
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        myInvertTree_1(root.left);
        myInvertTree_1(root.right);
        return root;
    }

OK,《算法通关村第八关——树经典算法青铜挑战笔记》结束,喜欢的朋友三联加关注!关注鱼市带给你不一样的算法小感悟!(幻听)

再次,感谢鱼骨头教官的学习路线!鱼皮的宣传!小y的陪伴!ok,拜拜,第八关第二幕见!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,让我来回答你于聚类算法的问题。 聚类算法是一种无监督学习方法,它将数据集中的对象分成若干组或者类,使得同一组内的对象之间相似度较高,而不同组之间的相似度较低。聚类算法广泛应用于数据挖掘、模式识别、图像处理、自然语言处理等领域中。 常见的聚类算法有K-Means、层次聚类、DBSCAN等。其中,K-Means算法是最常用的聚类算法之一。 K-Means算法的基本思想是:首先随机选取K个中心点(质心),然后将每个样本点归属于距离其最近的中心点所在的簇,接着重新计算每个簇的中心点,重复上述过程直到簇不再发生变化或达到预设的迭代次数。 层次聚类算法是一种基于距离的聚类算法,它将样本点视为一棵,从下往上逐步合并相似的簇,最终形成一棵形结构。层次聚类算法可分为自底向上的凝聚聚类和自顶向下的分裂聚类两种。自底向上的凝聚聚类从每个样本点开始,逐步合并相似的簇,最终形成一棵形结构;自顶向下的分裂聚类从所有样本点开始,逐步将簇分裂成较小的簇,最终形成一棵形结构。 DBSCAN算法是一种基于密度的聚类算法,它将样本点分为核心点、边界点和噪音点三类,核心点周围的样本点被划分为同一个簇,边界点则被划分到离其最近的核心点所在的簇中,而噪音点则被剔除。DBSCAN算法具有不需要预先指定聚类数、对噪音点不敏感等优点,但对于不同密度的簇,其聚类效果可能不尽如人意。 以上是聚类算法的基础知识,希望能够对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值