【leetcode】剑指offer1

news2024/11/15 18:03:08

🌈1.Pow(x,n)

 

  • -100.0 < x < 100.0
  • -2^31 <= n <= 2^31-1
  • n 是一个整数
  • -10^4 <= x^n <= 10^4

思路分析:

暴力求解直接一个for循环n个x相乘解决,但是你拿那代码怎么好意思拿高薪?

所以而且那个的时间复杂度是O(n),效率并不是很高,我们再学习新的。

  • 1.分治实现(快速幂+递归)

x→x^2→x^4→x^8→x^16→x^32→x^64

从 x 开始,每次直接把上一次的结果进行平方,计算 6次就可以得到 x^64 的值,而不需要对 x乘 63次 x。

这个幂是偶数,如果幂是奇数咋办?

例如:x^77:

x→x^2→x^4→x^9→x^19→x^38→x^77

x→x^2,x^2→x^4,x^19→x^38这几步是直接平方处理,其他步是平方处理后再乘x处理。

如果说拆分从左往右不好看出来,那就从右往左倒着看。

这里还要注意一个细节,就是n<0,那样幂是负数,所以把求得的数倒过来变成分之一就行了。

  • 首先是实现一个快速幂函数两个形参(底数x,指数N)
  1.  如果指数是0,那直接返回0,任何数的0次方是1。
  2. 这里我们是根据N逆着推导,设置一个变量y,y就是一个过度,由于我们把总结果看作是一个数的平方,所以把y就看作这个数,如果N是偶数,就是y*y,如果是奇数就要再多乘一个x,y*y*x,在调用递归继续去分解这个y去找y=y1*y1。一直递归到x^0。
class Solution {
public:
    double QuickMul(double x, long long N)
    {
       //x是底数,N是指数
       if(N == 0)  //任何数的0次方为一,除去0
       {
           return 1.0;
       }
       double y = QuickMul(x, N/2);
       return N%2 == 0 ? y*y : y*y*x;
    }
    double myPow(double x, int n) {
        long long N = n;
        return N >= 0 ? QuickMul(x, N) : 1.0 / QuickMul(x, -N);
    }
};

复杂度分析

时间复杂度:O(log n),即为递归的层数。

空间复杂度:O(log n),即为递归的层数。这是由于递归的函数调用会使用栈空间。

  • 2.快速幂+迭代

我们还是以 x^{77} 作为例子:

x→x^2→x^4→+x^9+x^19→x^38→+x^77

这个+就是标记一下那一次需要多乘x,没啥特殊含义。

  • x^38→+x^77中额外贡献了一个x。
  • +x^9+x^19中额外贡献一个x,但是这个x被平方2次(19到38一次,38到77一次)=x^2^2=x^4
  • x^4→+x^9中额外贡献一个x,但是这x被平凡3次,x^2^3=x^8。
  • 最初的 xx 在之后被平方了 66 次,因此在 x^77 中贡献了 x^2^6= x^64。

我们把这些贡献相乘x*x^4*x^8*x^64=x^77,恰好等于 x^77 。

  • 而这些贡献的指数部分又是什么呢?它们都是 2 的幂次,这是因为每个额外乘的 x 在之后都会被平方若干次。而这些指数 1,4,8 和 64,恰好就对应了 77 的二进制表示 (1001101)_2
  • 中的每个 1!
  • 因此我们借助整数的二进制拆分,就可以得到迭代计算的方法,一般地,如果整数 nn 的二进制拆分为:n=2^i0​+2^i1​+⋯+2^ik​
  • 这样以来,我们从 x 开始不断地进行平方,得到x^2, x^4, x^8, x^16,⋯如果 n 的第 k 个(从右往左,从 0 开始计数,二进制都是从右往左)二进制位为 1,那么我们就将对应的贡献 x^2k。

所以设一个ans为贡献总和,如果N%2==1,那它一定贡献一个x,然后N/2,再判断是否贡献x,就这个一直循环,直到N=1,然后把所有的贡献x累乘即可。

class Solution {
public:
    double QuickMul(double x, long long N) {
        double ans = 1.0;
        // 贡献的初始值为 x
        double x_contribute = x;
        // 在对 N 进行二进制拆分的同时计算答案
        while (N > 0) {
            if (N % 2 == 1) {
                // 如果 N 二进制表示的最低位为 1,那么需要计入贡献
                ans *= x_contribute;
            }
            // 将贡献不断地平方
            x_contribute *= x_contribute;
            // 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可
            N /= 2;
        }
        return ans;
    }

