c++里面stl的一些值得注意的知识

一、next_permutation()

next_permutation() 是 C++ 标准库中的一个函数,用于生成给定序列的下一个排列。如果当前排列是该序列的最后一个排列,next_permutation() 会将序列重新排列成第一个排列。

函数原型

#include <algorithm>

bool next_permutation(BidirectionalIterator first, BidirectionalIterator last);
bool next_permutation(BidirectionalIterator first, BidirectionalIterator last, Compare comp);

 

  • first 和 last 是指向容器开始和结束的迭代器。
  • comp 是一个可选的比较函数,用于自定义排列的顺序。如果未提供,默认使用 < 操作符。

功能

  • 返回值:如果生成了下一个排列,函数返回 true;如果当前排列是最后一个排列,函数返回 false 并将序列重置为第一个排列。

使用示例

假设我们有一个容器 std::vector<int>,并希望使用 next_permutation 来生成所有可能的排列。

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 打印所有排列
    do {
        for (int x : vec) std::cout << x << ' ';
        std::cout << '\n';
    } while (std::next_permutation(vec.begin(), vec.end()));

    return 0;
}

 

代码解释
  1. 初始化容器:我们定义了一个 std::vector<int> 并初始化为 {1, 2, 3}
  2. 循环排列:使用 do-while 循环打印当前排列,并调用 std::next_permutation 生成下一个排列。
  3. 结束条件:当 std::next_permutation 返回 false 时,循环结束,表示已经生成了所有可能的排列。

使用自定义比较函数

next_permutation 也可以接受一个自定义比较函数,用于定义排列的顺序。例如,我们可以使用自定义的比较函数来生成降序排列。

#include <iostream>
#include <vector>
#include <algorithm>

// 自定义比较函数:大于
bool customCompare(int a, int b) {
    return a > b;
}

int main() {
    std::vector<int> vec = {1, 2, 3};

    // 打印所有降序排列
    do {
        for (int x : vec) std::cout << x << ' ';
        std::cout << '\n';
    } while (std::next_permutation(vec.begin(), vec.end(), customCompare));

    return 0;
}
代码解释
  1. 自定义比较函数customCompare 函数定义了一个大于比较(降序排列)。
  2. 使用自定义比较函数std::next_permutation 使用 customCompare 来生成排列。

总结

  • next_permutation() 用于生成序列的下一个排列。
  • 默认行为:生成升序排列的下一个排列。
  • 自定义行为:可以通过提供比较函数来生成不同顺序的排列。
  • 返回值true 表示成功生成了下一个排列,false 表示当前排列已经是最后一个排列。

这个函数非常有用,可以用于需要生成所有排列或特定排列顺序的场景,例如排列组合、算法优化等。

二、lowbit(x) = x & -x

lowbit(x) = x & -x 是一种计算整数 x 的“最低有效位” (least significant bit) 的技巧,这个技巧在很多算法中非常有用,特别是在处理二进制表示和位操作时。下面详细解释这句代码的含义和用途。

1. 代码解释

lowbit(x) = x & -x 这个表达式利用了位运算来计算 x 的最低有效位。让我们逐步解释:

  • x:一个整数,通常以二进制形式表示。
  • -x:是 x 的负数。在计算机中,负数的二进制表示是其正数的二进制表示取反再加 1(即,二进制补码)。例如,如果 x 的二进制表示是 00001000(即十进制的 8),那么 -x 的二进制表示将是 11111000(即二进制的 -8)。

例子: 假设 x = 12,其二进制表示为 00001100

  • -x 的二进制表示是 11110100(取反加 1)。
  • x & -x 是 00001100 & 11110100,结果是 00000100,这是 x 的最低有效位的值。

2. 用途

计算最低有效位的值

lowbit(x) 的结果是 x 二进制表示中最低的 1 所对应的值。例如:

  • 对于 x = 12 (00001100),lowbit(x) = 4 (00000100),因为 12 的最低有效位是 4。
  • 对于 x = 10 (00001010),lowbit(x) = 2 (00000010),因为 10 的最低有效位是 2。
