c.unique()
在C++中,std::list是一个双向链表容器,它提供了许多成员函数来操作链表。其中,unique是std::list的一个成员函数,用于删除链表中的重复元素。
unique函数的用法很简单,它接受一个可选的二元谓词(binary predicate)作为参数,用于定义如何比较两个元素是否相等。如果未提供二元谓词,则默认使用std::equal_to来比较元素。
以下是unique函数的基本用法:
#include <iostream>
#include <list>
#include <algorithm>
int main() {
std::list<int> mylist = {1, 2, 2, 3, 4, 4, 5};
// 删除重复元素
mylist.unique();
// 输出链表中的元素
for (int x : mylist) {
std::cout << x << ' ';
}
return 0;
}
在这个例子中,mylist是一个包含重复元素的链表。调用mylist.unique()后,链表中的重复元素将被删除,只保留一个。最后,使用范围for循环遍历链表并输出其中的元素,输出结果为1 2 3 4 5。
如果需要自定义比较两个元素是否相等的方式,可以提供一个二元谓词作为unique函数的参数。例如,以下代码演示了如何删除链表中所有相邻且差值为1的重复元素:
#include <iostream>
#include <list>
#include <algorithm>
bool is_adjacent_duplicate(int a, int b) {
return std::abs(a - b) == 1;
}
int main() {
std::list<int> mylist = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 删除相邻且差值为1的重复元素
mylist.unique(is_adjacent_duplicate);
// 输出链表中的元素
for (int x : mylist) {
std::cout << x << ' ';
}
return 0;
}
在这个例子中,我们定义了一个名为is_adjacent_duplicate的二元谓词,它检查两个元素之间的差值是否为1。然后,我们将这个谓词作为参数传递给unique函数,以便删除所有相邻且差值为1的重复元素。最后,输出链表中的元素,结果为1 3 5 7 9。
c.splice()
std::list::splice 是 C++ 标准库中的 std::list 容器的一个成员函数,用于重新组织列表中的元素。它可以将一个列表中的元素移动到另一个列表的指定位置,或者合并两个列表。
以下是 std::list::splice 的函数声明:
void splice(const_iterator position, list& x);
void splice(const_iterator position, list&& x) noexcept; // (C++11 起)
void splice(const_iterator position, list& x, const_iterator i);
void splice(const_iterator position, list& x, const_iterator first, const_iterator last);
详细用法
splice(const_iterator position, list& x)
将列表 x 中的所有元素移动到调用列表的 position 位置之前。x 列表在移动后变为空。
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5, 6};
list1.splice(list1.begin(), list2); // list2 中的元素移动到 list1 开头
// list1 现在包含 {4, 5, 6, 1, 2, 3}
// list2 现在为空
splice(const_iterator position, list&& x) noexcept (C++11 起)
将移动列表 x 中的所有元素到调用列表的 position 位置之前,并将 x 置于有效但未指定的状态(通常是空列表)。
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5, 6};
list1.splice(list1.begin(), std::move(list2)); // list2 的元素移动到 list1 开头,list2 变为有效但未指定的状态
// list1 现在包含 {4, 5, 6, 1, 2, 3}
// list2 现在处于有效但未指定的状态
splice(const_iterator position, list& x, const_iterator i)
将列表 x 中由迭代器 i 指向的元素移动到调用列表的 position 位置之前。
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5, 6};
auto it = list2.begin(); // 指向 list2 的第一个元素
list1.splice(list1.begin(), list2, ++it); // list2 的第二个元素(值为 5)移动到 list1 开头
// list1 现在包含 {5, 1, 2, 3}
// list2 现在包含 {4, 6}
splice(const_iterator position, list& x, const_iterator first, const_iterator last)
将列表 x 中由范围 [first, last) 指定的元素移动到调用列表的 position 位置之前。
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5, 6, 7, 8};
auto it_first = list2.begin(); // 指向 list2 的第一个元素
auto it_last = std::next(it_first, 3); // 指向 list2 的第四个元素
list1.splice(list1.begin(), list2, it_first, it_last); // list2 中的前三个元素移动到 list1 开头
// list1 现在包含 {4, 5, 6, 1, 2, 3}
// list2 现在包含 {7, 8}
注意事项:
-
有效性: splice 操作后,原列表(即被移动元素所在的列表)的迭代器可能不再有效,因为元素已经被移动。
-
异常安全性: splice 操作是异常安全的,这意味着如果在操作过程中抛出异常,列表的状态将保持不变。
-
性能: 由于 splice 是在内部实现的,它通常比使用 insert 和 erase 来移动元素更高效,因为它不涉及复制或移动元素,而是直接重新链接节点。
-
空列表: 如果将一个空列表的所有元素移动到另一个列表,或者移动一个空范围,则操作仍然有效,但不会有任何元素被移动。
-
自移动: 尝试将一个列表的元素移动到它自身通常是不安全的,因为这可能导致迭代器失效和未定义的行为。尽管标准库不禁止这种操作,但通常应该避免它。
-
移动语义: 在 C++11 及更高版本中,splice 提供了移动语义的重载版本,可以高效地将一个列表的所有元素移动到另一个列表。这减少了不必要的复制操作,提高了性能。
总的来说,std::list::splice 是一个强大而灵活的工具,用于在 std::list 容器之间重新组织元素。正确使用它可以提高代码的效率并简化逻辑。
c.sort()
std::list 容器在 C++ 标准库中提供了 sort 成员函数,用于对列表中的元素进行排序。sort 函数使用默认的比较运算符(<)对元素进行升序排序,但你也可以提供自定义的比较函数或 lambda 表达式来改变排序的行为。
以下是 std::list::sort 的函数声明:
void sort();
template< class Compare >
void sort( Compare comp );
详细用法
默认排序(sort())
当调用 sort() 时,列表中的元素会使用默认的比较运算符(<)进行排序。这意味着列表中的元素类型必须支持这个运算符。
std::list<int> numbers = {4, 2, 5, 1, 3};
numbers.sort(); // 对 numbers 进行升序排序
// 输出排序后的列表
for (int num : numbers) {
std::cout << num << ' ';
}
// 输出: 1 2 3 4 5
自定义比较排序(sort(Compare comp))
如果你想要根据特定的比较逻辑对列表进行排序,你可以提供一个自定义的比较函数或 lambda 表达式。这个函数或表达式应该接受两个参数(即列表中的两个元素),并返回一个布尔值,指示第一个参数是否应该在排序后位于第二个参数之前。
#include <list>
#include <iostream>
#include <algorithm> // 为了使用 std::next_permutation
bool compareDescending(int a, int b) {
return a > b; // 降序排序
}
int main() {
std::list<int> numbers = {4, 2, 5, 1, 3};
// 使用自定义比较函数进行降序排序
numbers.sort(compareDescending);
// 输出排序后的列表
for (int num : numbers) {
std::cout << num << ' ';
}
// 输出: 5 4 3 2 1
return 0;
}
使用 lambda 表达式进行排序的例子:
std::list<std::string> words = {"apple", "banana", "cherry"};
// 使用 lambda 表达式进行字典序排序
words.sort([](const std::string& a, const std::string& b) {
return a < b;
});
// 输出排序后的列表
for (const std::string& word : words) {
std::cout << word << ' ';
}
// 输出: apple banana cherry
注意点
- sort 函数会就地(in-place)对列表进行排序,这意味着列表本身会被修改,而不是创建一个新的排序列表。
- 排序操作的复杂度通常为 O(N log N),其中 N 是列表中的元素数量。
- 如果提供了自定义比较函数或 lambda 表达式,它应该定义一个严格的弱序(strict weak ordering),即如果 comp(a, b) 为真,那么 comp(b, a) 必须为假,并且对于任何 a,comp(a, a) 必须为假。
- 在多线程环境中,如果列表被多个线程同时修改,排序操作可能是不安全的。需要确保在排序期间列表不会被其他线程修改。
c.merge()
std::list::merge 是 C++ 标准库中 std::list 容器的一个成员函数,它用于合并两个已排序的列表。该函数将两个列表合并成一个单一的有序列表,并且原始列表在合并操作后不再存在。
以下是 std::list::merge 的函数声明:
void merge(list& x);
template< class Compare >
void merge(list& x, Compare comp);
详细用法
默认合并(merge(list& x))
当调用 merge 而没有提供比较函数时,它假设两个列表都已经按照默认的比较运算符(<)排序。它会将两个列表合并成一个单一的有序列表,保持原有的排序顺序。
#include <iostream>
#include <list>
int main() {
std::list<int> list1 = {1, 3, 5};
std::list<int> list2 = {2, 4, 6};
// 合并两个已排序的列表
list1.merge(list2);
// 输出合并后的列表
for (int num : list1) {
std::cout << num << ' ';
}
// 输出: 1 2 3 4 5 6
return 0;
}
自定义比较合并(merge(list& x, Compare comp))
如果你想要根据自定义的比较逻辑来合并两个列表,你可以提供一个比较函数或 lambda 表达式。这个函数应该接受两个参数(即来自两个列表的元素),并返回一个布尔值,指示第一个参数是否应该在排序后位于第二个参数之前。
#include <iostream>
#include <list>
struct Compare {
bool operator()(const int& a, const int& b) {
// 自定义比较逻辑,例如降序排序
return a > b;
}
};
int main() {
std::list<int> list1 = {1, 3, 5};
std::list<int> list2 = {2, 4, 6};
// 使用自定义比较函数进行合并(降序)
list1.merge(list2, Compare());
// 输出合并后的列表
for (int num : list1) {
std::cout << num << ' ';
}
// 输出: 6 5 4 3 2 1
return 0;
}
注意事项
- merge 操作会改变调用它的列表,并且会销毁传入的列表 x。合并后的元素将存储在调用 merge 的列表中。
- 如果两个列表中有重复的元素,merge 操作会保留它们,并按照它们在各自列表中的相对顺序进行排序。
- 自定义比较函数应该定义严格的弱序(strict weak ordering),以保证合并操作的正确性。
- 在多线程环境中,如果列表被多个线程同时修改,合并操作可能是不安全的。需要确保在合并期间列表不会被其他线程修改。
- merge 操作的复杂度通常为线性,即 O(N + M),其中 N 和 M 分别是两个列表中的元素数量。
c.reverse()
std::list 容器在 C++ 标准库中提供了 reverse 成员函数,用于反转列表中的元素顺序。这是一个就地(in-place)操作,意味着它直接修改列表本身,而不是创建一个新的反转后的列表。
以下是 std::list::reverse 的函数声明:
void reverse();
详细用法
reverse 函数非常简单,不需要任何参数。调用它之后,列表中的元素顺序将被反转。
#include <iostream>
#include <list>
int main() {
std::list<int> numbers = {1, 2, 3, 4, 5};
// 输出原始列表
std::cout << "Original list: ";
for (int num : numbers) {
std::cout << num << ' ';
}
std::cout << std::endl;
// 反转列表
numbers.reverse();
// 输出反转后的列表
std::cout << "Reversed list: ";
for (int num : numbers) {
std::cout << num << ' ';
}
std::cout << std::endl;
return 0;
}
输出
Original list: 1 2 3 4 5
Reversed list: 5 4 3 2 1
注意事项
- reverse 操作的时间复杂度通常是 O(N),其中 N 是列表中的元素数量。这是因为需要遍历列表中的每个元素一次以完成反转。
- reverse 是一个就地操作,它会直接修改调用它的列表,而不是创建一个新的反转列表。
- 由于 reverse 仅仅交换了元素的位置,而不是它们的值,所以它不需要元素的类型支持任何特定的操作或函数。
- 在多线程环境中,如果列表被多个线程同时修改,反转操作可能是不安全的。需要确保在反转期间列表不会被其他线程修改。
- reverse 函数不接受任何参数,因此无法根据自定义的比较函数来反转列表。它总是按照元素在列表中的当前顺序进行反转。