2024年【力扣刷题笔记(二)】特别技巧,模块突破,看完这一篇你就懂了

img
img

既有适合小白学习的零基础资料,也有适合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 = 19

1

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;

简单练习题

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事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行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值