代码随想录

数组

1. 二分搜索法

. - 力扣(LeetCode)

注意三点:

1.区间的闭合,定义的左右区间是要合理[1,1)不对,  [1,1]对,[1,2)对
//左闭右闭,初始下标为0
int left = 0;
int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]

//左闭右开,初始下标为0
int right = nums.size(); // 定义target在左闭右开的区间里,[left, right)
2.  当left==right,区间[left, right] [left, right)是否依然有效,和1一起思考
 while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
 while (left < right)  { // 当left==right,区间[left, right)依然有效,所以用 <
3.  当目标值不在中间时,在新区间,核心要更新新区间的边界值,且middele不在新区间内
//左闭右闭
if (nums[middle] > target) 
{
  right = middle - 1; // target 在左区间,所以[left, middle - 1]
 
} 

else if (nums[middle] < target) 
{
  left = middle + 1; // target 在右区间,所以[middle + 1, right]
           
 } 

else 
{ // nums[middle] == target
   return middle; // 数组中找到目标值,直接返回下标
}

//左闭右开
if (nums[middle] > target) 
{
  right = middle; // target 在左区间,所以[left, middle),middle不在新区间内
 
} 

else if (nums[middle] < target) 
{
  left = middle + 1; // target 在右区间,所以[middle + 1, right]
           
 } 

else 
{ // nums[middle] == target
   return middle; // 数组中找到目标值,直接返回下标
}

4.计算middle的一些细节

//左闭右闭
int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
//左闭右开
int middle = left + ((right - left) >> 1);
 4.1 int middle = left + ((right - left) >> 1);这个>>1是什么意思?

`>> 1` 是一个右移位运算符,它将操作数的二进制表示右移一位。相当于将操作数除以2,并舍弃小数部分。

在二分查找算法中,`middle = left + ((right - left) >> 1);` 这行代码用来计算数组的中间位置。由于 `(right - left)` 表示当前搜索区间的长度,右移一位相当于取中点的偏移量。将这个偏移量加到 `left` 上就得到了 `middle` 的索引。

这个写法的主要目的是避免在计算 `middle` 时出现整数溢出。如果直接使用 `(left + right) / 2`,当 `left` 和 `right` 都很大的时候,`left + right` 可能会超出整数的范围,而 `left + ((right - left) >> 1)` 则可以避免这个问题。

总结:
- `>> 1` 将数除以2,效率比除法运算高。
- 这种写法可以避免整数溢出,是一种比较安全的计算中间索引的方法。

4.2  如果(right - left)是奇数呢

当 `(right - left)` 是奇数时,右移一位 (`>> 1`) 相当于执行整除操作,即只保留整数部分,舍弃小数部分。

具体来说:
- 假设 `(right - left)` 是奇数,比如 `5`,它的二进制表示是 `101`。
- 右移一位 (`>> 1`) 后,得到 `10`,即 `2`。

这种情况下,`middle` 的计算相当于将 `(right - left)` 除以 `2` 并向下取整。所以即使 `(right - left)` 是奇数,`middle` 的计算仍然是有效的,它会返回向下取整后的中间位置。

例如:
- 如果 `left = 0`,`right = 5`,那么 `(right - left) >> 1` 等于 `2`,此时 `middle = 0 + 2 = 2`。
- 如果 `left = 3`,`right = 8`,那么 `(right - left) >> 1` 等于 `2`,此时 `middle = 3 + 2 = 5`。

因此,无论 `(right - left)` 是奇数还是偶数,这种写法都能正确计算出中间位置。

4.2 偶数呢

当 `(right - left)` 是偶数时,右移一位 (`>> 1`) 仍然有效且直观。

具体来说:
- 假设 `(right - left)` 是偶数,比如 `4`,它的二进制表示是 `100`。
- 右移一位 (`>> 1`) 后,得到 `10`,即 `2`。

此时 `(right - left) >> 1` 就等于 `2`,这意味着中间索引距离 `left` 的偏移量是 `2`。

例如:
- 如果 `left = 0`,`right = 4`,那么 `(right - left) >> 1` 等于 `2`,此时 `middle = 0 + 2 = 2`。
- 如果 `left = 1`,`right = 5`,那么 `(right - left) >> 1` 仍然等于 `2`,此时 `middle = 1 + 2 = 3`。

因此,无论 `(right - left)` 是奇数还是偶数,`middle = left + ((right - left) >> 1)` 都能正确计算出 `middle` 索引,指向搜索区间的中间位置。这个写法同样对奇数和偶数情况适用。

4.3

右移一位 (>> 1) 是一种位操作,将一个数的二进制表示向右移动一位。

具体来说,二进制表示的每一位都向右移动一位,右移后的最低位会被舍弃,而最左边则补上 0(对于无符号整数)或保持符号位不变(对于有符号整数)。这相当于将数字除以 2 并舍弃小数部分。

对于偶数和奇数的情况,右移一位的效果如下:

  • 偶数:偶数的二进制表示最后一位是 0。右移一位后,相当于这个数字除以 2,并且结果是一个整除的结果。
    • 例如:4 的二进制表示是 100,右移一位得到 10,即 2
  • 奇数:奇数的二进制表示最后一位是 1。右移一位后,同样相当于这个数字除以 2,但结果会向下取整。
    • 例如:5 的二进制表示是 101,右移一位得到 10,即 2(相当于 5 / 2 = 2.5,取整为 2)。

总结

  • 对于 (right - left) 是偶数时,右移一位得到准确的中间索引。
  • 对于 (right - left) 是奇数时,右移一位相当于向下取整,得到一个偏左的中间索引。

这个操作在二分查找中广泛使用,目的是快速计算数组的中间位置,同时避免直接使用除法运算。

4.4 int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2\

这行代码中的 `middle = left + ((right - left) / 2)` 是一种防止溢出的中间索引计算方法,用于替代传统的 `(left + right) / 2`。

### 防止溢出

在计算数组中间位置时,最直接的方式是使用 `(left + right) / 2`,但这种方法在某些情况下可能会导致溢出问题。如果 `left` 和 `right` 都是非常大的正整数,它们相加的结果可能超过整数的最大表示范围,从而导致溢出。

### 安全的计算方式

`left + ((right - left) / 2)` 这种写法是为了避免溢出。通过先计算 `right - left`,确保中间偏移量不会超出整数范围,然后再加上 `left` 来得到中间位置。具体步骤如下:

1. 计算区间长度:`right - left`,它表示当前搜索区间的长度。
2. 计算中间偏移量:`(right - left) / 2`,它表示中间位置相对于 `left` 的偏移。
3. 加上 `left`:`left + ((right - left) / 2)` 得到中间位置 `middle`。

### 举个例子

假设 `left = 1000`,`right = 2000`:

- `(right - left)` 是 `1000`。
- `(right - left) / 2` 是 `500`。
- `left + 500` 得到 `middle = 1500`。

这种方法即使在 `left` 和 `right` 都很大时也不会出现溢出问题。相比于直接使用 `(left + right) / 2`,这种写法更安全可靠。

\

4.5(right - left)为奇数和偶数的情况

在 `right - left` 为奇数和偶数的情况下,`middle = left + ((right - left) / 2)` 的计算方式和结果会有些不同,具体如下:

### 1. `(right - left)` 为偶数的情况

当 `(right - left)` 为偶数时,`(right - left) / 2` 结果是一个整数,这个整数是搜索区间长度的一半。

**例子:**
- 假设 `left = 2`,`right = 8`,则 `right - left = 6`。
- `(right - left) / 2 = 6 / 2 = 3`。
- `middle = left + 3 = 2 + 3 = 5`。

在这种情况下,`middle` 正好位于区间的中点。

### 2. `(right - left)` 为奇数的情况

当 `(right - left)` 为奇数时,`(right - left) / 2` 的结果是一个整数,但它实际上是向下取整后的结果,即小于 `right - left` 的一半。

**例子:**
- 假设 `left = 2`,`right = 7`,则 `right - left = 5`。
- `(right - left) / 2 = 5 / 2 = 2`(整除运算时舍弃小数部分)。
- `middle = left + 2 = 2 + 2 = 4`。

在这种情况下,`middle` 位于区间偏左的位置,也就是说,它会比真正的中点稍微靠近 `left`。

### 总结

- 当 `(right - left)` 为偶数时,`middle` 正好位于区间的中间。
- 当 `(right - left)` 为奇数时,`middle` 会偏向区间的左侧,因为整除操作舍弃了小数部分。

不论是奇数还是偶数情况,这种计算方法都能确保 `middle` 的值在 `[left, right]` 范围内,并且是二分查找的有效实现。

3.移除元素

暴力解法:两个for循环,覆盖;

双指针:快慢指针(读写指针),写指针=0(初始化),快指针入for循环,慢指针写入需求数组

. - 力扣(LeetCode)

4. 有序数组的平方

错误:定义函数返回值未写到到函数体内部

这个错误通常是由于代码语法或拼写错误引起的。具体来说,expected member name or ';' after declaration specifiers 错误提示说明编译器期望在某个声明后出现成员名称或分号,但并未找到。这通常发生在以下几种情况下:

  1. 缺少分号:某个声明之后可能缺少了分号 ;
  2. 拼写错误:可能有拼写错误导致编译器无法理解代码的含义。
  3. 函数或变量定义不完整:可能函数或变量的定义不完整,导致编译器无法解析。

可能的原因与解决方法:

  1. 检查上一行:错误可能不是出现在 return nums; 这一行,而是出现在前一行或更前面的代码中。请检查上一行代码是否缺少分号或有其他语法错误。

 

你在代码中的 return nums; 语句位于 sortedSquares 函数之外,导致了语法错误。这个问题的原因是 return nums; 语句不在函数体内,编译器无法理解它是属于哪个函数的。

修正后的代码:

要解决这个问题,你需要确保 return nums;sortedSquares 函数的花括号 {} 内。以下是修正后的代码:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) 
    {
        // 对数组中的每个元素进行平方
        for(int i = 0; i < nums.size(); i++)
        {
            nums[i] *= nums[i];
        }
        // 对平方后的数组进行排序
        sort(nums.begin(), nums.end());

        // 返回排序后的数组
        return nums;
    }
};