    double myPow(double x, int n) {
        long long N = n;
        return N >= 0 ? QuickMul(x, N) : 1.0 / QuickMul(x, -N);
    }
};



 🌈2.整数除法 

   【简单】

提示:

  • -231 <= a, b <= 231 - 1
  • b != 0

这就是力扣的简单题型吗?这还指什么offer,直接进场了。

🐎思路分析:

xdm,这个题是考啥的?个人认为是不是对边界判定,溢出和二分查找有联系。

首先我们就要对容易发生溢出或者是易错边界讨论:

  • 当被除数a为 32 位有符号整数的最小值 -2^31 时:
  1. 如果除数为 1,那么我们可以直接返回答案 -2^31 ;
  2. 如果除数为 -1,那么答案为 2^31,产生了溢出。此时我们需要返回 2^31  −1。
  • 当除数b为 32位有符号整数的最小值 -2^31时:
  1. 如果被除数同样为 -2^31,那么我们可以直接返回答案 1;
  2. 对于其余的情况,我们返回答案 0。
  • 当被除数a为0时,我们可以直接返回答案0。

其次我们就要讨论这两个数相除的4中符号形式:(正正,正负,负正,负负)

  • 那肯定第一反应就是符号变成相同的,所以都变成正的,最后再变回来。但是如果说被除数是-2^31,那他的正数就是2^31,越界了(题上的注意)。
  • 所以把他俩都变成负数这样就不需要考虑越界和溢出问题了。
  • 最后返回的时候别忘了变回来。
class Solution {
public:
    int divide(int a, int b) {
        //如果被除数是32位最小值
        if(a==INT_MINT)
        {
            if(b==1)
              return INT_MINT;
            if(b==-1)
                return INT_MAX;
        }
        //如果除数是最小值
        if(b==INT_MINT)
        {
            if(a==INT_MINT)
                return 1;
            else
             return 0;
        }
        //被除数为0,(除数不能为0)
        if(a==0)
        {
            return 0;
        }
    }
};

准备工作已经做好,接下来进行二分查找。

老师说一定要做足前戏,不能直奔主题,现在前戏已经完成,接下来就该高潮部分了。

接下来我们设商是c,a/b=c所以c不是正数就是0,这是前提条件。

我们把a,b,c都看成是正数,a>=b*c(9/2=4,9>2*4),但是a,b都是负数,所以a<=b*c

  • 所以这个题我们利用二分查找找出最大的数c使得b*c>=a成立就行了。
  1. 不出意外的话就出现了意外,这个题不让使用‘*’
  2. 二分查找的下界为 1,上界为 2^31 - 1。唯一可能出现的答案为 2^31的情况已经被我们在前言部分进行了特殊处理,因此答案的最大值为 2^31 - 1。如果二分查找失败,那么答案一定为 0。

  3. 所以我们就会使用到快速乘,和上面的快速幂很相似,前者通过加法实现乘法,后者通过乘法实现幂运算。
  4. 在实现「快速乘」时,我们需要使用加法运算,然而较大的 c 也会导致加法运算溢出。例如我们要判断 a + b是否小于 c 时(其中 a, b, c均为负数),a+b可能会产生溢出,因此我们必须将判断改为 a<c-b是否成立。由于任意两个负数的差一定在 [-2^{31} + 1, 2 ^31-1] 范围内,这样就不会产生溢出。
  5. 我们需要使用「快速乘」算法得到Z×Y 的值。然后再去找最大的Z。
