剑指 offer (专项突击版)

2023-11-03

剑指 Offer II 001. 整数除法

342342343

//方法一:
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;
        }

        // 快速乘,内联函数
        //注意:此时的x,y都为负数!!!!
        auto quickAdd = [](int x, int y, int z) {
            //这里的z就表示y自身相加多少次
            // x 和 y 是负数,z 是正数
            // 需要判断 z * y >= x 是否成立
            int result = 0, add = y;
            //计算乘法,每次都对半处理
            while (z) {
                //z为奇数
                if (z & 1) {
                    // 需要保证 result + add >= x
                    if (result < x - add) {
                        return false;
                    }
                    result += add;
                }
                //z不等于1
                if (z != 1) {
                    // 需要保证 add + add >= x
                    if (add < x - add) {
                        return false;
                    }
                    add += add;
                }
                // 不能使用除法,z / 2
                z >>= 1;
            }
            return true;
        };
        
        int left = 1, right = INT_MAX, ans = 0;
        while (left <= right) {
            // 注意溢出,并且不能使用除法
            //获取两个数中间的数
            int mid = left + ((right - left) >> 1);
            //check用于判断 b * mid 后是否小于 a,确认小于就使用ans存储该mid然后继续进行二分,不小于就缩短范围
            //注意:此时的a,b都为负数!!!!
            bool check = quickAdd(a, b, mid);
            if (check) {
                ans = mid;
                // 注意溢出
                if (mid == INT_MAX) {
                    break;
                }
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }

        return rev ? -ans : ans;
    }
};

//方法二:
class Solution {
public:
    int divide(int a, int b) {
        if (a == INT_MIN && b == -1) return INT_MAX;

        int sign = (a > 0) ^ (b > 0) ? -1 : 1;

        if (a > 0) a = -a;
        if (b > 0) b = -b;
        
        int res = 0;
        while (a <= b) {
            int value = b;
            int k = 1;
            // 0xc0000000 是十进制 -2^30 的十六进制的表示
            // 判断 value >= 0xc0000000 的原因:保证 value + value 不会溢出
            // 可以这样判断的原因是:0xc0000000 是最小值 -2^31 的一半,
            // 而 a 的值不可能比 -2^31 还要小,所以 value 不可能比 0xc0000000 小
            while (value >= 0xc0000000 && a <= value + value) {
                value += value;
                // 代码优化:如果 k 已经大于最大值的一半的话,那么直接返回最小值
                // 因为这个时候 k += k 的话肯定会大于等于 2147483648 ,这个超过了题目给的范围
                if (k > INT_MAX / 2) return INT_MIN;
                k += k;
            }
            a -= value;
            res += k;
        }

        // bug 修复:因为不能使用乘号,所以将乘号换成三目运算符
        return sign == 1 ? res : -res;
    }
};

方法一:快速乘 + 二分查找。
方法二:成倍数相减。

剑指 Offer II 002. 二进制加法

4234234234

class Solution {
public:
    string addBinary(string a, string b) {
        string ans;

        reverse(a.begin(), a.end());
        reverse(b.begin(), b.end());

        int n = max(a.size(), b.size()), carry = 0;
        for (size_t i = 0; i < n; ++i) {
            carry += i < a.size() ? (a.at(i) == '1') : 0;
            carry += i < b.size() ? (b.at(i) == '1') : 0;
            ans.push_back((carry % 2) ? '1' : '0');
            carry /= 2;
        }
        if (carry) {
            ans.push_back('1');
        }
        
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

模拟法,没啥说的,自己搞一个二进制加法器。

剑指 Offer II 003. 前 n 个数字二进制中 1 的个数

4234234234

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> myVector(n + 1, 0);
        for (int i = 0; i <= n; i++) {
            myVector[i] = myVector[i >> 1] + (i & 1); //右移1位相当于i/2
        }
        return myVector;
    }
};

如果正整数 i 是一个偶数,那么 i 相当于将 i/2 左移一位的结果,因此偶数 i 和 i/2 的二进制形式 1 的个数是一样的,如果 i 是奇数,那么 i 相当于将 i/2 左移一位之后再将最右边的位设为 1 的结果,因此奇数 i 比 i/2 的二进制形式 1 的个数多 1 个 可以利用这个规律写出这个代码。

剑指 Offer II 004. 只出现一次的数字

4324234

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a = 0, b = 0;
        for (auto x : nums) {
            a = ~b & (a ^ x);
            b = ~a & (b ^ x);
        }
        return a;
    }
};

经典三进制器。
4324234
4234234
4234234234
4234234

剑指 Offer II 005. 单词长度的最大乘积

4324234