在算法中的应用
  1. 位操作优化

    • 在一些算法中,特别是需要操作集合、更新数据结构或在树状结构中进行操作时,lowbit 可以帮助快速找到和操作数据。例如,Binary Indexed Tree(树状数组)中,经常使用 lowbit 来更新或查询数据。
  2. 生成子集

    • 在生成所有子集的算法中,lowbit 可以用来快速确定当前元素的最低有效位,从而生成所有可能的子集。

3. 代码示例

以下是一个简单的 C++ 代码示例,展示了如何使用 lowbit

 

#include <iostream>

int lowbit(int x) {
    return x & -x;
}

int main() {
    int x = 12; // 二进制表示为 00001100
    std::cout << "lowbit(" << x << ") = " << lowbit(x) << std::endl;
    // 输出: lowbit(12) = 4
    return 0;
}

 

4. 实际应用示例

例子 1:更新和查询 Binary Indexed Tree(树状数组) 

假设你在实现一个 Binary Indexed Tree(BIT)来进行快速前缀和查询和更新操作:

 

#include <iostream>
#include <vector>

class BIT {
public:
    BIT(int size) : tree(size + 1, 0), n(size) {}

    void update(int index, int value) {
        while (index <= n) {
            tree[index] += value;
            index += lowbit(index);
        }
    }

    int query(int index) {
        int sum = 0;
        while (index > 0) {
            sum += tree[index];
            index -= lowbit(index);
        }
        return sum;
    }

private:
    std::vector<int> tree;
    int n;

    int lowbit(int x) {
        return x & -x;
    }
};

int main() {
    BIT bit(10);
    bit.update(1, 10);
    bit.update(2, 20);
    std::cout << "Sum of first 2 elements: " << bit.query(2) << std::endl;
    return 0;
}

 

在这个例子中,lowbit 用于在更新和查询操作中计算索引的增量。

通过理解 lowbit 的定义和应用,你可以在各种需要二进制位操作的算法中利用这个技巧来优化你的代码。

三、unique(a.begin(), a.end())

unique(a.begin(), a.end()) 函数返回的是已经去重后的序列的最后一个有效元素的下一个位置的迭代器。它的作用是将相邻的重复元素移到序列的末尾,并返回一个指向去重后序列末尾的迭代器。

具体来说:

  1. 去重过程unique 函数将相邻的重复元素移动到序列的末尾,并返回一个指向去重后的序列末尾的迭代器。注意,这个迭代器指向的是最后一个有效元素的下一个位置,即去重后序列的结束位置。

  2. 后续操作:你可以用这个迭代器来调整容器的大小,去掉不再需要的部分。例如,使用 container.erase(new_end, container.end()) 来清除末尾的多余元素。

例如,考虑以下代码:

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> a = {1, 1, 2, 2, 3, 3, 3, 4};
    auto new_end = std::unique(a.begin(), a.end());
    a.erase(new_end, a.end());

    for (int i : a) {
        std::cout << i << " ";
    }
    return 0;
}

解释

  • std::unique(a.begin(), a.end()) 将序列变成 {1, 2, 3, 4, 3, 3, 3, 4},并返回一个迭代器 new_end,它指向 4 后的位置。
  • a.erase(new_end, a.end()) 删除了从 new_end 到 a.end() 的所有元素,最终容器 a 只保留了 {1, 2, 3, 4}

所以,unique 返回的迭代器是指向去重后序列最后一个有效元素的下一个位置。

 

 四、a.erase(new_end, a.end());

erase 是 C++ STL 容器(如 std::vectorstd::list)的成员函数,用于删除容器中的元素。它的用法有两个主要变体:

  1. 删除单个元素container.erase(iterator)

    • 作用:删除指定位置的单个元素。
    • 参数:一个指向要删除元素的迭代器。
    • 返回值:指向被删除元素之后位置的迭代器。
  2. 删除范围内的元素container.erase(first, last)

    • 作用:删除从 first 迭代器到 last 迭代器之前的位置的所有元素(不包括 last)。
    • 参数:两个迭代器,first 是要开始删除的第一个元素的位置,last 是删除范围的末尾迭代器(不包括这个位置)。
    • 返回值:指向 last 迭代器位置的元素之后的位置的迭代器。

示例解释

考虑如下代码:

#include <vector>
#include <algorithm>
#include <iostream>

