523. 连续的子数组和 同余定理,前缀和,set

连续子数组和问题的解法与同余定理
博客围绕“连续的子数组和”问题展开,给出题目,即根据特定条件判断能否从二进制字符串下标 0 移动到最后一位。介绍两种解法,解法一利用前缀和性质与同余定理,避免超时;解法二为暴力解法,但会超时。

523. 连续的子数组和 同余定理,前缀和

题目:

给你一个下标从 0 开始的二进制字符串 s 和两个整数 minJump 和 maxJump 。一开始,你在下标 0 处,且该位置的值一定为 ‘0’ 。当同时满足如下条件时,你可以从下标 i 移动到下标 j 处:

i + minJump <= j <= min(i + maxJump, s.length - 1) 且
s[j] == ‘0’.
如果你可以到达 s 的下标 s.length - 1 处,请你返回 true ,否则返回 false 。

解法一:

区间和:

由前缀和的性质,preSum[j] - preSum[i]可以得到 (i+1) ~ j 区间内的和。

余数等于k:

作判断时需用到同余定理,不然会超时。

同余定理总结下:

  • (a+b)mod n = ((a mod n)+(b mod n)) mod n
  • (a-b) mod n = ((a mod n) - (b mod n)) mod n
  • ab mod n = ((a mod n) (b mod n) mod n

(a - b)mod n = 0 -> a mod n = b mod n

于是我们可以用上最后一条的性质,从i = 2 开始循环,每次插入preSum[i-2]%k到容器中,并对preSum[i]%k作判断,天然的能保证preSum[i]%k是与i-2及之前的前缀和作判断即:

((preSum[i] - preSum[j]) mod k = 0 -> preSum[i] mod k == preSum[j] mod k == 0或t, j = 0~i-2 ),这样就可以不用map存键值对,直接用set存preSum就好。同时我们还可以边求前缀和边作判断。

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        const int len = nums.size();
        int preSum[len+2];
        preSum[0] = 0, preSum[1] = nums[0]%k;
        for(int i = 2; i <= len; i++) {
            preSum[i] = (preSum[i-1] + nums[i-1])%k;
            set.insert(preSum[i-2]);
            if(set.count(preSum[i]%k))
                return true;
        }
        return false;
    }
private:
    unordered_set<int> set;
};

解法二:

暴力解法,感觉还是挺简练的,虽然会超时:

class Solution {
public:
    bool checkSubarraySum(vector<int>& nums, int k) {
        const int len = nums.size();
        int preSum[len+2];
        preSum[0] = 0;
        for(int i = 1; i <= len; i++)
            preSum[i] = (preSum[i-1] + nums[i-1])%k;
        for(int i = 0, j = len; j != 2 ; i++) {
            if(i == j-1 && i) j--, i = 0;//有j = 2的情况。
            if((preSum[j]-preSum[i])%k == 0)
                return true;
        }
        return false;
    }
};
<think>我们正在准备后端开发面试,需要整理常见面试问题及其参考答案。根据引用[1]引用[2],面试官会考察分布式系统、并发、数据结构与算法、数据库、网络、系统设计等方面的问题。同时,引用[3]提到了可变值与不可变值的选择,引用[4]提到了线程的等待唤醒机制,这些都是并发编程中的重要知识点。 下面我们将分类列出后端开发面试的常见问题,并提供简要的参考答案。注意,由于篇幅限制,参考答案不会过于详细,但会覆盖核心要点。 ### 一、数据结构与算法 1. **快速排序的原理是什么?** 快速排序是一种分治算法。它选择一个元素作为基准(pivot),将数组分为两部分:小于基准的元素大于等于基准的元素,然后递归地对这两部分进行排序。 时间复杂度:平均$O(n \log n)$,最坏$O(n^2)$(当数组已经有序时)。优化方法包括随机选择基准或使用三数取中法。 2. **如何实现二叉树的遍历?** - 前序遍历:根节点 -> 左子树 -> 右子树 - 中序遍历:左子树 -> 根节点 -> 右子树 - 后序遍历:左子树 -> 右子树 -> 根节点 可以使用递归或迭代(栈)实现。 ### 二、数据库 1. **数据库事务的ACID特性是什么?** - 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败回滚。 - 一致性(Consistency):事务执行前后,数据库状态必须保持一致(满足所有约束)。 - 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务。 - 持久性(Durability):一旦事务提交,其结果就是永久性的。 2. **什么是SQL注入?如何防止?** SQL注入是攻击者通过输入恶意SQL语句来操纵数据库。防止方法: - 使用预编译语句(Prepared Statements)参数化查询。 - 对用户输入进行过滤转义。 - 最小化数据库权限。 ### 三、网络 1. **TCPUDP的区别?** - TCP是面向连接的、可靠的、基于字节流的传输层协议,提供超时重传、流量控制、拥塞控制等机制,保证数据顺序正确性。 - UDP是无连接的、不可靠的、基于数据报的协议,不保证顺序正确性,但开销小、速度快。 2. **HTTP状态码有哪些?** - 1xx:信息响应 - 2xx:成功(如200 OK) - 3xx:重定向(如301 永久重定向) - 4xx:客户端错误(如404 未找到) - 5xx:服务器错误(如500 内部服务器错误) ### 四、并发编程 1. **线程进程的区别?** - 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位。 - 一个进程可以包含多个线程,线程共享进程的内存空间,而进程之间内存独立。 - 线程切换开销小于进程。 2. **wait()sleep()的区别?** - `wait()`是Object类的方法,调用时会释放锁,需要被`notify()`或`notifyAll()`唤醒。 - `sleep()`是Thread类的方法,调用时不释放锁,时间到后自动恢复运行[^4]。 ### 五、分布式系统 1. **分布式系统中常见的问题挑战是什么?如何解决?** - 问题:网络延迟、分区、节点故障、数据一致性等。 - 解决方案: - 使用超时重试处理网络问题。 - 通过副本机制(Replication)提高可用性。 - 使用分布式一致性算法(如Raft、Paxos)保证数据一致性。 - 采用分布式事务协议(如两阶段提交)[^1]。 ### 六、系统设计 1. **如何设计一个短链接服务?** - 将长链接映射为短链接(使用哈希或自增ID)。 - 使用分布式键值存储(如Redis)缓存映射关系。 - 使用数据库持久化存储。 - 考虑高并发扩展性。 ### 七、编程语言(以Python为例) 1. **Python中可变对象不可变对象有什么区别?** - 不可变对象:创建后不能修改(如int, tuple, str)。修改操作会创建新对象。 - 可变对象:创建后可以修改(如list, dict, set)。 - 使用不可变对象可以提高并发安全性可预测性[^3]。 2. **Python的GIL是什么?** GIL(全局解释器锁)是CPython解释器中的互斥锁,它确保同一时刻只有一个线程执行Python字节码。这导致多线程程序在CPU密集型任务中无法充分利用多核。 以上是后端开发面试中常见的问题及参考答案。建议根据自身知识短板进行深入学习,并多做实践。 §§相关问题§§ 1. 如何优化数据库查询性能? 2. 解释CAP定理及其在分布式系统中的应用。 3. 什么是RESTful API?设计原则是什么? 4. 解释HTTP/2的新特性。 5. 如何设计一个高并发的秒杀系统?</think>### 后端开发面试常见问题及参考答案 #### 一、数据结构与算法 1. **快速排序的原理与实现** 快速排序采用分治策略: - 选择基准元素(pivot) - 将数组分为小于基准大于基准的两部分 - 递归排序子数组 时间复杂度:平均 $O(n \log n)$,最坏 $O(n^2)$ 优化:随机选择基准或三数取中法避免最坏情况 ```python def quicksort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr)//2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quicksort(left) + middle + quicksort(right) ``` 2. **二叉树遍历的实现** - 前序遍历:根 → 左 → 右 - 中序遍历:左 → 根 → 右 - 后序遍历:左 → 右 → 根 递归实现示例(中序): ```python def inorder(root): return inorder(root.left) + [root.val] + inorder(root.right) if root else [] ``` --- #### 二、数据库 1. **事务的ACID特性** - **原子性**(Atomicity):事务全部成功或全部回滚 - **一致性**(Consistency):事务前后数据满足完整性约束 - **隔离性**(Isolation):并发事务互不干扰 - **持久性**(Durability):提交后数据永久存储 实现方式:WAL(Write-Ahead Logging)锁机制[^1] 2. **SQL索引优化** - 索引类型:B+树(范围查询)、哈希(等值查询) - 最左前缀原则:联合索引 $(a,b,c)$ 仅支持 $a$、$a,b$、$a,b,c$ 查询 - 避免索引失效:不使用函数转换字段、避免 `OR` 条件 --- #### 三、并发编程 1. **线程同步机制** - `synchronized`(Java)/ `Lock`(Python):保证代码块互斥访问 - `wait()` 与 `notify()`:线程等待与唤醒(需在同步块中使用)[^4] - 信号量(Semaphore):控制并发线程数 2. **可变性与不可变性** - **不可变对象**(如String):线程安全,无需锁 - **可变对象**(如HashMap):需同步控制 优先使用不可变对象提高并发安全性[^3] --- #### 四、分布式系统 1. **CAP定理** - **一致性**(Consistency):所有节点数据一致 - **可用性**(Availability):每个请求都能响应 - **分区容错性**(Partition tolerance):网络分区时系统仍可用 分布式系统必须满足 **P**,在 **C** **A** 间权衡(如CP的ZooKeeper vs AP的Cassandra)[^1] 2. **分布式事务解决方案** - **两阶段提交(2PC)**:协调者统一决策,存在阻塞风险 - **TCC模式**(Try-Confirm-Cancel):业务层补偿机制 - **消息队列**:最终一致性(如RabbitMQ事务消息) --- #### 五、网络协议 1. **TCP vs UDP** | 特性 | TCP | UDP | |------------|----------------|--------------| | 连接 | 面向连接 | 无连接 | | 可靠性 | 可靠(重传机制) | 不可靠 | | 速度 | 慢 | 快 | | 应用场景 | HTTP/MySQL | 视频流/DNS | 2. **HTTP状态码** - `200 OK`:请求成功 - `404 Not Found`:资源不存在 - `503 Service Unavailable`:服务不可用 - `401 Unauthorized`:未认证 --- #### 六、系统设计 1. **短链接系统设计** - 哈希算法:将长URL映射为62进制短码(如 `aBc1d`) - 存储:Redis缓存键值对,MySQL持久化 - 容错:布隆过滤器防恶意请求 2. **高并发限流策略** - **令牌桶算法**:恒定速率生成令牌,突发流量可处理 - **漏桶算法**:固定速率处理请求 - 工具:Redis + Lua脚本实现分布式限流 --- ### 面试准备建议 1. **基础知识**:重点复习算法、数据库、操作系统 2. **项目经验**:用STAR法则(情境-任务-行动-结果)描述项目 3. **系统设计**:从需求分析 → 数据模型 → 接口设计逐步展开 4. **行为问题**:准备“团队冲突处理”“技术决策依据”等案例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值