解释:

  1. 函数体的完整性

    • return nums; 必须在 sortedSquares 函数体内,因为它是函数的返回值。修正后的代码中,return nums; 已经正确地位于函数的最后一行。
  2. 返回类型

    • 这个函数的返回类型是 vector<int>,因此它必须返回一个 vector<int> 类型的值。在修正后的代码中,return nums; 返回了经过平方和排序处理的 nums 数组。
  3. 操作顺序

    • 先对数组中的每个元素进行平方操作,然后使用 sort 函数对平方后的数组进行升序排序。

这个修正后的代码可以正确地完成对输入数组的平方和排序操作,并返回处理后的数组。

 6.螺旋矩阵

我们详细解释一下这行代码的语法和参数定义规则:

vector<vector<int>> res(n, vector<int>(n, 0));

1. vector<vector<int>>:二维向量的声明

  • vector 是 C++ 标准库提供的动态数组,可以根据需要自动调整大小。
  • vector<int> 表示一个存储 int 类型元素的一维向量。
  • vector<vector<int>> 表示一个存储 vector<int> 类型元素的二维向量,实际上可以看作是一个“向量的向量”,即二维数组。

2. res:二维向量的变量名

  • res 是声明的这个二维向量的变量名,它用于存储最终生成的矩阵。

