vector 初始化误区
// ❌ 错误:空vector不能直接用[]赋值
vector<double> vec;
vec[0] = 3.14; // 运行时错误!
// ✅ 方法1: 指定大小
vector<double> vec(5);
vec[0] = 3.14; // 正确
// ✅ 方法2: push_back
vector<double> vec;
vec.push_back(3.14); // 正确
// ✅ 方法3: 初始化列表
vector<double> vec = {3.14, 2.71}; // 正确2413. 最小偶倍数 已解决
题意
给你一个正整数 n ,返回 2 和 n 的最小公倍数(正整数)。
思路
- 我就直接用循环模拟了
- 我居然没想到,n为奇答案就是2*n,n为偶答案就是n;但是这个优雅的写法我还是写不出来哈
代码
class Solution {
public:
int smallestEvenMultiple(int n) {
return (n % 2 + 1) * n;
}
};位运算符
在 C++ 中,有三个主要的位运算符,我来详细解释一下:
三个主要位运算符:
1. 按位与 (AND) - &
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int result = a & b; // 结果: 0001 (十进制 1)2. 按位或 (OR) - |
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int result = a | b; // 结果: 0111 (十进制 7)3. 按位异或 (XOR) - ^
int a = 5; // 二进制: 0101
int b = 3; // 二进制: 0011
int result = a ^ b; // 结果: 0110 (十进制 6)位运算的简单应用:
// 检查奇偶性
bool isEven = (num & 1) == 0; // 与1进行AND运算
// 乘以2的幂次
int multiplyBy8 = num << 3; // 左移3位相当于乘以8
// 除以2的幂次
int divideBy4 = num >> 2; // 右移2位相当于除以41512. 好数对的数目 已解决
题意
给你一个整数数组 nums 。
如果一组数字 (i,j) 满足 nums[i] == nums[j] 且 i < j ,则称这组数字是一个 好数对 。
返回好数对的数目。
思路
- 我的思路直接是二重循环。。O(n^2)复杂度
- 哈希表,O(n)复杂度
- 注意到:当 cnt[x] 中的 x 第一次出现时:会自动创建键 x,值会被初始化为 0(对于 int 类型)
代码
class Solution {
public:
int numIdenticalPairs(vector<int>& nums) {
int ans = 0;
unordered_map<int, int> cnt;
for (int x : nums) { // x = nums[j]
// 此时 cnt[x] 表示之前遍历过的 x 的个数,加到 ans 中
// 如果先执行 cnt[x]++,再执行 ans += cnt[x],就把 i=j 这种情况也统计进来了,算出的答案会偏大
ans += cnt[x];
cnt[x]++;
}
return ans;
}
};1534. 统计好三元组 已解决
题意
给你一个整数数组 arr ,以及 a、b 、c 三个整数。请你统计其中好三元组的数量。
如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件,则认为它是一个 好三元组 。
- 0 <= i < j < k < arr.length
- |arr[i] - arr[j]| <= a
- |arr[j] - arr[k]| <= b
- |arr[i] - arr[k]| <= c
其中 |x| 表示 x 的绝对值。
返回 好三元组的数量 。
思路
- 我还是只会暴力O(n^3)复杂度,学一下前缀和
- 前缀和定义为:( S_i = \sum_{j=1}^{i} a_j )
- 递推关系式为:( S_i = S_{i-1} + a_i )
- 计算区间 ([l, r]) 的和,只需用前缀和相减:( S([l, r]) = S_r - S_{l-1} )
- 通过 (O(n)) 时间预处理前缀和数组,可以将单次区间和查询的复杂度降至 (O(1))。
代码
class Solution {
public:
int countGoodTriplets(vector<int>& arr, int a, int b, int c) {
int n = arr.size(), mx = ranges::max(arr);
vector<int> count(mx+ 1, 0); //表示值v出现次数
// prefix[v] 表示值 ≤ v 的元素个数
vector<int> prefix(mx + 2, 0);
int ans = 0;
for (int j = 0; j < n; j++) {
// 对于每个 j,枚举所有 k > j
for (int k = j + 1; k < n; k++) {
// 先检查 |arr[j] - arr[k]| <= b
if (abs(arr[j] - arr[k]) <= b) {
// 确定 i 的取值范围
int left = max({arr[j] - a, arr[k] - c, 0});
int right = min({arr[j] + a, arr[k] + c, mx});
// 如果范围有效,计算范围内有多少个元素
if (left <= right) {
// prefix[right+1] - prefix[left] 就是值在 [left, right] 内的元素个数
ans += prefix[right + 1] - prefix[left];
}
}
}
// 处理完当前 j 的所有 k 后,把 arr[j] 加入统计
// 这样保证后续的 j 能看到当前 j 的元素
count[arr[j]]++;
// 更新前缀和数组
prefix[0] = 0;
for (int v = 1; v <= mx + 1; v++) {
prefix[v] = prefix[v - 1] + count[v - 1];
}
}
return ans;
}
};258. 各位相加 已解决
题意
给定一个非负整数 num,反复将各个位上的数字相加,直到结果为一位数。返回这个结果。
思路
- 还是只会用循环,来看看O(1)做法:
- 比如 num = 678,计算过程为:
678 -> 21 -> 3 - 本质上是求模9的同余数。
- 如果 num == 0,返回 0;
- 如果 num % 9 == 0,返回 9;
- 否则返回 num % 9。
- 公式:
(num - 1) % 9 + 1
代码
// 循环版
// 略
// O(1)版
class Solution {
public:
int addDigits(int num) {
return (num - 1) % 9 + 1;
}
};231. 2 的幂 已解决
题意
给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。 如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方
思路
- 我用循环,当n很大的时候,超时,找数学规律,没找出来。
- 位运算:如果 n 是 2 的幂,那么
n & (n-1)一定等于 0。 - 注意优先级。
代码
class Solution {
public:
bool isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
//注意这个括号,优先级:算术 > 移位 > 关系 > 相等 > 位运算 > 逻辑 > 赋值
}
};326. 3 的幂 已解决
题意
给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。
思路
- 循环法:已知 $2^{31} \approx 2.1 \times 10^9$,大约 $x$ 最大约等于20,保险起见可以枚举到30。
- 数论法:
while (n % 3 == 0) n /= 3; return n == 1; - 递归法
- 整数限制法:在 32 位有符号整数范围内,最大的 3 的幂是 $3^{19} = 1162261467$,判断是否能整除。
代码
class Solution {
public:
bool isPowerOfThree(int n) {
//计算出大概是20左右,可以测试输出,刚好报错的那个数前面就是了
// 在 32 位有符号整数范围内,最大的 3 的幂是 3^19 = 1162261467
// 如果 n 是 3 的幂,那么 1162261467 一定能被 n 整除
return n > 0 && 1162261467 % n == 0;
}
};263. 丑数 已解决
题意
丑数 就是只包含质因数 2、3 和 5 的 正 整数。
思路
- 我的思路是循环判断,但无法处理多个相同质因数。
- 位运算优化/数学法:
- 如果
n <= 0,返回false。 - 依次去掉
n中的因子3,直到n不是3的倍数。 - 同理去掉
n中的因子5。 - 最后只剩下因子
2,即判断n是否为2的某次幂。
- 如果
代码
class Solution {
public:
bool isUgly(int n) {
if (n <= 0) {
return false;
}
while (n % 3 == 0) {
n /= 3;
}
while (n % 5 == 0) {
n /= 5;
}
while(n % 2 == 0) n/=2;
if(n == 1) return true;
return false;
//或者这三行直接写成return (n & (n - 1)) == 0;
}
};867. 转置矩阵 已解决
题意
给你一个二维整数数组 matrix, 返回 matrix 的 转置矩阵 。 矩阵的 转置 是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引。
思路
- 太生疏了,错误好多;
- 二元数组计算行和列
m = matrix.size(),n = matrix[0].size()。 - 赋值
ans[j][i] = matrix[i][j]。
代码
class Solution {
public:
vector<vector<int>> transpose(vector<vector<int>>& matrix) {
int m = matrix.size(); // 行
int n = matrix[0].size(); // 列
vector<vector<int>> ans(n, vector<int>(m)); // n x m
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
ans[j][i] = matrix[i][j];
}
}
return ans;
}
};1422. 分割字符串的最大得分 已解决
题意
给你一个由若干 0 和 1 组成的字符串 s ,请你计算并返回将该字符串分割成两个 非空 子字符串(即 左 子字符串和 右 子字符串)所能获得的最大得分。
「分割字符串的得分」为 左 子字符串中 0 的数量加上 右 子字符串中 1 的数量。
思路
- 暴力法 O(n^2)。
- 前缀和 O(n)。
代码
class Solution {
public:
int maxScore(string s) {
int n = s.size();
// 方法1:前缀和数组(清晰易懂)
vector<int> prefixZero(n, 0); // 前缀0的个数
vector<int> prefixOne(n, 0); // 前缀1的个数
// 构建前缀和数组
for(int i = 0; i < n; i++) {
if(i == 0) {
if(s[i] == '0') {
prefixZero[0] = 1;
prefixOne[0] = 0;
} else {
prefixZero[0] = 0;
prefixOne[0] = 1;
}
} else {
prefixZero[i] = prefixZero[i-1] + (s[i] == '0' ? 1 : 0);
prefixOne[i] = prefixOne[i-1] + (s[i] == '1' ? 1 : 0);
}
}
int totalOnes = prefixOne[n-1];
int ans = 0;
for(int i = 0; i < n - 1; i++) {
// 左子串0的个数:prefixZero[i]
// 右子串1的个数:totalOnes - prefixOne[i]
int score = prefixZero[i] + (totalOnes - prefixOne[i]);
ans = max(ans, score);
}
return ans;
}
};2586. 统计范围内的元音字符串数 已解决
题意
给定一个下标从 0 开始的字符串数组 words 和两个整数 left、right。
如果一个字符串以元音字母开头且以元音字母结尾(元音为 'a', 'e', 'i', 'o', 'u'),则称其为元音字符串。
返回在闭区间 [left, right] 内,words[i] 为元音字符串的数量。
思路
- 计数器,判断函数。
- 灵神优雅版:利用 string 的
find方法。
代码
class Solution {
public:
int vowelStrings(vector<string>& words, int left, int right) {
const string vowel = "aeiou";
int ans = 0;
for (int i = left; i <= right; i++) {
string& s = words[i];
ans += vowel.find(s[0]) != string::npos &&
vowel.find(s.back()) != string::npos;
//充分利用string的迭代器
}
return ans;
}
};852. 山脉数组的峰顶索引 已解决
题意
符合下列属性的数组 arr 称为 山脉数组 :
- arr.length >= 3
- 存在 i(0 < i < arr.length - 1)使得:
- arr[0] < arr[1] < ... < arr[i-1] < arr[i]
- arr[i] > arr[i+1] > ... > arr[arr.length - 1]
给你一个整数数组 arr,它保证是一个山脉,请你找出并返回顶部所在的山峰元素 arr[i] 的下标 i。
思路
- 二分查找。
mid = l + (r - l) / 2防止溢出。
代码
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int l = 0, r = arr.size() - 1;
while (l < r) {
int mid = l + (r - l) / 2;
if (arr[mid] < arr[mid + 1]) { // 上升阶段
l = mid + 1;
} else { // 下降阶段
r = mid;
}
}
return l; // 或 r,二者相等
}
};