int main() {
    std::vector<int> a = {1, 1, 2, 2, 3, 3, 3, 4};
    auto new_end = std::unique(a.begin(), a.end());
    a.erase(new_end, a.end());

    for (int i : a) {
        std::cout << i << " ";
    }
    return 0;
}

 

  1. std::unique:将 a 变成 {1, 2, 3, 4, 3, 3, 3, 4},并返回指向序列中 4 后的位置的迭代器 new_end

  2. a.erase(new_end, a.end()):删除从 new_enda.end() 之间的所有元素。

    • new_end 是指向 4 后的位置的迭代器。
    • a.end() 是容器的末尾迭代器。

    所以,这行代码删除了 {3, 3, 3, 4} 部分,最终容器 a 变成 {1, 2, 3, 4}

总结erase(first, last) 通过指定范围来删除容器中的多个元素,非常有用来清理不再需要的部分。

五、count

在哈希表(如 C++ 中的 std::unordered_mapstd::unordered_set)中,count 方法用于确定某个元素的出现次数。哈希表的实现可以区分不同类型的数据结构,因此它们的行为在处理重复元素方面有所不同。

1. std::unordered_map

std::unordered_map 是一个基于哈希表的关联容器,存储键值对(key-value)。在这种结构中,键(key)是唯一的,但每个键对应的值(value)可以是任意的。哈希表不会允许重复的键。

  • count 方法
    • 对于 std::unordered_mapcount 方法通常用于检查某个键的出现次数。由于每个键在哈希表中是唯一的,count 方法的结果要么是 0(键不存在),要么是 1(键存在)。
示例代码
#include <iostream>
#include <unordered_map>

int main() {
    std::unordered_map<int, std::string> umap;
    umap[1] = "one";
    umap[2] = "two";
    umap[3] = "three";

    // 查询键 2 的出现次数
    std::cout << "Count of key 2: " << umap.count(2) << std::endl; // 输出 1

    // 查询不存在的键 4 的出现次数
    std::cout << "Count of key 4: " << umap.count(4) << std::endl; // 输出 0

    return 0;
}

 

2. std::unordered_set

std::unordered_set 是一个只存储键的哈希表容器,不存储与键关联的值。键在哈希表中必须唯一。

  • count 方法
    • 对于 std::unordered_setcount 方法用于检查某个元素是否存在于集合中。由于集合中的元素必须唯一,count 方法的结果要么是 0(元素不存在),要么是 1(元素存在)。
示例代码
#include <iostream>
#include <unordered_set>

int main() {
    std::unordered_set<int> uset = {1, 2, 3, 4, 5};

    // 查询元素 3 的出现次数
    std::cout << "Count of element 3: " << uset.count(3) << std::endl; // 输出 1

    // 查询不存在的元素 6 的出现次数
    std::cout << "Count of element 6: " << uset.count(6) << std::endl; // 输出 0

    return 0;
}

 

3、std::vector 的 count 方法

std::vector 是一个动态数组,允许存储重复的元素。在 std::vector 中,元素的计数通常需要手动实现,因为标准库的 vector 没有直接提供 count 方法。你可以通过算法库中的函数来计算元素的出现次数,例如 std::count

示例代码
#include <iostream>
#include <vector>
#include <algorithm> // for std::count

int main() {
    std::vector<int> vec = {1, 2, 2, 3, 4, 2, 5};

    // 计算元素 2 的出现次数
    int count = std::count(vec.begin(), vec.end(), 2);
    std::cout << "Count of element 2: " << count << std::endl; // 输出 3

    // 计算不存在的元素 6 的出现次数
    count = std::count(vec.begin(), vec.end(), 6);
    std::cout << "Count of element 6: " << count << std::endl; // 输出 0

    return 0;
}

 

总结

  • std::unordered_map 中的 count 方法检查的是键的存在性,每个键在哈希表中只能出现一次,因此 count 的结果要么是 0(键不存在),要么是 1(键存在)。
  • std::unordered_set 中的 count 方法检查的是元素的存在性,每个元素在集合中只能出现一次,因此 count 的结果要么是 0(元素不存在),要么是 1(元素存在)。
  • 在 std::vector 中,你需要使用算法函数(如 std::count)来统计元素的出现次数,因为 vector 允许重复元素。

因此,这些容器的 count 方法用法和 std::vector 的处理方式不同,主要体现在它们是否允许重复元素以及如何查询这些元素的存在性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值