3. res(n, vector<int>(n, 0)):二维向量的初始化

这部分代码用来初始化二维向量 res,将其构建为 nn 列的矩阵。

3.1 n(外层 vector 的大小)
  • 外层的 vector(即 res)有 n 个元素,对应于矩阵中的 n 行。
3.2 vector<int>(n, 0)(内层 vector 的初始化)
  • vector<int>(n, 0) 是内层向量的构造函数,表示生成一个大小为 n 的一维向量,并将其中的每个元素都初始化为 0
  • n 表示内层 vector<int> 的大小,即每一行的元素个数(列数)。
  • 0 是内层 vector 的初始值,也就是说每一行的所有元素初始值都是 0
3.3 res(n, vector<int>(n, 0)) 组合起来的含义
  • res(n, vector<int>(n, 0)) 表示创建一个 n x n 的二维向量矩阵,其中每个元素初始化为 0
举个例子:

arr[][] ={ [1, 0, 0],[0, 0, 0],[0, 0, 0]}是3x3矩阵

这个矩阵由三个一维数组(行)组成,每个数组包含三个元素(列)。矩阵的行和列数相等,因此称为 3x3 矩阵。

具体来说,矩阵的表示如下

1  0  0
0  0  0
0  0  0

如果我们用代码表示这个矩阵,可以这样写: 

int arr[3][3] = {
    {1, 0, 0},
    {0, 0, 0},
    {0, 0, 0}
};

同理 