class Solution {
public:
    int maxProduct(vector<string>& words) {
        vector<int> myVector(words.size(), 0);
        for (int i = 0; i < words.size(); i++) {
            for (int j = 0; j < words[i].size(); j++) {
                myVector[i] |= 1 << (words[i][j] - 'a');
            }
        }
        int maxNum = 0;
        for (int i = 0; i < myVector.size() - 1; i++) {
            for (int j = i + 1; j < myVector.size(); j++) {
                if ((myVector[i] & myVector[j]) == 0) {
                    maxNum = max(maxNum, (int)(words[i].size() * words[j].size()));
                }
            }
        }
        return maxNum;
    }
};

5435345
还有其优化版,这里就不过多赘述了。
45345345

剑指 Offer II 006. 排序数组中两个数字之和

5345345

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int i = 0, j = numbers.size() - 1;
        while (i < j) {
            if (numbers[i] + numbers[j] == target) {
                return {i, j};
            } else if (numbers[i] + numbers[j] > target) {
                j--;
            } else {
                i++;
            }
        }
        return {0};
    }
};

双指针遍历,没啥意思。

剑指 Offer II 007. 数组中和为 0 的三个数

5345345

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> myVector;
        if (nums.size() < 3) {
            return myVector;
        }
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size() - 2; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1, right = nums.size() - 1;
            while (left < right) {
                if (nums[i] + nums[left] + nums[right] == 0) {
                    myVector.push_back({nums[i], nums[left], nums[right]});
                    //去重
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    left++;
                    right--;
                } else if (nums[i] + nums[left] + nums[right] > 0) {
                    right--;
                } else {
                    left++;
                }
            }
        }
        return myVector;
    }
};

排序 + 三指针,固定一个指针,另外两个指针一个指向固定指针的下一个元素位置,一个指向最后一个元素,再暴力求和求解。

剑指 Offer II 008. 和大于等于 target 的最短子数组

434234

class Solution {
public:
    //双指针法,类似于滑动窗口
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum = 0, minLength = INT_MAX;
        //定义两个变量i、j(类似于滑动窗口的感觉)
        for (int i = 0, j = 0; j < nums.size(); j++) {
            //扩大窗口
            sum += nums[j];
            while (i <= j && sum >= target) {
                //更新最小值
                minLength = min(minLength, j - i + 1);
                //缩小窗口
                sum -= nums[i++];
            }
        }
        //若所有数组和都小于target,则返回0,否则返回更新值
        return minLength == INT_MAX ? 0 : minLength;
    }
};

类似于滑动窗口,每次循环扩大窗口,当其中的值大于等于target时,缩小窗口,更新最小值,一值这样循环更新循环更新就行了。

剑指 Offer II 012. 左右两边子数组的和相等

5345345345

class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        int frontNum = 0;
        int endNum = 0;
        for (int i = 0; i < nums.size(); i++) {
            endNum += nums[i];
        }
        for (int i = 0; i < nums.size(); i++) {
            endNum -= nums[i];
            if (frontNum == endNum) {
                return i;
            }
            frontNum += nums[i];
        }
        return -1;
    }
};

先求后序总和endNum,然后指针往后走,每次都减掉指向的这个数,再frontNum加当前指向的数,再每次进行判断就行,如果循环完了还没有返回,那就说明没找到,返回-1。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

剑指 offer (专项突击版) 的相关文章

