快速幂+公共父节点

``# 快速幂

求:23的10000次幂,那么就是求23的5000次幂,因为2350*2350=23^100;所以可以遍历log(n)次

cpp 复制代码
int res=1;
int tmp=23;
for(int i=1;i<=logn;++i)
{
    tmp*=tmp;
}

显然,我们无法通过logn计算次数;

比如是非偶数的怎么计算呢?

我们可以考虑一下:100二进制位=(1100100)=22+25+2^6=4+32+64=100

所以我们可以得到

cpp 复制代码
int res=1;
int tmp=23;
int n=100
while(n>0)
{
    while(n&1)res*=tmp;
    tmp*=tmp;
    n>>1;
}

应用:找好数字

题目描述

一个数字字符串是好数字 当它满足(下标从 0 开始偶数下标处的数字为偶数且奇数下标处的数字为 质数 (2,3,5 或 7)。

比方说,"2582" 是好数字,因为偶数 下标处的数字(2 和 8)是偶数且奇数 下标处的数字(5 和 2)为质数 。但 "3245" 不是 好数字,因为3在偶数下标处但不是偶数。

给你一个整数n,请你返回长度为n且为好数字的数字字符串总数。由于答案可能会很大,请你将它对1e9+7 取余后返回

一个 数字字符串 是每一位都由 0 到 9 组成的字符串,且可能包含前导 0 。

shell 复制代码
示例 1:
输入:n = 1
输出:5
解释:长度为 1 的好数字包括 "0","2","4","6","8" 。
示例 2:
输入:n = 4
输出:400

理论分析

代码

cpp 复制代码
long long fastPow(long long a,long long b,long long mod)
    {
        int res=1;
        a=a%mod;
        while(b>0)
        {
            if(b&1)res=(res*a)%mod;
            a=(a*a)%mod;
            b>>=1;
        }
        return res;
    }
    int countGoodNumbers(long long n) {
        const long long mod=1e9+7;
        long long evePosition=(n+1)/2;
        long long oddPosition=n/2;
        long long res=(fastPow(5,evePosition,mod)*fastPow(4,oddPosition,mod))%mod;
        return (int)res;
    }

二叉树的父节点

"对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。"

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)

分析

思路1

找父节点,那就是找前序遍历的节点

比如:找5的父节点:3;找4的父节点:3,5,2三个父节点

找公共父节点:就是二者前序遍历中的5,我们可以通过map<TreeNode,int>mp

然后逆序遍历map就可以得到最近的父节点

  1. 缺点:需要mp来维护,但是这个不是二叉搜索树,如果要查找6,一个查找8,我们需要记录全部的过程,并且是没有规律的,遍历的后面处理的很复杂,尤其是树的深度很深的时候

思路2

上面算是迭代的过程,递归+迭代,递归遍历,然后迭代找父节点

我们如何用迭代来结算呢?
转换思考方向:我们递归遍历,查看该节点是不是父节点就好了

  1. 必须输入的是root,节点p,节点q
  2. 是否是父节点:
    1. 情况1:root节点p或者root节点q,root为最近父节点
    2. 情况2:root->left==节点p,root->right=q,root为最近父节点
    3. 情况3:root的左右子树中存在p,q;这个就是递归开始的条件了
      1. 情况3.1:p,q全在左子树
      2. 情况3.2:p,q全在右子树
      3. 情况3.3:p,q分别在左右子树中
代码1
cpp 复制代码
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
    if(root==nullptr)return nullptr; //递归结束的标志,找到叶子节点了
    if(root==p||root==q)return root; //p,q就是跟节点了,所以可以直接找到
    TreeNode* left=tranverse(root->left, p, q);
    TreeNode* right=tranverse(root->right, p, q);
    //情况2+情况3.3,从递归角度来看,p,q是分别在左右子树还是分别为左右子节点是没有区别的
    if(left!=nullptr&&right!=nullptr)
    {
        return root;
    }
    return left==nullptr?left:right;
}
优化代码1

我们可以看到,如果在左子树找到了p,q我们还是需要遍历右子树的?有没有什么方法是找到了就不需要遍历的,我们可以通过一个状态码state来记录

cpp 复制代码
 bool state=false;
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(state){
            return nullptr;
        }
        if (root == nullptr) {
            return nullptr;
        }
        // 前序位置
        if (root== p || root == q) {
            // 如果遇到目标值,直接返回
            return root;
        }
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        // 后序位置,已经知道左右子树是否存在目标值
        if (left != nullptr && right != nullptr) {
            // 当前节点是 LCA 节点
            state=true;
            return root;
        }
        
        return left != nullptr ? left : right;
    }
优化2

上面state虽然减少了进入递归,即通过提前返回nullptr来的方式减少进入左右子树,

我们可以通过遍历左子树,如果左子树返回不为空,直接不进入右子树即可

cpp 复制代码
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == nullptr || root == p || root == q) {
            return root;
        }
        
        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        if (left && left != p && left != q) {
            return left; // 如果 left 已经是 LCA,直接返回,不再递归右子树
        }
        
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        
        if (left && right) {
            return root;
        }
        
        return left ? left : right;
}

stor(root->right, p, q);

复制代码
    if (left && right) {
        return root;
    }
    
    return left ? left : right;

}

复制代码
第一种优化的缺点
相关推荐
小媛早点睡28 分钟前
贪心算法day10(无重叠区间)
算法·贪心算法
DataFunTalk1 小时前
乐信集团副总经理周道钰亲述 :乐信“黎曼”异动归因系统的演进之路
前端·后端·算法
行走的bug...1 小时前
sklearn估计器和变换器共有的一些方法 待更新
人工智能·算法·sklearn
DataFunTalk1 小时前
开源一个MCP+数据库新玩法,网友直呼Text 2 SQL“有救了!”
前端·后端·算法
编程侦探1 小时前
【设计模式】适配器模式:让不兼容的接口和谐共处
开发语言·c++·设计模式·适配器模式
张立龙6662 小时前
有序二叉树各种操作实现(数据结构C语言多文件编写)
c语言·开发语言·数据结构
2401_845417452 小时前
C++ string类
java·开发语言·c++
Y.O.U..2 小时前
力扣HOT100——560.和为k的子数组
数据结构·c++·算法·leetcode
wuqingshun3141592 小时前
经典算法 判断一个图中是否有环
java·开发语言·数据结构·c++·算法·蓝桥杯·深度优先
柃歌2 小时前
【LeetCode Solutions】LeetCode 160 ~ 165 题解
数据结构·算法·leetcode