3.3 res(n, vector<int>(n, 0)) 组合起来的含义
  • res(n, vector<int>(n, 0)) 表示创建一个 n x n 的二维向量矩阵,其中每个元素初始化为 0

总结

这一行代码的整体功能是创建了一个 nn 列的二维矩阵,并且矩阵中的所有元素都被初始化为 0。每一行是一个一维向量(vector<int>),整个矩阵是一个包含这些一维向量的二维向量(vector<vector<int>>)。

哈希表

思路:

1.当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法

但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

2. 去重就要想到set

此时就要使用另一种结构体了,set ,关于set,C++ 给提供了如下三种可用的数据结构:

  • std::set
  • std::multiset  (可以重复)
  • std::unordered_set(自动去重)

std::set和std::multiset底层实现都是红黑树,

std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。

3.349. 两个数组的交集

https://leetcode.cn/problems/intersection-of-two-arrays/

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result;
        unordered_set<int> num_set(nums1.begin(),nums1.end());//自动去重

        for(int i = 0;i<nums2.size();i++)
        {
            if(num_set.find(nums2[i]) != num_set.end())//如果在nums_set中找到了nums[i]
            {
                result.insert(nums2[i]);
            }
        }    
        // nums1.insert(nums1.end(),nums2.begin(),nums2.end());
        // return result;//错误
        return vector<int>(result.begin(),result.end());//这行代码将 unordered_set 中的结果转换为一个 vector 并返回。unordered_set 是无序的,所以返回的 vector 可能是无序的。

    }
};

 语法讲解:

if(num_set.find(nums2[i]) != num_set.end())

2. num_set.find(nums2[i])

  • num_set 是一个 unordered_set<int> 类型的集合。
  • .find()unordered_set 的一个成员函数,它的作用是查找集合中是否存在某个元素。
  • nums2[i]nums2 数组的第 i 个元素。

num_set.find(nums2[i]) 的意思是,在 num_set 集合中查找是否存在 nums2[i] 这个元素。

  • 如果找到了这个元素,find() 函数会返回一个指向该元素的迭代器(类似于指针,指向元素在集合中的位置)。
  • 如果没有找到这个元素,find() 函数会返回一个特殊的迭代器,指向集合的末尾,这个末尾是用 num_set.end() 表示的

这句代码的意思是:

  • 先查找 nums2[i] 这个元素是否在 num_set 中。
  • 如果找到了(即 find 返回的迭代器不是 num_set.end()),则条件为 trueif 语句块内的代码会被执行。
  • 如果没找到(即 find 返回的迭代器是 num_set.end()),则条件为 falseif 语句块内的代码不会被执行。

错误:

vector<int> result_vec(result.begin(),result.end());

 出现问题的原因是因为你在代码中声明了一个新的 vector<int> result,而 result 这个名字已经在之前被定义为一个 unordered_set<int> 类型的变量。这会导致编译器无法区分这两个 result,进而产生错误。

这里的 result 被多次使用,导致名称冲突。为了解决这个问题,你可以使用不同的变量名来避免冲突。例如,可以将 unordered_set<int> result 改为 unordered_set<int> result_set,然后用 result_set 初始化新的 vector<int>

vector<int> result(result_set.begin(), result_set.end()); // 使用 result_set 进行初始化
sort(result.begin(), result.end());
return result;

注意和这个区别

return vector<int>(result. Begin(), result. end());

 这行代码将 unordered_set 中的结果转换为一个 vector 并返回。unordered_set 是无序的,所以返回的 vector 可能是无序的。

!!!不需要重新定义变量名

3.第202题. 快乐数

202. 快乐数 - 力扣(LeetCode)

class Solution {

public:

        //先取余,算平方和;n再/取整,sum加起来,想法从个位往更高级去取

        //条件是n>0(正整数)

        //徐定义sum n

    int getsum(int n)

    {

        int sum = 0;

        while(n>0)

        {

            sum+=(n%10)*(n%10);

            // sum+=(n%10)^2; //这个不行,拓展答疑

            n = n/10;

        }

        return sum;        

    }



    //unordered_set一个哈希表,或矩阵向量,存储暂时性的sum不为1的数

        // 如果sum==1 返回true

        // 否则再set里寻找sum,如果没有相同==没有陷入死循环==》insert进去

        //如果有相同,false

    bool isHappy(int n)