随机推荐

  • JavaWeb详解(第四篇)之JSP 简介

    JavaWeb详解 第四篇 之JSP 简介 1 JSP概述 1 1 什么是JSP JSP 全称是 Java Servlet Pages 它是和 servlet 技术一样 都是 SUN 公司定义的一种用于动态开发 web 资源的技术 JSP
  • conda 创建/删除/复制/重命名 深度学习环境

    1 创建 打开anaconda的prompt面板 先创建一个python3 9的环境 conda create n pytorch1 9 python 3 9 创建完之后可以激活环境 activate pytorch1 9 进一步可以安装t
  • CRM软件系统能否监控手机的使用

    CRM可以监控手机吗 答案是不可以 CRM是一款帮助企业优化业务流程 提高销售效率的工具 例如Zoho CRM 最多也就是听一下销售的通话录音 却不可以监控手机 毕竟CRM不是一款监控软件 CRM的主要作用有以下几点 1 管理客户数据 CR
  • 【数据结构】带头双向循环链表---C语言版(单链表我们分手吧,不要再找我玩了!!!)

    文章目录 一 前言 二 链表的分类 1 单向或者双向链表 2 带头或者不带头链表 3 循环或者非循环 4 最常用链表 三 带头双向循环链表详解 创建带头双向循环链表 接口1 定义结构体 LTNode 接口2 初始化 创建哨兵卫 LTInit
  • mmdet_config_builder_win

    在mmdet框架中使用config配置文件构建网络模型 from mmdet models builder import build detector from mmcv import Config import torch import
  • Android 内核调用充电状态和电池电量

    Android 内核调用充电状态和电池电量 前言 一 调用的文件 二 调用函数 1 引入使用 2 返回值说明 小结 前言 因为Android项目需求 不是什么时候都是用APP来实现功能 部分项目是要求需要驱动需要独立完成部分系统层面的功能
  • 在vue中引入echart的折线图时,echarts.graphic.LinearGradient,不能正常显示的解决方法。

    在vue中需要达到折线图 且有区域渐变色的效果 那么像下面那样子直接复制过来 在vue中不能渲染出来 需要将原来的 new echarts graphic LinearGradient 改成这样 new this echarts graph
  • Vue线上部署之cdn加速(终极加速)

    文章目录 1 概述 3 cdn gzip vs gzip 1 概述 之前做过服务器nginx开启gzip压缩 速度缩减了很多 加载时间在1秒多 会出现白屏 原因是好多依赖被打包到js中了 体积太大 加载很慢 今天加了下cdn 速度真正起飞
  • Tomcat 线程池

    目录 概述 tomcat线程池工作原理 关键源码 Connector 配置 Executor 线程配置 tomcat核心组件 题外 概述 Tomcat 是一个流行的 Java Web 服务器 它使用线程池来处理客户端请求 线程池是一组预先创
  • 矩阵的转置等于矩阵的逆

    http zhidao baidu com question 334500638 html 百度知道三个回答 矩阵A的转置矩阵A T等于A的逆矩阵A 1 那么AA T AA 1 E 设A 1 2 3 n T 其中 i为n维列向量 那么A T
  • 华为机试-在字符串中找出连续最长的数字串

    题目描述样例输出输出123058789 函数返回值9输出54761 函数返回值5接口说明函数原型 unsignedint Continumax char pOutputstr char intputstr 输入参数 char intputs
  • Android zygote进程启动过程

    zygote启动过程中涉及到以下模块 app process zygote USAP socket FileDescriptor FD AndroidRuntime AppRuntime 定义于app process模块 继承自Androi
  • STM32F103构建固件库模板(PS固件库文件树介绍)

    参考 STM32F103ZE新建固件库模板 作者 追兮兮 发布时间 2020 10 14 10 31 45 网址 https blog csdn net weixin 44234294 article details 109065495 参
  • 官网ISE14.7虚拟机版本在Win11的安装记录

    目录 第一步 下载ISE14 7虚拟机版本 第二步 下载IOracle VM VirtualBox虚拟机 第三步 安装虚拟机Oracle VM VirtualBox 第四步 安装ISE14 7 4 1 参考博客 4 2 格外注意 4 3 安
  • 软件测试之linux复习!

    1 介绍linux linux分为 内核版 发行版 常见的发行版 Ubuntu redhat fedora kaliLinux backtrack linux 2 命令 cd 跳转路径 相对路径 根据当前目录进行跳转时的方式 绝对路径 从
  • Redis(一)

    Redis和Memcache比较 Redis是单进程单线程模式 采用I O多路复用 Memcached 采用 多线程模型 采用非阻塞I O 1 Redis不仅仅支持简单的k v类型的数据 同时还提供list set zset hash等数据
  • 区块链基础知识(3)-区块链的存储(怎样记账)

    我们已经知道 比特币相当于是 全球账薄 那这份账单是如何存储的 也就是说把账记在哪里 区块链包含N个随时间排序的块 每个块都有一个指向前一区块的指针 所有块通过这个指针形成一个链 所以称为区块链 第一个块称为创世区块 如图 从上图可见 区块
  • 统计学基础面点

    文章目录 1 T检验 F检验 卡方检验 2 方差分析 3 多重共线性 4 参数估计 5 假设检验 6 大数定律和中心极限定理 总结一下统计学的基础概念和考点给即将秋招的统计学er以及baozi 1 T检验 基本概念 t检验 亦称studen
  • 基于AR模型的数据预测及Matlab实现

    基于AR模型的数据预测及Matlab实现 自动回归 AR 模型是一种常见的时间序列分析方法 它基于过去一段时间的数据 预测未来的数值走势 本文将介绍如何使用基于AR模型的方法来预测数据 并提供相应的Matlab源代码 首先 我们需要了解AR
  • 剑指 offer (专项突击版)

    剑指 Offer II 001 整数除法 方法一 class Solution public int divide int a int b 考虑被除数为最小值的情况 if a INT MIN if b 1 return INT MIN if