2024年C C++最全【力扣刷题笔记(二)】特别技巧,模块突破(1),2024年最新2024C C++常见面试题

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

给你一个十进制数,转化成7进制数的字符串
输入:100
输出:“202” —解释:100 = 2 * 49 + 0 * 7 + 2 * 1

Java API :

return Integer.toString(num, 7);

进制转换一般利用除法和取模进行计算, 类似十进制取出每位的数。取出七进制每位的数再拼接起来就行了。

while (num != 0) {
      sb.append(num % 7); //最低位到最高位
      num /= 7;
 }

172.阶乘后的零👐

给你一个整数,求这个数的阶乘结尾有几个0;

尾部的 0 是 由 2 x 5 而来的, 把阶乘中每个数都拆成质数相乘,统计有多少个 2 和 5. 由于 2 的个数一定是远多于 5 的个数的,所有只需统计 5 的个数。 6 之前有 1 个 5 , 25 之前有 6个5【5、10、15、20、5、5】

    public int trailingZeroes(int n) {
        return n == 0 ? 0 : n / 5 + trailingZeroes(n / 5);
    }

326.3的幂👊

判断一个数,是否是3的整数次方。

两种办法:

  1. 利用对数,

log

3

n

=

m

\log_3^n = m

log3n​=m, 如果 n 是 3 的整数次方,那么m一定是整数。由换底公式:

log

3

n

=

log

10

n

log

10

3

\log_3^n = \frac{\log_{10}^n}{ \log_{10}^3}

log3n​=log103​log10n​​
2. 由于3的幂次的质因子只有3,利用3的次方的最大数, 在 int 范围 3的最大次方数是

3

19

3^{19}

319, 如果 n 是 3 的整数次方,那么一定能被这个数整除,反正亦然,注意 n 小于0 的情况直接返回。

        double tem = Math.log10(n) / Math.log10(3);
        return tem == (int)tem;

1.4 随机与采样

384.打乱数组👐

给你一个数组, 实现两个指令函数,第一个随机打乱这个数组,第二个恢复这个数组。

把原始数组保存下来,恢复的时候直接恢复。打乱数组可以用洗牌算法。即遍历数组,把位置 i 的数字和其他随机位置的数字交换。
打乱数组代码:

    public int[] shuffle() {
        int[] tem = nums.clone();
        for(int i = 0; i < tem.length; i++) {
            swap(i, r.nextInt(tem.length), tem); //位置 i 数字 和 随机位置交换
        }
        return tem;
    }

练习

168.Excel表列名称👊

给你一个整数 columnNumber ,返回它在 Excel 表中相对应的列名称。
A – 1
Z – 26
AA–27
AB–28

26进制变种, 注意这里是1-26, 不是0-25, 所有取余和相除的时候得注意

        char[] arr = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
        'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
        StringBuilder sb = new StringBuilder();
        while(columnNumber != 0) {
            int num = ((columnNumber % 26 - 1) + 26) % 26; // 余1 对应 0 , 余0 对应 25
            sb.append(arr[num]);
            columnNumber = (columnNumber - 1) / 26; // 26 对应 25。再除以 26
        }
        return sb.reverse().toString();

67.二进制求和👊

给你两个二进制数字得字符串, 计算相加后的二级制字符串

思路: 模拟。 模拟进位即。

238.除自身以外数组的乘积👐

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。

思路: 类似前缀和思想,answer[i] = 左边的积 * 右边的积

        int[] ret = new int[nums.length];
        //左边乘积 
        int sum = 1;
        for(int i = 0 ; i < nums.length; i++) {
            ret[i] = sum;
            sum \*= nums[i];
        }
        //\* 右边乘积
        sum = 1;
        for(int i = nums.length - 1 ; i >= 0; i--) {
            ret[i] \*= sum; //左边乘积 \* 右边乘积 
            sum \*= nums[i];
        }
        return ret;

462.最少移动次数使数组元素相等II👐

给你一个整数数组, 每次可以使一个数 +1 或 -1, 求最少多少次能把所有数字变成相同的数字。

思路: 全变成中位数就是最优解。

169.多数元素👊

给你一个数组,返回多数元素,多数元素指出现次数 大于 ⌊ n/2 ⌋ 的元素

思路: 摩尔投票。
可以这样理解。每个不同的元素都是一个阵营,现在所有人大混战,所有人战斗力都是一样的也就是1v1 一定同归于尽。
如果有人数超过一半的阵营,那这个阵营一定是最后胜出者。

470.用Rand7()实现Rand10()👐

系统给你一个能实现生成1-7随机数的函数, 你在这个函数基础上写一个能生成1-10随机数的函数。不能借助其他内置函数。

👉生成1-10的随机数,即每个数的概率的

1

10

\frac1{10}

101​ 。

1

2

1

5

=

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; // 遍历到更大的数,就把之前较小的数取出,进行计算即可


![img](https://img-blog.csdnimg.cn/img_convert/9bdf50480b3d0b0d983d651440e779d3.png)
![img](https://img-blog.csdnimg.cn/img_convert/020ee15d37ba21de89b42bf18ccd8266.png)

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

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


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

        	//如果栈顶元素小于要放入的元素,就弹出。
            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; // 遍历到更大的数,就把之前较小的数取出,进行计算即可


[外链图片转存中...(img-UnYWxHLV-1715535314317)]
[外链图片转存中...(img-iNOeU9y8-1715535314318)]

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

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


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

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值