    {

        // unordered_set vector<int> result; //拓展答疑语法错误

        unordered_set<int> result;

        int sum = getsum(n);

        while(1)//拓展答疑

        {  

            if(sum==1)

            {

                return true;

            }

            else if(result.find(sum) != result.end())

            {

                return false;

            }

            else{

                result.insert(sum);

            }

            sum = getsum(sum);

        }



    }

};

/* 拓展答疑while(1)

while(1) 是一个无限循环的写法。!!!

    !!!!

    在 C++ 中,while 循环的条件部分要求是一个布尔表达式,

如果表达式为 true,则循环会继续执行;如果为 false,则循环会终止。

为什么使用无限循环?

在这个 isHappy 函数中,使用 while(1) 是为了让代码不断计算数字的平方和,直到满足特定的条件后才跳出循环并返回结果。这些特定的条件包括:

sum == 1:一旦计算出的平方和等于 1,函数就会返回 true,因为 n 是一个快乐数。

result.find(sum) != result.end():如果在 unordered_set 中找到了这个平方和,说明已经陷入了循环,函数返回 false,表示 n 不是一个快乐数。

由于循环条件 while(1) 保证了循环会一直运行,直到遇到上述的返回条件,这种写法可以确保所有可能的情况都被正确处理。

使用无限循环的优势

简洁:通过 while(1),你不需要在循环条件中判断具体的退出条件,只要在合适的时候用 return 语句退出即可。

控制灵活:你可以在循环内部根据不同的条件随时退出循环,而不需要在循环条件中进行复杂的判断。

退出无限循环

在这段代码中,退出无限循环的方式是使用 return 语句:

当 sum == 1 时,直接 return true; 退出循环,表示这是一个快乐数。

当 result.find(sum) != result.end() 时,直接 return false; 退出循环,表示不是一个快乐数。

这种写法有效避免了不必要的条件检查,使得代码更加简洁高效。

*/





 

/* 拓展答疑sum+=(n%10)^2

在 C++ 中,(n % 10) * (n % 10) 表示计算 n 的最后一位数字的平方。你提到的 ^ 运算符其实是按位异或(bitwise XOR)运算符,而不是用于数学上的幂运算。因此,(n % 10)^2 不能正确地表示计算平方。

具体解释:

^ 运算符:在 C++ 中,^ 是按位异或运算符,它对两个整数的每一位进行异或操作。如果这两个整数在同一位上有相同的二进制位(即都是 0 或都是 1),结果为 0;如果不同,结果为 1。

示例:

假设 n % 10 的结果是 3,那么 (n % 10)^2 实际上计算的是 3^2,按位异或运算的结果不是 9(即 3 * 3),而是 1(因为 3 的二进制表示是 11,2 的二进制表示是 10,11 XOR 10 = 01,结果是 1)。

正确的平方计算:

正确的平方计算应该用乘法运算符 *,例如 (n % 10) * (n % 10),这表示将 n 的最后一位数字的平方相乘。

因此,如果你想计算 n 的最后一位数字的平方,应该使用乘法运算符 *,而不是按位异或运算符 ^。

*/



 

/*拓展答疑:unordered_set vector<int> result;

语法错误和类型定义不正确。

具体问题!!!

    缺少模板参数:unordered_set 是一个模板类,需要指定其中存储的元素类型。你在 unordered_set 后面没有加上模板参数,应该是 unordered_set<int>,表示存储整数的哈希集合。

    混合了不同的数据结构:unordered_set 是一个集合类型,而 vector<int> 是一个动态数组类型。你不能将它们直接混合在一起。这里的 vector<int> 作为类型应该独立存在,而不是作为 unordered_set 的模板参数。

正确的写法!!!

如果你想声明一个存储 int 类型的 unordered_set,应该这样写:

cpp

复制代码

unordered_set<int> result;

这段代码声明了一个名为 result 的哈希集合,其中存储的是整数。

如果你还需要一个 vector<int>,可以单独声明它:

cpp

复制代码

vector<int> vec;

综合示例

如果你需要同时使用 unordered_set<int> 和 vector<int>,可以这样写:

cpp

复制代码

unordered_set<int> result; // 用于存储整数的集合

vector<int> vec; // 用于存储整数的向量

这两者是独立的容器,分别用于不同的目的。

总结来说,unordered_set<int> 和 vector<int> 是两种不同的数据结构,不能直接混合使用,必须分别声明,并各自具有自己的模板参数和数据类型

*/

