LeetCode_Lowest Common Ancestor

题目:给出一颗二叉树中的节点n1和n2,要求找出两个节点的深度最低的祖先节点。节点的定义方式如下:

struct TreeNode{
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int val):val(val),left(NULL),right(NULL){};
}; 
这道题虽然不是OJ中的,但是觉得是一道很好的题目,同时与OJ中的二叉树相关题目相比还算挺有难度的。

何海涛在他的博客中给出了一个自顶之下构造两个单链表来求解这个问题的解法,建议大家先看一下。

LeetCode上有人给出了Lowest Common Ancestor of a Binary Tree Part I一个自底向上的解法。

英文描述如下:

A Bottom-up Approach (Worst case O(n) ):
Using a bottom-up approach, we can improve over the top-down approach by avoiding traversing the same nodes over and over again.

We traverse from the bottom, and once we reach a node which matches one of the two nodes, we pass it up to its parent. The parent would then test its left and right subtree if each contain one of the two nodes. If yes, then the parent must be the LCA and we pass its parent up to the root. If not, we pass the lower node which contains either one of the two nodes (if the left or right subtree contains either p or q), or NULL (if both the left and right subtree does not contain either p or q) up.

个人翻译一下:自底向上的遍历二叉树(就是深度优先遍历),一旦找到了两个节点其中的一个,就将这个几点返回给上一层,上一层节点通过判断其左右子树中是否恰好包含n1和n2两个节点,如果是这样的话,对应的上一层节点肯定是所求的LCA;若果不是的话,将包括两个节点中任意一个的较低的节点返回给上一层,否则返回NULL。

其代码如下:

class Solution{
public:
    TreeNode *LowestCommonAncestor(TreeNode *root,TreeNode *n1,TreeNode *n2){
        //如果当前根结点为空返回NULL
        if (root==NULL){
            return NULL;
        }
        //若当前找到n1或者n2其中一个,则直接返回
        if(root==n1||root==n2){
            return root;
        }
        //分别从左右子树中查找n1和n2
        TreeNode *left=LowestCommonAncestor(root->left,n1,n2);
        TreeNode *right=LowestCommonAncestor(root->right,n1,n2);
        //若正好在分别在左右子树中找到n1和n2则说明当前节点就是要找的解
        if (left&&right){
            return root;
        }
        //存在三种情况是的函数在此处返回
        //1.第一次在左或右子树中找到n1、n2中的某个节点
        //2.从在当前节点的左右子树中已经查找到要找的解
        //3.以当前节点为根的子树根本就不存在n1和n2
        return left?left:right;
    }
};

注释部分是我自己加的。只看代码有点难想明白,画个图自己走一下就明白了,这里自己写了一个测试用例程序中的根据字符串生成二叉树的代码:

void preOrderPrintTree(TreeNode * root){
    if (root==NULL)  {
        cout<<"#"<<" ";
        return;
    }
    cout<<root->val<<" ";
    preOrderPrintTree(root->left);
    preOrderPrintTree(root->right);
}

void FindNode(TreeNode *root ,int val ,TreeNode *&pf){
    if (root==NULL){
        return;
    }
    if (root->val==val){
        pf=root;
        return;
    }
    FindNode(root->left,val,pf);
    FindNode(root->right,val,pf);
}
int _tmain(int argc, _TCHAR* argv[])
{
    string str="{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}";
    TreeNode *root=StringToBinaryTree(str);
    cout<<"PreOrder Travellsor:"<<endl;
    preOrderPrintTree(root);
    cout<<endl;
    TreeNode *n1=NULL;
    FindNode(root,16,n1);
    TreeNode *n2=NULL;
    FindNode(root,8,n2);
    Solution ss;
    TreeNode *CommonAncester = ss.LowestCommonAncestor(root,n1,n2);
    cout<<"8 and 11 Lowest common Ancestor:"<<CommonAncester->val<<endl;
	return 0;
}

程序运行结果如下


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给定一个字符串,请将字符串里的字符按照出现的频率降序排列。 示例 1: 输入: "tree" 输出: "eert" 解释: 'e'出现两次,'r'和't'都只出现一次。因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。 示例 2: 输入: "cccaaa" 输出: "cccaaa" 解释: 'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。注意"cacaca"是不正确的,因为相同的字母必须放在一起。 示例 3: 输入: "Aabb" 输出: "bbAa" 解释: 此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。注意'A'和'a'被认为是两种不同的字符。 Java代码如下: ``` import java.util.*; public class Solution { public String frequencySort(String s) { if (s == null || s.length() == 0) { return ""; } Map<Character, Integer> map = new HashMap<>(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); map.put(c, map.getOrDefault(c, 0) + 1); } List<Map.Entry<Character, Integer>> list = new ArrayList<>(map.entrySet()); Collections.sort(list, (o1, o2) -> o2.getValue() - o1.getValue()); StringBuilder sb = new StringBuilder(); for (Map.Entry<Character, Integer> entry : list) { char c = entry.getKey(); int count = entry.getValue(); for (int i = 0; i < count; i++) { sb.append(c); } } return sb.toString(); } } ``` 解题思路: 首先遍历字符串,使用HashMap记录每个字符出现的次数。然后将HashMap转换为List,并按照出现次数从大到小进行排序。最后遍历排序后的List,将每个字符按照出现次数依次添加到StringBuilder中,并返回StringBuilder的字符串形式。 时间复杂度:O(nlogn),其中n为字符串s的长度。遍历字符串的时间复杂度为O(n),HashMap和List的操作时间复杂度均为O(n),排序时间复杂度为O(nlogn),StringBuilder操作时间复杂度为O(n)。因此总时间复杂度为O(nlogn)。 空间复杂度:O(n),其中n为字符串s的长度。HashMap和List的空间复杂度均为O(n),StringBuilder的空间复杂度也为O(n)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值