既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
1
10
\frac{1}{2}*\frac{1}{5}=\frac1{10}
21∗51=101 ,所以只需得到一个生成概率为
1
2
\frac{1}{2}
21 的数 和一个生成概率为
1
5
\frac{1}{5}
51 的数👈
int a = rand7(), b = rand7();
while(a > 5) a = rand7(); // 1 / 5
while(b == 7) b = rand7(); // b 为偶数的概率是 1/2
return ((b & 1) == 0 ? 0 : 5) + a;
202.快乐数👊
快乐数,就是每位变成它的平方再相加,最后变成1。是快乐数就返回true
例如:n = 191
2
9
2
=
82
12+92 = 82
12+92=82
8
2
2
2
=
68
82+22 = 68
82+22=68
6
2
8
2
=
100
62+82 = 100
62+82=100
1
2
0
2
0
2
=
1
12+02+0^2 = 1
12+02+02=1
返回true
如果一个数是快乐数,那么它最后一定是变成 1 。
如果不是快乐数,分为两种情况:①陷入一个循环 ②无限递增不循环
分析无限递增的情况:
9
2
=
81
9^2 = 81
92=81 、
9
2
9
2
=
162
9^2 + 9^2 = 162
92+92=162 、
9
2
9
2
9
2
=
243
9^2 + 9^2 +9^2 = 243
92+92+92=243 、
9
2
9
2
9
2
9
2
=
324
9^2 + 9^2 +9^2 +9^2 = 324
92+92+92+92=324 11个9 = 81*11=891
3位数收敛在243 以内, 4位数到11位数收敛在891(三位数),下一步一定也收敛在243以内。所以不存在无限递增的情况
最后只有两种情况 要么变成1,要么陷入一个不是1的循环中。
方法1: 做个标记,陷入循环返回false,得到1返回true
方法2: 快慢指针,由于它们一定陷入循环,要么是全为1的循环,要么是别的循环,类似快慢指针判断环形链表。
如果头碰到1了返回true, 如果头尾相遇了还没碰到1返回false
你快乐了吗🌈
二、神奇的位运算
2.1 位运算常用技巧
最基础的位运算: 复习一遍吧 👇:
<< 左移, >>右移, >>> 忽略符号位右移
除此之外,还有以下常用技巧 👇,记忆一下吧!⭐️
-n = ~n + 1
n & (n - 1) 可以去除 n 的最低位的 1 比如 1010 --> 1000
n & (-n) 可以只保留 n 的最低位的 1 比如: 1010–> 0010
两个数异或,相当于无进位的加法。两个数相与, 可以得到进位位。
2.2 位运算基础
461.汉明距离👊
汉明距离是两个二进制数,二进制位不同的数目。 给你两个数返回汉明距离
二进制位不同的数目,也就是两个数异或一下,不同的地方变成了1,看异或的结果有几个1。
int tem = x ^ y;
int ret = 0;
while(tem != 0) {
ret++;
tem &= tem - 1;//每次去除最后一位1
}
return ret;
190.颠倒二进制位👊
给你一个32位的无符号数, 返回左右翻转后得到的二进制数。
比如:输入11111111111111111111111111111101 返回 10111111111111111111111111111111对于的数
思路: 位运算,左移、右移来实现,第 1 位移到32位处, 第 2 位移到31位处… 第32位移到第1位处,32位全移动一遍就欧克了。
int ret = 0;
for(int i = 0; i < 32; i++) {
ret |= (n & 1) << (31-i);
n >>>= 1;
}
return ret;
136.只出现一次的数字👊
一个数组,里面有一个数字只出现了一次,其他都出现了两次
应为 x ^ x = 0, 且异或满足结合律,直接全部异或,把出现两次的清除。
int ret = 0;
for(int num : nums) {
ret ^= num;
}
return ret;
2.3 二进制特性
我们可以创建一个数,把二进制每位的0 、 1当作一种标记,比如第一位为1, 表示选中了第一个选项。
342.4的幂👊
给你一个整数,判断他是不是 4 的整数次方。
法一: 和 3 的幂的方法 1 一样 ,利用对数,
log
4
n
=
m
\log_4^n = m
log4n=m, 如果 n 是 4 的整数次方,那么m一定是整数
法二: 2的次幂二进制只有 1 个 1。 4 的 次幂也是,并且多一个限制条件, 1只能在奇数位置 。 1、 100、 10000、等
5二进制是0101,奇数位全为1
if (n < 0 || (n & (n-1)) != 0) return false; //有多个1, 或 < 0
return (n & 0x55555555) != 0; //只有一个1 且 奇数位为1;
318.最大单词长度乘积👐
给你一个字符串数组,返回其中两个字符串长度乘积的最大值, 且这两个字符串不能有相同字母。
思路: 首先得判定两个字符串有没有共同字母, 首先想到的可能是用HashSet存字母,两两判断。或者用 bool 数组标记字母,两两判断。
每次两两判断都遍历两人标记数组或集合,很浪费。 用二进制位表示是否有对于字母,两两直接相与即可得到是否有相同字母。
public int maxProduct(String[] words) {
int n = words.length;
int[] flag = new int[n];
for(int i = 0; i < n; i++) flag[i] = getFlag(words[i]);
int ret = 0;
for(int i = 0; i < n; i++)
for(int j = i + 1; j < n; j++)
if((flag[i] & flag[j])== 0) ret = Math.max(words[i].length() \* words[j].length(), ret);
return ret;
}
int getFlag(String s) {
int ret = 0;
for(char c : s.toCharArray()) ret |= 1 << (c - 26);
return ret;
}
338.比特位计数👊
给你一个整数n, 返回0 - n 每个数的二进制数有几个1;返回的是一个长度为 n + 1 的数组
法一:可能最容易想到的遍历每个数, 计算这个是的二进制数 1 的个数。
计算二进制数个数可以用API, Integer.bitCount(), 也能用循环+ n & (n - 1) 消除最后一位
法二: 动态规划, 可以发现后面的数的二进制数,其实是前面某个较小的某位 0 变成 1 形成的。
假设我们用消除最后一位1来找到前面比他小的数, 那0001, 0010,0100,1000都可以由 0000推出。
int[] ret = new int[n + 1];
for(int i = 1; i < n + 1; i++) {
ret[i] = ret[(i & (i - 1))] + 1;
}
return ret;
练习题
268.丢失的数字👊
给你长度为 n 的数组, 数组的数字从 [0, n] 这 n + 1个数中选的,数组里的数不重复。 返回哪个数没被选中。
哪个没选中呢?最简单方法: 求个和, 0 到 n 总和 - 数组总和 剩下的数不就是没选中的嘛
求和得两次遍历, [0, n] 和数组里的值合并起来, 丢失的数字不就是只出现一次的数字嘛。用异或即可得到。
int ret = 0, n = nums.length;
for(int i = 0; i < n; i++) {
ret ^= nums[i] ^ i;
}
return ret ^ n;
693.交替位二进制数👊
给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现
交替出现,最简单方法,从最低位第一位开始,设置一个标志位,进行0 1判断即可
int pre = (n & 1);
n >>= 1;
while(n != 0) {
int cur = (n & 1);
if((pre ^ cur) == 0) return false;
n >>= 1;
pre = cur;
}
return true;
方法二: 不用循环遍历,如果是0 1 交替出现的,则 n ^ (n << 1) ==> 得到最高位1之前全为1, 即 00001010 ==> 00001111
怎么判断最高位及之前低位全是1呢? 给这个数 + 1, 就变成最高位后一位高位为1其他为0, 即00001111 ==> 00010000。 两者相与必为0。
int m = n ^ (n >> 1);
return (m & (m + 1)) == 0;
476.数字的补数👊
对整数的二进制表示取反,再表示为十进制数。 前导0不算。
给每个位置赋值就好啦✌️
int ret = 0;
int count = 0;
while(num != 0) {
ret |= ((num & 1) == 1 ? 0 : 1 ) << count;
num >>= 1;
count++;
}
return ret;
260.只出现一次的数字III👐
给你一个数组,恰好有两个数只出现一次,其他数都是出现两次。 进阶:线性复杂度,常数空间
方法一:用Map,直接做。
进阶:线性复杂度,常数空间
恰好两个数只出现一次, 把数组分成两组,每组一个单独的数字,一组异或起来就可以得到拿个单独的数字。
有什么办法能使这些数分为两组,且一样的数字一定被分在同一组,个数为1的数字被分到不同组?
奇偶肯定不行,个数为 1 的数可能是奇数也可能是偶数。这个区分的标准一定和这两个数的特征相关。
怎么一次遍历就能获得这两个数的特征呢? 全部异或起来, 相同的数的特征被变成了0, 结果相当于这只有两个数异或。
这两个数异或结果为1的位,就可以把两个数区分开。
因为两个数为异或结果为 1 的位 ,对于原始数字来说一定一个为 1 一个 为 0
怎么得到这个为 1 的位呢? 可以用位运算获取最后一个1的位置即 n & (-n)
int sum = 0;
for(int num : nums) sum ^= num;
int flag = sum & (-sum); //最后一个1的位
int[] ret = new int[2];
for(int num : nums) {
//flag 那个位为 0
if((num & flag) == 0) ret[0] ^= num;
else ret[1] ^= num;
}
return ret;
三、基础数据结构
3.1 数组
448.找到所有数组中消失的数字👊
给你一个数组, 数组里的数字是[1, n], 返回[1, n]中数组中没出现的数字
方法很多。有个不用开新空间,用原始数组做标记的方法: 出现过的数A,将原始数组nums[A] 置为负数。最后为正的数的位置是没出现的数字
for(int i = 0; i < nums.length; i++) {
int cur = Math.abs(nums[i]);
if(nums[cur - 1] > 0) nums[cur - 1] = - nums[cur - 1];
}
List<Integer> ret = new ArrayList<>();
for(int i = 0; i < nums.length; i++) {
if(nums[i] > 0) ret.add(i + 1);
}
return ret;
48.旋转图像
给你一个二维矩阵,原地旋转90度。
思路: 模拟即可,可以找找规律,例如先转置,再交换左右列
240.搜索二维矩阵II👐
给你一个二维矩阵,已知每行每列都是递增的,设计可以能快速搜索一个数组是否在矩阵中的算法
从右上角, 大就往下,小就往左。到左下角还没有那就是无了
769.最多能完成排序的块👐
给你一个数组,数组包含数组0-n 每个数只出现一次。求这个数组最多可以分割成多少个子数组,使得子数组排序后,再拼起来仍是有序的。
例如:4 3 2 1 0 只能分成一块,子块排序后整体才是有序的
例如:0 3 2 1 5 4 能分成三块 【0】【3 2 1】【5 4】 子块排序后整体有序
思路:什么情况能拆成子块? 数字大于索引的时候我们必须把他和后面的放到一块,好让他排序达到数字对应索引。 5 1 2 3 4 0, 只能分一块。数字大于索引,必须有足够的位置让这个数字放到对应索引
int ret = 0, max = 0;
for(int i = 0; i < arr.length; i++) {
if(arr[i] == i) ret++;
else {
max = arr[i];
while(i < max) {
i++;
max = Math.max(arr[i], max);
}
ret++;
}
}
return ret;
3.2 栈和队列
232.用栈实现队列👊
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty)
思路: 栈是先入后出, 队列是先入先出。 栈实现队列,入的时候直接放入一个栈1,放到栈顶
出的时候如果栈2为空就把栈1全弹入栈2,这样栈2的顶即为栈1 的底,如果栈2不为空,那栈2的顶一直都是栈1的低
155.最小栈👊
设计一个支持以O(n) 返回栈内最小值的最小栈
思路: 再加一个栈2,栈2里放栈1中的最小值, 如果栈 1 弹出了最小值,栈2也弹出,如果栈1新放入更小的值,就给栈2也加入更小值。
20.有效的括号👊
给定一个只由左右原括号、花括号、和方括号组成的字符串。求这个字符串是否合法。合法就是左右一一对应。
思路:左括号放栈里右括号和栈顶比,匹配不上就返回,匹配上就弹出
3.3 单调栈
单调栈通过维持栈内值的单调递增(递减)性,在整体O(n) 的时间内处理需要大小比较的问题
单调栈保证栈内元素单调递增(递减),代码框架:
Stack<Integer> stack = new Stack<>();
int[] nums = new int[]{9, 6, 4, 3, 1, 2, 6, 3};
for (int num : nums) {
//如果栈顶元素小于要放入的元素,就弹出。
while (!stack.isEmpty() && stack.peek() < num) {
stack.pop();
//
}
stack.push(num);
}
//看情况处理栈内剩余元素
更多练习和技巧:单调栈的运用思路及相关题解 https://blog.csdn.net/weixin_44179010/article/details/122278178
739.每日温度👐
给定一个数组,表示每日温度,求几天后会有更高的温度,如果没有更高的温度返回0
输入: [73, 74, 75, 71, 69, 72, 76, 73]
输出: [1, 1, 4, 2, 1, 1, 0, 0]
单调栈最简单的应用: 找到下一个更大(更小)的数。
维持一个单调递减的栈,一旦遇到更大的数就取出之前之前所有比他小的数,计算天数差。
为方便计算,栈内存放数组下标(即日期)
Stack<Integer> stack = new Stack<>();
int n = temperatures.length;
int[] ret = new int[n];
for(int i = 0; i < n; i++) {
while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
int index = stack.pop();
ret[index] = i - index; // 遍历到更大的数,就把之前较小的数取出,进行计算即可
}
stack.push(i);
}
//如果栈内还有元素,说明这些都是后几天没有更高温度的,天数返回为0,可以省略这一步
return ret;
3.4 优先队列
优先队列可以在O(1)时间获取最大(最小)值, 可以在O(log(n))时间取出最大值或插入任意值
优先队底层是堆来实现的。堆是一个完全二叉树,每个节点的值总是大于等于子节点的值
如果对优先队列不了解,可以看看我另一篇博文:
优先队列用法及相关题目解题思路 https://blog.csdn.net/weixin_44179010/article/details/122127012
23.合并K个升序链表🙌
给你一个链表数组,每个链表都是增序的,把这些链表合并成一个增序链表
优先队列,直接把链表放队列里,按照头节点值的大小升序,每次取出头最小的链表,得到这个最小值后,再把链表的next放回队列
用优先队列这道困难题很容易就秒了
if(lists == null || lists.length == 0) return null;
//创建小顶堆
PriorityQueue<ListNode> priorityQueue = new PriorityQueue<>((node1, node2) -> node1.val - node2.val);
//把链表全放入小顶堆
for(ListNode tem : lists) {
if(tem == null) continue;
priorityQueue.add(tem);
}
ListNode dump = new ListNode();
ListNode cur = dump;
while(!priorityQueue.isEmpty()) {
//取出头最小的链表
ListNode tem = priorityQueue.poll();
cur.next = new ListNode(tem.val);
cur = cur.next;
//把链表下一个放回堆
tem = tem.next;
if(tem != null) priorityQueue.add(tem);
}
return dump.next;
218.天际线问题🙌
给定建筑物起始位置和高度,返回建筑物轮廓线的拐点(水平线的左端点)
题目分析:关键点即水平线的左端点,从左到右扫描所以的边,可以发现,所以的关键点都在两条扫面线围成的矩形的左上角, 还要忽略没有高度后面没有高度变化的矩形。
怎么确定扫描线维成的矩形的真实高度?
遍历到左端点的时候,记录一个高度,碰到更高的左端点记录更高的,获取最高高度
碰到右端点,就把右端点对应的高度去掉,再获取最高的高度
可以用优先队列实现这个功能
class Solution {
public List<List<Integer>> getSkyline(int[][] buildings) {
List<List<Integer>> ret = new ArrayList<>();
int n = buildings.length;
List<int[]> points = new ArrayList<>();
for (int[] building : buildings) {
int h = building[2];
points.add(new int[]{building[0], h}); //左端点存正值,右端点存负值,便于区分左右端点
points.add(new int[]{building[1], -h});
}
Collections.sort(points, (q, w) ->{
if(q[0] != w[0]) return q[0] - w[0];
//两个端点相等,得保证重合得时候下一个左端点在前面
return w[1] - q[1];
});
int preH = 0;
PriorityQueue<Integer> queue = new PriorityQueue<>((q, w) -> w - q);
queue.add(preH);
for (int[] point : points) {
if(point[1] < 0) {
// 如果是右端点,说明这条边结束了,将当前高度从队列中移除
queue.remove(-point[1]);
}else {
// 如果是左端点,说明存在一条往右延伸的可记录的边,将高度存入优先队列
queue.offer(point[1]);
}
// 取出最高高度,如果和前一个高度一样则不记录
int cur = queue.peek();
if (cur != preH) {
List<Integer> list = new ArrayList<>();
list.add(point[0]);
list.add(cur);
ret.add(list);
preH = cur;
}
}
return ret;
}
}
主要参考:【宫水三叶】的题解
3.5 双端队列
双端队列:既能先入先出【获取底部数据】,也能先入后出。【获取顶部数据】。
239.滑动窗口最大值🙌
给你一个整数数组nums 和一个整数 k , 大小为 k 的滑动窗口从最左侧滑动到最右侧。返回滑动窗口中的最大值
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
递增单调栈, 来一个小的放上面,来个大的,把小的踢出去。保证栈底到栈顶是递减的。
取底部就是最大的,最大的出去了就移除底部
public int[] maxSlidingWindow(int[] nums, int k) {
int[] ret = new int[nums.length - k + 1];
Deque<Integer> deque = new LinkedList<>();
for(int i = 0; i < k - 1; i++) {
// 递增单调栈, 来一个小的放上面,来个大的全出去,取底部就是最大的,最大的出去了就移除底部
while(!deque.isEmpty() && nums[i] > deque.peekLast()) {
deque.removeLast();
}
deque.offer(nums[i]);
}
//
int left = 0;
for(int i = k - 1; i < nums.length; i++) {
while(!deque.isEmpty() && nums[i] > deque.peekLast()) { // 保证栈底到栈顶是递减的。
deque.removeLast();
}
deque.offer(nums[i]);
//System.out.println(deque);
ret[i - k + 1] = deque.peekFirst(); // 取底部就是最大的
if(deque.peekFirst() == nums[left++]) { //最大的出去了就移除底部
deque.removeFirst();
}
}
return ret;
}
3.6 哈希表
1.两数之和
给定一个整数数组,已知有且只有两个数的和等于给定值,求这两个数的位置
哈希表key存数组的值, value存数组的索引,遍历hash表的key, 如果存在另一个key,使得两个key和为给定值,那就返回这两个key的value值,即这两个数的位置。
128.最长连续序列👐
字节一面原题。我干了一个排序,凉了。
给你个未排序的数组,找出数字连续的最长序列。 用时间复杂度为O(n)解决
输入:nums = [100,4,200,1,3,2]
输出:4 , 解释: 最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
方法一:排序时间复杂度 O(nlogn)
方法二:用HashSet, 先放入,然后遍历数组找前后+1,2,3…和-1,-2,–3…的元素,找过的数字直接删除。最差O(2n)。
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int num : nums) {
set.add(num);
}
int ret = 0;
for(int num : nums) {
if(!set.contains(num)) continue;
int countInc = 0, countDec = 0;
while(set.contains(num + countInc)) {
set.remove(num + countInc);
countInc++;
}
while(set.contains(num - countDec - 1)) {
set.remove(num - countDec);
countDec++;
}
if(countInc + countDec > ret) ret = countInc + countDec;
}
return ret;
}
方法三: HashMap存每个值及其对应区间长度。每次找比他大1,2,3…的数。 从最小那个数开始往后找才是最长的,所有如果存在比这个数小1的数,就不从它开始找。最差O(2n)不用删除元素。
方法核心,从最小的数位置开始往后找。一段连续的数字,最小位置只有一个
public int longestConsecutive(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int num : nums) set.add(num);
int ret = 0;
for(int num : set) {
if(set.contains(num - 1)) continue;
int count = 1;
while(set.contains(num + count)) {
count++;
}
if(count > ret) ret = count;
}
return ret;
}
149.直线上最多的点🙌
给你一个数组points, points[i] = [xi, yi] 表示X-Y平面上一个点,求最多多个点在一条直线上。
看数据长度,数组长度不大于300。可以疯狂暴力啦。
最简单的思路, 遍历所有点,计算这个点到其他点的斜率,斜率相同就是一条线的,用Map计数, 时间复杂度O(n2).
【一个点+斜率就可以唯一确定一条直线】
虽然是暴力遍历,还是要注意一些细节。 斜率应该用分数来存储,因为浮点数不准。
怎么化简为最简分数呢? 两数都除以最大公约数。 当然这是分子分母都不为0, 如果分子为0,直接等于0,分母为0等于一个特殊值即可。
public int maxPoints(int[][] points) {
int ret = 1;
int n = points.length;
if(n <= 2) return n;
for(int i = 0; i < n; i++) {
if (ret >= n - i || ret > n / 2) break; //如果一条直线上的点的数量已经超过半数,那这条直线一定是点最多的直线
Map<Integer, Integer> map = new HashMap<>();
for(int j = i + 1; j < n; j++) {
int dx = points[i][0] - points[j][0];
int dy = points[i][1] - points[j][1];
if(dx == 0) dy = 1;
else if(dy == 0) dx = 1;
else {
if(dy < 0) {//负数提到前面
dy = -dy;
dx = -dx;
}
int gcdXY = gcd(Math.abs(dx), Math.abs(dy));
dx /= gcdXY;
dy /= gcdXY;
}
int k = dx \* 20001 + dy ;
map.put(k, map.getOrDefault(k, 1) + 1);
ret = Math.max(ret, map.get(k));
}
}
return ret;
}
public int gcd(int a, int b) {
return b != 0 ? gcd(b, a % b) : a;
}
3.7 前缀和
一维、二维的前缀和,都是把每个位置之前的一维线段或二维矩形预先存储,方便加速计算。
通过前缀和,我们很容易获取到数组任意 [l ,r]的连续区间的和。后面的前缀和减前面的就是一段连续子数组[l ,r]区间和。
更多前缀和总结:看我的这篇文章 https://blog.csdn.net/weixin_44179010/article/details/121906773
303.区域和检索-数组不可变👊
一维前缀和简单应用
304.二维区域和检索-矩阵不可变👐
二维前缀和简单应用
560.和为K的子数组👐
给你一个整数数组 nums 和一个整数 k ,返回 该数组中和为 k 的子数组的个数 。
思路:拿到前缀和数组后,题目相当于返回前缀和数组,两两区间差为k的个数。
我们可以把前缀和存入Map中,后面得前缀和找前面是否存在相减为k的前缀和
int n = nums.length;
Map<Integer, Integer> map = new HashMap<>();
map.put(0,1);
int sum = 0, ret = 0;
for(int i = 0; i < n; i++) {
sum += nums[i];
if(map.containsKey(sum - k)) ret += map.get(sum - k);
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
return ret;
简单练习题
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
3.7 前缀和
一维、二维的前缀和,都是把每个位置之前的一维线段或二维矩形预先存储,方便加速计算。
通过前缀和,我们很容易获取到数组任意 [l ,r]的连续区间的和。后面的前缀和减前面的就是一段连续子数组[l ,r]区间和。
更多前缀和总结:看我的这篇文章 https://blog.csdn.net/weixin_44179010/article/details/121906773
303.区域和检索-数组不可变👊
一维前缀和简单应用
304.二维区域和检索-矩阵不可变👐
二维前缀和简单应用
560.和为K的子数组👐
给你一个整数数组 nums 和一个整数 k ,返回 该数组中和为 k 的子数组的个数 。
思路:拿到前缀和数组后,题目相当于返回前缀和数组,两两区间差为k的个数。
我们可以把前缀和存入Map中,后面得前缀和找前面是否存在相减为k的前缀和
int n = nums.length;
Map<Integer, Integer> map = new HashMap<>();
map.put(0,1);
int sum = 0, ret = 0;
for(int i = 0; i < n; i++) {
sum += nums[i];
if(map.containsKey(sum - k)) ret += map.get(sum - k);
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
return ret;
简单练习题
[外链图片转存中…(img-SWOsX13a-1715591345491)]
[外链图片转存中…(img-XaxQmoWz-1715591345492)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!