1. 两数之和 - 力扣(LeetCode)

3.map的使用
class Solution{
    public:
    vector<int> twoSum(vector<int>& nums, int target)
    {
        //unordered_map<int> result用来存储,键值对,
        //搜索的时候查找有没有target-nums[i],没有把该键值对加入,有就返回两个下表
        //定义 map  iter = result.find(tarfet-nums[i])
        //return {iter->second,i}
        unordered_map<int,int> result;//定义拓展答疑

        for(int i = 0; i<nums.size(); i++)
        {
            int m = target - nums[i];
            // int iter = result.find(m);
            auto iter = result.find(m); //拓展答疑auto
            
            if(iter != result.end())
            {
                return {iter->second,i}; //拓展答疑
            }
            else
            {
                result.insert(pair<int, int>(nums[i],i));//pair拓展答疑
            }
        }
        return {};
    }
};

1.为什么是auto int不对

在 C++ 中,auto 关键字用于自动推断变量的类型。它让编译器根据初始化表达式的类型自动确定变量的类型。

在你提到的这行代码中:

auto iter = result.find(m);

使用 auto 是正确的,因为 result.find(m) 返回的是一个迭代器,auto 可以自动推断出这个迭代器的类型。这个迭代器类型比较复杂,所以使用 auto 可以简化代码,不必手动写出具体的类型名称。

1. 为什么不能用 int

如果你写成:

int iter = result.find(m);

这是错误的,因为 result.find(m) 返回的类型并不是 intfind() 方法返回的是一个迭代器,而不是整数。

  • unordered_map<int, int>::iterator 类型result.find(m) 的返回值是一个 unordered_map<int, int>::iterator 类型的迭代器,用于指向哈希表中的某个元素。这个迭代器类型很长,所以用 auto 来简化代码。

auto 的优势

  • 简化代码:对于复杂类型,auto 可以让代码更加简洁和易读。
  • 减少错误:手动指定复杂的类型容易出错,auto 能减少这种错误。

结论

auto 是正确且推荐的用法,而使用 int 是不对的,因为类型不匹配,无法编译通过。

2.pair 为什么可以直接用,不用提前申明定义

在 C++ 中,pair 是一个标准模板类,用于存储一对值。你可以直接使用 pair,而不需要提前声明或定义它,因为它已经在 C++ 标准库中定义好了。

为什么可以直接使用 pair

  • 标准模板库(STL)的一部分pair 是 C++ 标准模板库(STL)的一部分,已经被标准库实现并提供给开发者使用。只要你包含了对应的头文件,你就可以直接使用 pair 以及它的相关功能。

  • 自动推断类型:C++11 及以后版本支持通过 auto 关键字和 make_pair 函数自动推断 pair 的类型,避免了繁琐的类型声明。

示例代码

在使用 pair 时,通常包含 <utility> 头文件,这是 pair 定义所在的头文件:

#include <utility> // 包含 pair 所在的头文件

pair<int, int> p(1, 2); // 定义一个 int 类型的 pair

使用 pair 的场景

  1. 直接使用

    result.insert(pair<int, int>(nums[i], i));

    这里直接使用 pair,它存储两个 int 类型的值,分别是 nums[i]i

  2. 使用 make_pair(C++11 引入):

    result.insert(make_pair(nums[i], i));

    make_pair 函数可以自动推断类型,使代码更简洁。

  3. 自动类型推断: 在使用 C++11 及以上版本时,你可以直接使用 auto 来声明一个 pair,让编译器推断类型:

    auto p = make_pair(1, 2);

总结

  • 无需提前声明:因为 pair 是 C++ 标准库的一部分,已经在 <utility> 头文件中定义好了,可以直接使用。
  • 自动推断:使用 automake_pair 可以进一步简化代码,不需要手动指定类型。

只要包含了相应的头文件(如 <utility>),你就可以直接在代码中使用 pair,无需额外的声明或定义。

3. iter->second  这是什么?是m的键值对所在位置的第二位吗

是的,iter->second 指的是在哈希表 unordered_map 中,键值对中对应 m 的那个键值对的 "值"。

具体解释

在 C++ 中,unordered_map 存储的是键值对(key-value pairs),每个元素由一个键(key)和一个对应的值(value)组成。每个键值对在哈希表中的存储形式可以表示为:

pair<const Key, T>