class Solution {
public:
    int divide(int a, int b) {
        // 考虑被除数为最小值的情况
        if (a == INT_MIN) {
            if (b == 1) {
                return INT_MIN;
            }
            if (b == -1) {
                return INT_MAX;
            }
        }
        // 考虑除数为最小值的情况
        if (b == INT_MIN) {
            return a == INT_MIN ? 1 : 0;
        }
        // 考虑被除数为 0 的情况
        if (a == 0) {
            return 0;
        }
        
        // 一般情况,使用二分查找
        // 将所有的正数取相反数,这样就只需要考虑一种情况
        bool rev = false;
        if (a > 0) {
            a = -a;
            rev = !rev;
        }
        if (b > 0) {
            b = -b;
            rev = !rev;
        }

        // 快速乘
        auto quickAdd = [](int y, int z, int x) {
            // x 和 y 是负数,z 是正数
            // 需要判断 z * y >= x 是否成立
            int result = 0, add = y;
            while (z) {
                if (z & 1) {
                    // 需要保证 result + add >= x
                    if (result < x - add) {
                        return false;
                    }
                    result += add;
                }
                if (z != 1) {
                    // 需要保证 add + add >= x
                    if (add < x - add) {
                        return false;
                    }
                    add += add;
                }
                // 不能使用除法
                z >>= 1;
            }
            return true;
        };
        
        int left = 1, right = INT_MAX, ans = 0;
        while (left <= right) {
            // 注意溢出,并且不能使用除法
            int mid = left + ((right - left) >> 1);
            bool check = quickAdd(b, mid, a);
            if (check) {
                ans = mid;
                // 注意溢出
                if (mid == INT_MAX) {
                    break;
                }
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }

        return rev ? -ans : ans;
    }
};

result + add >= x这个咋一看有点难理解,但是因为都是负数,所以反过来看就是如果当前值已经比x(实际值)大则退出了,即已经不满足z*y<x(正数情况)。

这要是简单题那不废了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/130852.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[阿里云] 10分钟带你玩转阿里云ECS和云盘 (大数据上云必备)

前言 由于准备做一些离线计算和实时计算的模拟, 发现某些教程内的阿里云还挺好用的, 在这里把相关的经验分享给大家. 简单的心路历程: 起先笔者搭建了一套本地集群. 但是后来发现, 因为没用网络IP的反穿, 本地的集群的网络访问非常不便. 其次, 集群的启停, 网络和磁盘管理都非…

缓存原理的学习

在如今这个微服务分布式的大环境下,集群分布式部署 几乎 已经是我们每个人都熟知的了。 缓存也一样&#xff0c;对我们来说 &#xff0c;如果只是一个单体应用 &#xff0c; 那只要 有本地缓存就足以了&#xff0c;但是倘若分布式部署了很多台机器上&#xff0c;那我们该如何缓…

软考-操作系统

【考点梳理】 【进程管理】 考点1、进程的状态&#xff08;★★&#xff09; 【考法分析】 本考点主要考查形式主要是根据图示判断相关状态位置或状态变迁条件。 【要点分析】 操作系统三态模型如下图所示&#xff1a;操作系统五态模型&#xff1a;【备考点拨】 掌握操作…

vue2.0和vue3.0创建项目

由于vue项目依赖于nodejs&#xff0c;所以需要先安装它。没有nodejs去官网下载。 npm install --global vue-cli 国内npm网站很慢&#xff0c;可以使用淘宝镜像 npm install --registryhttps://registry.npm.taobao.org vue2.0创建项目&#xff1a; 进入到自己需要创建项目…

SAP给微信推送消息

导语&#xff1a;最近领导下发指令&#xff0c;要求研究SAP与微信&#xff0c;企业微信&#xff0c;钉钉&#xff0c;邮件推送消息的平台&#xff0c;类似于采购订单审批之后&#xff0c;可以通过以上软件给用户发消息&#xff0c;我认领了微信的部分。 整个研究过程是很痛苦的…

华为静态NAT、动态NAT、PAT端口复用

一、网络环境及TOP 1.1 R1 相当于内网的一台PC&#xff0c; IP&#xff1a;192.168.1.10 网关为 192.168.1.254 [R1]ip route-static 0.0.0.0 0 192.168.1.254 # R1配置默认路由&#xff08;网关&#xff09; 1.2 R2为出口路由器&#xff0c;分别连接内网R1及外网R3 1&…

7. 整数反转

题目链接&#xff1a;力扣 解题思路&#xff1a; 题目要求中有一句话&#xff1a;假设环境不允许存储 64 位整数&#xff08;有符号或无符号&#xff09;。 也就是说不能使用long类型来判断是否int溢出&#xff0c;只能使用int类型进行运算 首先对于一个整数的翻转比较简单…

学openCV,不会数字图像系统可不行

来源&#xff1a;投稿 作者&#xff1a;小灰灰 编辑&#xff1a;学姐 前言 在光照的情况下&#xff0c;通过成像系统将大自然中的物体拍摄出来&#xff0c;成像系统手机自带有&#xff0c;这里面我们关心的是分辨率&#xff0c;成像系统显示的点数越多&#xff0c;阵列越大&am…

HCIA静态试验(12.30-31复习)

目标实现&#xff1a; 2、首先进行子网划分 基于192.168.1.0 24划分 ‘一共7个路由器需要7个网段还有7个主干网 192.168.1.0/24 ----用于骨干 192.168.1.32/27 ----R1环回 192.168.1.32/28 192.168.1.48/28 192.168.1.64/27 --- R2环回 192.168.1.64/28 192.168.1.80/28 …

Java之网络相关概念

寒假又开始更新java了&#xff0c;之后更新的是b站教程韩顺平老师的课&#xff0c;编译器我从idea换成eclipse&#xff08;因为蓝桥杯只有eclipse&#xff0c;要先熟悉&#xff09; 1.网络相关概念 网络通信 网络 ip地址 1.简单来说ip地址是每一台主机的标识 类似于我们现…

Vue 疑难扎症(一)有时候取不到Vue对象中值怎么办?对象值发生改变了但是页面没有刷新怎么办?

目录 有时候取不到对象中值怎么办&#xff1f; 问题截图 问题代码 问题分析 情况1 情况2 情况3 问题解决 对象值发生改变了但是页面没有刷新怎么办&#xff1f; 为什么&#xff1f; 常见错误写法&#xff1a; 怎么办&#xff1f; 有时候取不到对象中值怎么办&…

Node.js--》如何在Node.js中使用中间件,看这一篇就足够了

目录 中间件 中间件函数使用 中间件的作用 中间件分类 使用中间件的注意事项 编写接口 跨域问题及其解决方案 中间件 中间件特指业务流程的中间处理环节。当一个请求到达 Express 的服务器之后&#xff0c;可以连续调用多个中间件&#xff0c;从而对这次请求进行预处理…

【网络排查】用于接口不通,mysql,kafka等数据库介质连不上的排查

这篇文章记录生产实践中遇到的网络不通的例子 文章目录前言1. 网络协议1.1 应用层找到有问题的服务端 IP总结前言 接口调用不同了了怎么办&#xff1f; 就找接口服务提供方&#xff0c;肯定是提供方的问题的&#xff0c;跟调用方有啥关系~ kafka&#xff0c;mysql等数据库介质…

JAVAGUI编程初识之Swing

文章目录一 常用窗口1.1 JFrame框架窗口1.2 演示-JFRame,JLable的使用1.3 JDialog标签1.3.1 演示-JDialog标签二 标签组件2.1 标签2.2 图标2.2.1 ICon接口简介2.2.2 演示-用Icon接口创建图标2.3 图片图标2.3.1 演示-图片图标三 布局管理器3.1 绝对布局3.1.1 绝对布局简介3.1.2 …

年末再看指针。看来搞C/C++,如影随形的指针就得门清~~~

继上篇博文因内核页表引出的指针问题&#xff0c;后来又研究了一番&#xff0c;这次应该比较清楚了&#xff0c;这里再总结一下。 目录 0 前言 1 普通指针&#xff1a; 2 指针的指针&#xff1a; 3 普通指针参数&#xff1a; 4 指针的指针参数&#xff1a; 5 函数指针&a…

[Kettle] 认识Kettle

1.初识Kettle Kettle是ETL数据整合与处理工具&#xff0c;翻译成中文是"水壶"的意思&#xff0c;可理解为希望把各种数据放到一个壶里&#xff0c;像水一样以一种指定的格式流出&#xff0c;表达数据流的含义 ETL(Extract - Transform - Load)是将数据从数据来源端…

centos7部署rancher2.5

一、 什么是 Rancher Rancher 是为使用容器的公司打造的容器管理平台。Rancher 简化了使用 Kubernetes 的流程&#xff0c;开发者可以随处运行 Kubernetes&#xff08;Run Kubernetes Everywhere&#xff09;&#xff0c;满足 IT 需求规范&#xff0c;赋能 DevOps 团队。 Ran…

单纯形法与对偶单纯形法的通俗理解

cigma<0,a>0 min cigma/(a) 决定出基变量 1对偶单纯形法 意思是看c就是所有货物的价值&#xff0c;去看一眼这些货物单价组合售卖的价值&#xff0c;这些价值肯定要都大于0&#xff0c;而且&#xff0c;组成这个c的系数也应该是都是正的&#xff0c; c最小证明对min&a…

港科夜闻|香港科大-越秀集团百万奖金国际创业大赛2022年度前8强20强项目评审结果公布...

关注并星标每周阅读港科夜闻建立新视野 开启新思维1、“香港科大-越秀集团”百万奖金国际创业大赛2022年度前8强&20强项目评审结果公布。2022年赛事中的各赛区前三名项目&#xff0c;共计23个项目自动入围年度总决赛&#xff0c;本轮评审在这23个项目中&#xff0c;评选出了…

Hudi学习02 -- Hudi核心概念

文章目录基本概念时间轴&#xff08;Timeline&#xff09;文件布局&#xff08;File Layout&#xff09;索引&#xff08;Index&#xff09;索引原理索引类型索引的选择策略表类型&#xff08;Table Types&#xff09;查询类型&#xff08;Query Types&#xff09;写操作&#…