其中:

  • Key 是键的类型,在你的例子中是 int
  • T 是值的类型,在你的例子中也是 int

iter->second 的含义

假设你有一个 unordered_map<int, int> 类型的哈希表,并使用 find() 函数查找某个键 m,返回一个迭代器 iter

  • iter->first:指向的是该迭代器所在位置的键,即 m
  • iter->second:指向的是该迭代器所在位置的值,即与键 m 相关联的值。

在你的 twoSum 代码中:

if(iter != result.end()) { return {iter->second, i}; }

这里的 iter->second 获取的是与键 m 相关联的索引值(即在 result 中存储的某个之前遍历过的数字的索引)。i 是当前正在遍历的索引。

总结

  • iter->second 是哈希表中键值对中的 "值",它与 m(即 target - nums[i])这个键相关联。
  • twoSum 问题中,iter->second 是之前存储的某个索引,表示数组中第一个符合条件的数的位置,i 是当前数的位置。最终返回这两个索引。 

 

 3    15. 三数之和 - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //三数和,同一个数组,左右指针,一层for
        //定义矩阵,

        vector<vector<int>> result;
        // vector<vector<int>> sum;
        sort(nums.begin(),nums.end());
        for(int i = 0; i<nums.size(); i++)
        {
            
            int left = i+1;
            int right = nums.size()-1;

            if(nums[i] > 0)
            {
                return result;
            }
            //去重的操作
            if(i>0 && nums[i] == nums[i-1]){
                continue;
            }
            
            while(left < right)
            {

                if(nums[i]+nums[left]+nums[right] < 0){
                    left++;
                }
                else if(nums[i]+nums[left]+nums[right] > 0){
                    right--;
                }

                else if(nums[i]+nums[left]+nums[right] == 0){

                    /*********************一******************************/
                    // result.push_back(vector<int>{nums[i],nums[left],nums[right]});


                    /**********************二*******************************/
                    // vector<int> sum = {nums[i],nums[left],nums[right]};
                    // result.push_back(sum);


                    /***********************三*****************************/
                    // vector<int> sum;
                    // sum.insert(sum.end(),nums[i]);
                    // sum.insert(sum.end(),nums[left]);
                    // sum.insert(sum.end(),nums[right]);
                    // result.push_back(sum);
                    
                    /***********************四*****************************/
                    vector<int> sum;
                    sum.push_back(nums[i]);   // 将 nums[i] 插入到 sum 的末尾
                    sum.push_back(nums[left]);   // 将 nums[left] 插入到 sum 的末尾
                    sum.push_back(nums[right]);  // 将 nums[right] 插入到 sum 的末尾
                    result.push_back(sum); // 将 sum 作为一个整体添加到 result 中

                    /***********************错误*****************************/
                    // vector<int> sum;
                    // sum.insert(nums[i]);
                    // sum.insert(nums[left]);
                    // sum.insert(nums[right]);
                    // result.push_back(sum);

                    /*错误
                    // result.insert(result.end(),sum.begin(),sum.end());//错误
                    // sum.push_back(sum.end(),nums[i]); //错误
                    // sum.insert(nums[left]);
                    // sum.insert(nums[right]);
                    // result.insert(sum);
                    */
                    left++;
                    right--;//没有双指针收缩操作,会超出时间限制
                }
            }
        }
        return result;
        
    }
};

push_back 和 insert的区别详解

push_backinsert 是 C++ 中 vector 类的两个常用方法,用于向 vector 中添加元素。它们的使用方式和作用略有不同。

1. push_back

push_back 用于在 vector 的末尾添加元素。它是一个简单的操作,只需将一个元素追加到 vector 的末尾。

用法

vector<int> vec;
vec.push_back(10); // 将10添加到vec的末尾
vec.push_back(20); // 将20添加到vec的末尾

特点

  • 只在 vector 的末尾添加元素。
  • 适合用于需要动态扩展 vector 的情况。
  • 时间复杂度为均摊 O(1)。

2. insert

insert 用于在 vector 的指定位置插入一个或多个元素。它可以在 vector 的任意位置插入元素,不局限于末尾。

用法:

vector<int> vec = {10, 20, 30};
vec.insert(vec.begin() + 1, 15); // 在vec的第二个位置插入15,vec变为{10, 15, 20, 30}

vector<int> vec2 = {1, 2, 3};
vec.insert(vec.end(), vec2.begin(), vec2.end()); // 在vec的末尾插入vec2的所有元素

特点

  • 可以在 vector 的任意位置插入元素。
  • 可以插入一个元素或一个范围内的多个元素。
  • 插入操作可能涉及到元素的移动,因此时间复杂度为 O(n),其中 nvector 中的元素数量。
总结对比
  • 用法范围

    • push_back 只能在末尾添加单个元素。
    • insert 可以在任意位置插入单个或多个元素。
  • 效率

    • push_back 更高效,特别是当 vector 需要频繁添加元素时。
    • insert 灵活性更高,但效率较低,尤其是在 vector 的中间插入时。
  • 使用场景

    • 当只需要向 vector 添加元素到末尾时,使用 push_back
    • 当需要在 vector 中间插入元素,或者插入多个元素时,使用 insert

错误

 

 insert(sum)在undered_set中可以直接用,其他地方vector<int>不可以,必须有位置和元素
 vector<int>不可以
 /***********************错误*****************************/
                    // vector<int> sum;
                    // sum.insert(nums[i]);
                    // sum.insert(nums[left]);
                    // sum.insert(nums[right]);
                    // result.push_back(sum);

你的意图是将 nums[i], nums[left], 和 nums[right] 依次插入到 sum 中,然后将 sum 添加到 result 中。然而,sum.insert() 的使用方式在这里是错误的。这个是在一个

insert 方法的正确用法

  • insert 方法用于在 vector 的指定位置插入元素或范围。
  • 正确的 insert 用法需要两个参数:插入的位置和要插入的元素。

在这种情况下,应该使用 push_back 而不是 insert

push_back 是用于在 vector 的末尾添加一个元素的简洁方法。

修正后的代码

你可以将代码修改如下:

vector<int> sum; // 初始化一个空的 vector<int>
sum.push_back(nums[i]);    // 插入 nums[i]
sum.push_back(nums[left]); // 插入 nums[left]
sum.push_back(nums[right]); // 插入 nums[right]
result.push_back(sum); // 将 sum 作为一个整体添加到 result 中

解释

  • sum.push_back(nums[i]);: 将 nums[i] 添加到 sum 的末尾。
  • sum.push_back(nums[left]);: 将 nums[left] 添加到 sum 的末尾。
  • sum.push_back(nums[right]);: 将 nums[right] 添加到 sum 的末尾。
  • result.push_back(sum);: 将 sum 整体添加到 result 中。

总结

对于你的需求,push_back 更适合。insert 一般用于在特定位置插入元素,或者插入一个范围内的多个元素。而在这种场景下,使用 push_back 更直观和简洁。

undered_set中可以直接用
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果,之所以用set是为了给结果集去重
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        for (int num : nums2) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

//这个里面为什么可以直接result_set.insert(num);

在这段代码中,result_set.insert(num); 是一个有效的操作,因为 result_set 是一个 unordered_set<int> 类型的集合,insert 方法是 unordered_set 的成员函数。

unordered_setinsert 方法

  • 功能insert 方法将一个元素插入到 unordered_set 中。
  • 特性unordered_set 是一个不允许重复元素的数据结构。如果你试图插入一个已经存在的元素,unordered_set 会自动忽略这个重复的插入操作,不会存储重复的值。

为什么可以直接使用 insert?

  1. 数据类型匹配result_set 是一个 unordered_set<int> 类型,而 num 是一个 int 类型,数据类型匹配,因此可以直接插入。

  2. 去重功能unordered_set 自动处理重复元素。当 num 插入 result_set 时,如果该元素已经存在,unordered_set 会自动忽略这次插入。这就是为什么你可以直接使用 insert 方法,而不用担心会有重复的元素。

result.push_back(vector<int>{nums[i],nums[left],nums[right]});

使用 result.push_back(vector<int>{nums[i], nums[left], nums[right]}); 是一种更简洁且常用的方法来将三个元素 nums[i]nums[left]nums[right] 组合成一个 vector<int>,并直接插入到 result 中。这个语法是完全正确的,并且比之前的方式更加简洁明了。

解释

  • vector<int>{nums[i], nums[left], nums[right]}

    • 这是一个临时的 vector<int> 对象,包含三个元素:nums[i]nums[left]nums[right]
    • {} 初始化列表被用来直接构造这个临时对象。
  • result.push_back(...)

    • push_back 方法会将上述临时构造的 vector<int> 对象插入到 result 中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值