链表和C++ std::list详解

news2024/11/18 15:32:55

文章目录

  • 1. 链表和std::list
  • 2. list的用法
    • 2.1 list的定义和声明
    • 2.2 成员函数
      • 2.2.1 基本函数
        • 构造函数
        • operator=
        • assign
        • get_allocator
      • 2.2.2 元素访问
        • front
        • back
      • 2.2.3 迭代器
        • begin、end和cbegin、cend
        • rbegin、rend和crbegin、crend
      • 2.2.4 容量
        • empty
        • size
        • max_size
      • 2.2.5 修改器
        • clear
        • insert
        • emplace
        • earse
        • push_back
        • emplace_back
        • pop_back
        • push_front
        • emplace_front
        • pop_front
        • resize
        • swap
      • 2.2.6 操作
        • merge
        • splice
        • remove、remove_if
        • reverse
        • unique
        • sort
    • 2.3 非成员函数
      • operator==,!=,<,<=,>,>=,<=>(std::list)
      • std::swap(std::list)
        • std::erase, std::erase_if (std::list)
  • 3. 总结

1. 链表和std::list

链表是一种在物理上非连续、非顺序的数据结构,数据元素的逻辑顺序是通过链表中的指针链接实现,其由若干节点所组成。std::list是C++中支持常数时间从容器任何位置插入和移除元素的容器,但其不支持快速的随机访问,其通常实现为双向链表。

在这里插入图片描述

由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器。在std::list中添加、移动和移除元素不会使迭代器或引用失效,迭代器只有在对应元素被删除时才会失效。

2. list的用法

2.1 list的定义和声明

std::list在头文件<list>中定义,其声明如下:

emplate<
    class T,
    class Allocator = std::allocator<T>
> class list;

namespace pmr {
    template< class T >
    using list = std::list<T, std::pmr::polymorphic_allocator<T>>; //C++17 起
}

其中,参数T为容器要存储的元素类型,对于T需要满足:

  • 可复制赋值和可复制构造(C++11前)。
  • 要求元素类型是完整类型并满足可擦除,即元素类型的对象能以给定的分配器(Allocator)销毁(C++11 起,C++17 前)。
  • 要求元素类型是完整类型并满足可擦除,但许多成员函数附带了更严格的要求。(C++17 起)。

Allocator为用于获取/释放内存及构造/析构内存中元素的分配器。

2.2 成员函数

2.2.1 基本函数

构造函数

功能描述

  • 创建list容器。

函数原型

//1.默认构造函数。构造拥有默认构造的分配器的空容器。
list();

//2.构造拥有给定分配器 alloc 的空容器。
explicit list( const Allocator& alloc );

//3.构造拥有 count 个有值 value 的元素的容器。
explicit list( size_type count,
               const T& value = T(),
               const Allocator& alloc = Allocator()); //C++11 前
list( size_type count,
               const T& value,
               const Allocator& alloc = Allocator()); //C++11 起

//4.构造拥有 count 个 默认插入的 T 实例的容器。不进行复制。
explicit list( size_type count ); //C++11 起, C++14 前
explicit list( size_type count, const Allocator& alloc = Allocator() ); //C++14 起

//5.构造拥有范围 [first, last) 内容的容器。
template< class InputIt >
list( InputIt first, InputIt last,
      const Allocator& alloc = Allocator() );

//6.复制构造函数。构造拥有 other 内容的容器。
list( const list& other );

//7.构造拥有 other 内容的容器,以 alloc 为分配器。
list( const list& other, const Allocator& alloc ); //C++11 起

//8.移动构造函数。用移动语义构造拥有 other 内容的容器。分配器通过属于 other 的分配器移动构造获得。
list( list&& other ); //C++11 起

//9.有分配器扩展的移动构造函数。以 alloc 为新容器的分配器,从 other 移动内容。
list( list&& other, const Allocator& alloc ); //C++11 起

//10.构造拥有 initializer_list init 内容的容器。
list( std::initializer_list<T> init,
      const Allocator& alloc = Allocator() ); //C++11 起

示例

// C++11 初始化器列表语法:
std::list<std::string> words1 {"the", "frogurt", "is", "also", "cursed"};
//words1 = {"the", "frogurt", "is", "also", "cursed"}

// words2 == words1
std::list<std::string> words2(words1.begin(), words1.end());
//words2 = {"the", "frogurt", "is", "also", "cursed"}

// words3 == words1
std::list<std::string> words3(words1);
//words3 = {"the", "frogurt", "is", "also", "cursed"}

// words4 是 {"Mo", "Mo", "Mo", "Mo", "Mo"}
std::list<std::string> words4(5, "Mo");
//words4 = {Mo, Mo, Mo, Mo, Mo}

operator=

功能描述

  • 用于赋值给容器。

函数原型

//复制赋值运算符。以 other 的副本替换内容。
list& operator=( const list& other );

//移动赋值运算符。用移动语义以 other 的内容替换内容(即从 other 移动 other 中的数据到此容器中)。之后other在合法但未指定的状态。
list& operator=( list&& other ); //C++11 起, C++17 前
list& operator=( list&& other ) noexcept(); //C++17 起

//以 initializer_list ilist 所标识者替换内容。
list& operator=( std::initializer_list<T> ilist ); //C++11 起

示例

std::list<int> nums1 {3, 1, 4, 6, 5, 9};
std::list<int> nums2;
std::list<int> nums3;

// 从 nums1 复制赋值数据到 nums2
nums2 = nums1;
//此时nums2 = {3, 1, 4, 6, 5, 9}

// 从 nums1 移动赋值数据到 nums3,
// 修改 nums1 和 nums3
nums3 = std::move(nums1);
//此时 nums1 = {}, nums3 = {3, 1, 4, 6, 5, 9}


// initializer_list 的复制赋值复制数据给 nums3
nums3 = {1, 2, 3};
//此时nums3 = {1, 2, 3}

assign

功能描述

  • 将值赋给容器,替换容器的内容。

函数原型

//以 count 份 value 的副本替换内容。
void assign( size_type count, const T& value );

//以范围 [first, last) 中元素的副本替换内容。其中有任何一个迭代器是指向 *this 中的迭代器时行为未定义。
template< class InputIt >
void assign( InputIt first, InputIt last );

//以来自 initializer_list ilist 的元素替换内容。
void assign( std::initializer_list<T> ilist ); //C++11 起

示例

std::list<char> c;

c.assign(5, 'a');//此时c = {'a', 'a', 'a', 'a', 'a'}

const std::string str(6, 'b');
c.assign(str.begin(), str.end());//此时c = {'b', 'b', 'b', 'b', 'b', 'b'}

c.assign({'C', '+', '+', '1', '1'});//此时c = {'C', '+', '+', '1', '1'}

get_allocator

功能描述

  • 返回相关的分配器。

函数原型

//返回值:与容器关联的分配器。
allocator_type get_allocator() const; //C++11 前
allocator_type get_allocator() const noexcept; //C++11 起

2.2.2 元素访问

front

功能描述

  • 访问容器的第一个元素,其返回值为容器首元素的引用。

函数原型

reference front();
const_reference front() const;

:在空容器上对 front 的调用是未定义的。

back

功能描述

  • 访问容器最后一个元素,其返回值为容器最后一个元素的引用。

函数原型

reference back();
const_reference back() const;

:在空容器上对 back 的调用是未定义的。

2.2.3 迭代器

begin、end和cbegin、cend

功能描述

  • begin和cbegin返回指向list首元素的迭代器,
  • end和cend返回指向list末元素后一元素的迭代器。

函数原型

iterator begin(); //C++11 前
iterator begin() noexcept; //C++11 起
const_iterator begin() const; //C++11 前
const_iterator begin() const noexcept; //C++11 起
const_iterator cbegin() const noexcept; //C++11 起

iterator end(); //C++11 前
iterator end() noexcept; //C++11 起
const_iterator end() const; //C++11 前
const_iterator end() const noexcept; //C++11 起
const_iterator cend() const noexcept; //C++11 起

如果list为空,则返回的迭代器将等于end或cend。end和cend指向list末元素后一元素的迭代器,该元素的表现为占位符,试图访问它将导致未定义行为。

rbegin、rend和crbegin、crend

功能描述

  • rbegin和crbegin返回指向list首元素的逆向迭代器。它对应非逆向list的末元素,若list为空,则返回的迭代器等于rend或crend。
  • rend和crend返回指向逆向list末元素后一元素的逆向迭代器,它对应非逆向list首元素的前一元素,此元素表现为占位符,试图访问它导致未定义行为。

函数原型

reverse_iterator rbegin(); //C++11 前
reverse_iterator rbegin() noexcept; //C++11 起
const_reverse_iterator rbegin() const; //C++11 前
const_reverse_iterator rbegin() const noexcept; //C++11 起
const_reverse_iterator crbegin() const noexcept; //C++11 起

reverse_iterator rend(); //C++11 前
reverse_iterator rend() noexcept; //C++11 起
const_reverse_iterator rend() const; //C++11 前
const_reverse_iterator rend() const noexcept; //C++11 起
const_reverse_iterator crend() const noexcept; //C++11 起

2.2.4 容量

empty

功能描述

  • 检查容器是否为空,若为空则返回true,否则为false。

函数原型

bool empty() const; //C++11 前
bool empty() const noexcept; //C++11 起,C++20 前
[[nodiscard]] bool empty() const noexcept; //C++20 起

其底层实现就是检查容器是否无元素,即判断是否begin() == end()

size

功能描述

  • 返回容器中元素数量,即std::distance(begin(), end())

函数原型

size_type size() const; //C++11 前
size_type size() const noexcept; //C++11 起

max_size

功能描述

  • max_size函数返回根据系统或库实现限制的容器可保有的元素最大数量,即对于最大容器std::distance(begin(), end())

函数原型

size_type max_size() const; //C++11 前
size_type max_size() const noexcept; //C++11 起

:此值通常反映容器大小上的理论极限,至多为 std::numeric_limits<difference_type>::max() 。运行时,可用 RAM 总量可能会限制容器大小到小于 max_size() 的值。

2.2.5 修改器

clear

功能描述

  • 擦除所有元素,使用clear()后,再次调用size(),size函数返回0。

函数原型

void clear(); //C++11 前
void clear() noexcept; //C++11 起

insert

功能描述

  • 插入元素到容器的指定位置。

函数原型

//在 pos 前插入 value。
//返回值:指向被插入 value 的迭代器。
iterator insert( const_iterator pos, const T& value );
iterator insert( const_iterator pos, T&& value ); //C++11 起

//在 pos 前插入 value 的 count 个副本。
//返回值:指向首个被插入元素的迭代器,或者在 count == 0 时返回 pos。
iterator insert( const_iterator pos, size_type count, const T& value );

//在 pos 前插入来自范围 [first, last) 的元素。
//返回值:指向首个被插入元素的迭代器,或者在 first == last 时返回 pos。
template< class InputIt >
iterator insert( const_iterator pos, InputIt first, InputIt last );

//在 pos 前插入来自 initializer_list ilist 的元素。
//返回值:指向首个被插入元素的迭代器,或者在 ilist 为空时返回 pos。
iterator insert( const_iterator pos, std::initializer_list<T> ilist ); //C++11 起

示例

std::list<int> c1(3, 100); //初始化c1,此时c1 = {100, 100, 100}

auto it = c1.begin();
it = c1.insert(it, 200); //在it前插入元素200
//c1 = {200,100, 100, 100}

c1.insert(it, 2, 300); //在it前插入两个元素值都为300
//c1 = {300,300,200,100, 100, 100}

// 将 it 重新指向开头
it = c1.begin();

std::list<int> c2(2, 400); //c2 = {400, 400}
c1.insert(std::next(it, 2), c2.begin(), c2.end()); //在it后两个元素(即200)的前面插入c2
//c1 = {300,300,400,400,200,100, 100, 100}

int arr[] = {501, 502, 503};
c1.insert(c1.begin(), arr, arr + std::size(arr));
//c1 = {501,502,503,300,300,400,400,200,100, 100, 100}

c1.insert(c1.end(), {601, 602, 603});
//c1 = {501,502,503,300,300,400,400,200,100, 100, 100,601,602,603}

emplace

功能描述

  • 原位构造元素并将其在pos前插入到容器中。

函数原型

/*----------------------------------
  pos:将构造新元素到其前的迭代器
  args:转发给元素构造函数的参数
  返回值iterator:指向被安置的元素的迭代器
------------------------------------*/
template< class... Args >
iterator emplace( const_iterator pos, Args&&... args ); //C++11 起

:通过 std::allocator_traits::construct构造元素,用布置 new 在容器提供的位置原位构造元素。

将参数 args... 作为 std::forward<Args>(args)... 转发给构造函数。 args... 可以直接或间接地指代容器中的值。

earse

功能描述

  • 擦除元素,

函数原型

//移除位于pos的元素
//返回值:最后移除元素之后的迭代器。如果pos指代末元素,则返回end()迭代器
iterator erase( iterator pos ); //C++11 前
iterator erase( const_iterator pos ); //C++11 起

//移除范围[first, last)中的元素。
/*返回值:最后移除元素之后的迭代器。
         如果在移除前last == end(),那么最终返回end()迭代器
         如果范围[first, last) 为空,那么返回 last。*/
iterator erase( iterator first, iterator last ); //C++11 前
iterator erase( const_iterator first, const_iterator last ); //C++11 起

:指向被擦除元素的迭代器和引用会失效。其他引用和迭代器不受影响。

示例

std::list<int> c{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

c.erase(c.begin());
// c = {1, 2, 3, 4, 5, 6, 7, 8, 9}

std::list<int>::iterator range_begin = c.begin();
std::list<int>::iterator range_end = c.begin();
std::advance(range_begin, 2);
std::advance(range_end, 5);

c.erase(range_begin, range_end);
// c = {1, 2, 6, 7, 8, 9}

// 移除所有偶数
for (std::list<int>::iterator it = c.begin(); it != c.end();)
{
  if (*it % 2 == 0)
    it = c.erase(it);
  else
    ++it;
}
// c = {1, 7, 9}

push_back

功能描述

将元素添加到容器末尾。

函数原型

//后附给定元素 value 到容器尾。初始化新元素为 value 的副本。
void push_back( const T& value );

//后附给定元素 value 到容器尾。移动 value 进新元素。
void push_back( T&& value ); //C++11 起

emplace_back

功能描述

emplace_back函数与emplace类似,只不过是在容器末尾就地构造元素。

函数原型

template< class... Args >
void emplace_back( Args&&... args ); //C++11 起,C++17 前

template< class... Args >
reference emplace_back( Args&&... args ); //C++17 起

由于emplace_back是原地构造元素,因此其插入效率要高于push_back。

pop_back

功能描述

移除末元素。

函数原型

void pop_back(); 

如果在空容器上调用pop_back会导致未定义行为。

:指向被擦除元素的迭代器和引用会失效。

push_front

功能描述

插入元素到容器起始。

函数原型

void push_front( const T& value );
void push_front( T&& value ); //C++11 起

emplace_front

功能描述

在容器头部原位构造元素,与push_front功能相同,主要区别是其它典型地用布置 new 在容器所提供的位置原位构造元素。将参数 args... 作为 std::forward<Args>(args)... 转发给构造函数。

函数原型

template< class... Args >
void emplace_front( Args&&... args ); //C++11 起, C++17 前

template< class... Args >
reference emplace_front( Args&&... args ); //C++17 起

pop_front

功能描述

移除容器首元素。若容器中无元素,则行为未定义。指向被擦除元素的迭代器和引用会失效。

函数原型

void pop_front();

resize

功能描述

改变容器中可存储元素的个数,通过该函数可以重新设置容器大小。

函数原型

/*
该函数重设容器的大小为count,在count==size()时不做任何操作。
如果当前大小大于 count,那么减小容器到它的开头 count 个元素。
如果当前大小小于 count,那么后附额外的默认插入的元素。
*/
void resize( size_type count );

/*
该函数重设容器的大小为count,在count==size()时不做任何操作。
如果当前大小大于 count,那么减小容器到它的开头 count 个元素。
如果当前大小小于 count,那么后附额外的 value 的副本
*/
void resize( size_type count, const value_type& value );

示例

std::list<int> c = {1, 2, 3};

c.resize(5); //将其size增加大小到5
//c = {1, 2, 3, 0, 0}

c.resize(2); //将其size减少大小到2
//c = {1, 2}

c.resize(6, 4); //将其size增加大小到6,填充值为4";
//c = {1, 2, 4, 4, 4,4}

swap

功能描述

交换两个list容器的内容,不在单独的元素上调用任何移动、复制或交换操作。所有迭代器和引用保持有效。在操作后,未指明保有此容器中 end()值的迭代器指代此容器还是另一容器。

函数原型

void swap( list& other ); //C++17 前
void swap( list& other ) noexcept(); //C++17 起

示例

std::list<int> a1{1, 2, 3}, a2{4, 5};

auto it1 = std::next(a1.begin()); //*it1 = 2 
auto it2 = std::next(a2.begin()); //*it2 = 5 

int& ref1 = a1.front(); //ref1 = 1
int& ref2 = a2.front(); //ref1 = 4

std::cout <<*it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
//打印结果为2 5 1 4

a1.swap(a2);

//此时a1 = {4, 5},a2 = {1, 2, 3}
std::cout <<*it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
//打印结果仍为2 5 1 4

/*注:
    交换后迭代器与引用保持与原来的元素关联,
    例如尽管 'a1' 中值为 2 的元素被移动到 'a2' 中,
    原来指向它的 it1 仍指向同一元素。*/

2.2.6 操作

merge

功能描述

合并二个已排序列表。

函数原型

//用 operator< 比较元素
void merge( list& other );
void merge( list&& other ); //C++11 起

//用给定的比较函数 comp 比较元素。
template < class Compare >
void merge( list& other, Compare comp );
template < class Compare >
void merge( list&& other, Compare comp ); //C++11 起

如果 other*this指代同一对象,那么什么也不做。否则将两个已经排序列表归并为一个。链表应以升序排序。不复制元素,并且在操作后容器other会变空。不会无效化任何引用或者迭代器,但被移动元素的迭代器现在指代到*this中,而不是到other中。

:对于两个链表中的等价元素,来自 *this 的元素始终在来自 other 的元素之前,并且 *this 和 other 的等价元素顺序不更改。如果 get_allocator() != other.get_allocator(),那么行为未定义。

示例

std::list<int> list1 = {5, 9, 1, 3, 3};
std::list<int> list2 = {8, 7, 2, 3, 4, 4};

list1.sort(); // 1 3 3 5 9
list2.sort(); // 2 3 4 4 7 8

list1.merge(list2); //1 2 3 3 3 4 4 5 7 8 9

splice

功能描述

从一个 list 转移元素给另一个。

函数原型

/*《参数说明》
pos	        -	将插入内容到它之前的元素
other	      -	要从它转移内容的另一容器
it	        -	要从 other 转移到 *this 的元素
first, last	-	要从 other 转移到 *this 的元素范围
*/


/*从 other 转移所有元素到 *this 中。元素被插入到 pos 指向的元素之前。
操作后容器 other 变为空。other 与 *this 指代同一对象时行为未定义。*/
void splice( const_iterator pos, list& other );
void splice( const_iterator pos, list&& other ); //C++11 起

/*从 other 转移 it 指向的元素到 *this。元素被插入到 pos 指向的元素之前。*/
void splice( const_iterator pos, list& other, const_iterator it );
void splice( const_iterator pos, list&& other, const_iterator it ); //C++11 起

/*从 other 转移范围 [first, last) 中的元素到 *this。
元素被插入到 pos 指向的元素之前。pos 是范围 [first,last) 中的迭代器时行为未定义。*/
void splice( const_iterator pos, list& other,
             const_iterator first, const_iterator last);
void splice( const_iterator pos, list&& other,
             const_iterator first, const_iterator last ); //C++11 起

不复制或移动元素,仅重指向链表结点的内部指针。get_allocator() != other.get_allocator() 时行为未定义。没有迭代器或引用会失效,指向被移动元素的迭代器保持有效,但现在指代到 *this 中,而非到 other 中。

示例

std::list<int> list1 = {1, 2, 3, 4, 5};
std::list<int> list2 = {10, 20, 30, 40, 50};

auto it = list1.begin();

list1.splice(it, list2);
//list1 = {10, 20, 30, 40, 50, 1, 2, 3, 4, 5};
//list2 = {};

list2.splice(list2.begin(), list1, it, list1.end());
//list1 = {10, 20, 30, 40, 50};
//list2 = {1, 2, 3, 4, 5};

remove、remove_if

功能描述

移除满足特定标准的元素。

函数原型

//移除所有等于 value 的元素
void remove( const T& value ); //C++20 前
size_type remove( const T& value ); //C++20 起

//移除所有谓词 p 对它返回 true 的元素
template< class UnaryPredicate >
void remove_if( UnaryPredicate p ); //C++20 前
template< class UnaryPredicate >
size_type remove_if( UnaryPredicate p ); //C++20 起

示例

std::list<int> l = { 1,100,2,3,10,1,11,-1,12 };

l.remove(1); // 移除两个等于 1 的元素
l.remove_if([](int n){ return n > 10; }); // 移除全部大于 10 的元素

for (int n : l) {
  std::cout << n << ' '; 
}
std::cout << '\n';

//输出结果:2 3 10 -1

reverse

功能描述

将该链表的所有元素的顺序反转。逆转容器中的元素顺序。不非法化任何引用或迭代器。

函数原型

void reverse(); //C++11 前)
void reverse() noexcept; //C++11 起

示例

std::list<int> list = { 8,7,5,9,0,1,3,2,6,4 };
list.reverse(); //4 6 2 3 1 0 9 5 7 8

unique

功能描述

删除连续的重复元素。从容器移除所有相继的重复元素。只留下相等元素组中的第一个元素。若选择的比较器不建立等价关系则行为未定义。

函数原型

//用 operator== 比较元素。
void unique(); //C++20 前
size_type unique(); //C++20 起

//用二元谓词 p 比较元素。
template< class BinaryPredicate >
void unique( BinaryPredicate p ); //C++20 前
template< class BinaryPredicate >
size_type unique( BinaryPredicate p ); //C++20 起

示例

std::list<int> c = {1, 2, 2, 3, 3, 2, 1, 1, 2};
c.unique();// 1 2 3 2 1 2

c = {1, 2, 12, 23, 3, 2, 51, 1, 2};
c.unique([mod=10](int x, int y) { return (x % mod) == (y % mod); });
//1 2 23 2 51 2

sort

功能描述

对元素进行排序。以升序排序元素。保持相等元素的顺序。

函数原型

//用 operator< 比较元素
void sort();
	
//用给定的比较函数 comp, 在第一参数小于(即先序于)第二参数时返回 true。
template< class Compare >
void sort( Compare comp );

示例

std::list<int> list = { 8,7,5,9,0,1,3,2,6,4 };

list.sort();//0 1 2 3 4 5 6 7 8 9
list.sort(std::greater<int>());//9 8 7 6 5 4 3 2 1 0

list = {1, 2, 14, 25, 3, 22};
//按照元素个位数的大小进行排序
list.sort([mod = 10](int x, int y)
         { return (x % mod) < (y % mod); });//1 2 22 3 14 25

2.3 非成员函数

operator==,!=,<,<=,>,>=,<=>(std::list)

功能描述

按照字典顺序比较 list 中的值。

函数声明

//1. ==
//返回值:在 list 内容相等时返回 true,否则返回 false
template< class T, class Alloc >
bool operator==( const std::list<T, Alloc>& lhs,
                 const std::list<T, Alloc>& rhs );

//2. !=
//返回值:在 list 内容不相等时返回 true,否则返回 false
template< class T, class Alloc >
bool operator!=( const std::list<T, Alloc>& lhs,
                 const std::list<T, Alloc>& rhs ); //C++20 前

//3. <
//返回值:在 lhs 的内容按字典序小于 rhs 的内容时返回 true,否则返回 false
template< class T, class Alloc >
bool operator<( const std::list<T, Alloc>& lhs,
                const std::list<T, Alloc>& rhs ); //C++20 前

//4. <=
//返回值:在 lhs 的内容按字典序小于或等于 rhs 的内容时返回 true,否则返回 false
template< class T, class Alloc >
bool operator<=( const std::list<T, Alloc>& lhs,
                 const std::list<T, Alloc>& rhs ); //C++20 前

//5. >
//返回值:在 lhs 的内容按字典序大于 rhs 的内容时返回 true,否则返回 false
template< class T, class Alloc >
bool operator>( const std::list<T, Alloc>& lhs,
                const std::list<T, Alloc>& rhs ); //C++20 前

//6. >=
//返回值:在 lhs 的内容按字典序大于或等于 rhs 的内容时返回 true,否则返回 false
template< class T, class Alloc >
bool operator>=( const std::list<T, Alloc>& lhs,
                 const std::list<T, Alloc>& rhs ); //C++20 前

//7. <=>
//返回值:lhs 与 rhs 中的首对不等价元素的相对顺序,如果有这种元素;否则是 lhs.size() <=> rhs.size()。
template< class T, class Alloc >
operator<=>( const std::list<T, Alloc>& lhs,
                             const std::list<T, Alloc>& rhs ); //C++20 起
  • 1,2中会检查 lhs 与 rhs 的内容是否相等,即它们是否拥有相同数量的元素且 lhs 中每个元素与 rhs 的同位置元素比较相等。

  • 3-6中按照字典比较lhs和rhs的内容,其内部等价于调用std::lexicographical_compare函数进行比较。

  • 7中也是按字典序比较lhs和rhs的内容。其内部等价于调用std::lexicographical_compare_three_way 进行比较。返回类型同合成三路比较的结果类型。其逻辑大致如下:

    lhs < rhs ? std::weak_ordering::less :
    rhs < lhs ? std::weak_ordering::greater :
                std::weak_ordering::equivalent
    //注:通常情况下less对应的是-1,greater对应1,equivalent对应0
    

    lhs与rhs中的首对不等价元素的相对顺序,如果有这种元素;否则是 lhs.size() <=> rhs.size()

示例

std::list<int> alice{1, 2, 3};
std::list<int> bob{7, 8, 9, 10};
std::list<int> eve{1, 2, 3};

std::cout << std::boolalpha;

// 比较不相等的容器
std::cout << "alice == bob returns " << (alice == bob) << '\n';
std::cout << "alice != bob returns " << (alice != bob) << '\n';
std::cout << "alice <  bob returns " << (alice < bob) << '\n';
std::cout << "alice <= bob returns " << (alice <= bob) << '\n';
std::cout << "alice >  bob returns " << (alice > bob) << '\n';
std::cout << "alice >= bob returns " << (alice >= bob) << '\n';

std::cout << '\n';

// 比较相等的容器
std::cout << "alice == eve returns " << (alice == eve) << '\n';
std::cout << "alice != eve returns " << (alice != eve) << '\n';
std::cout << "alice <  eve returns " << (alice < eve) << '\n';
std::cout << "alice <= eve returns " << (alice <= eve) << '\n';
std::cout << "alice >  eve returns " << (alice > eve) << '\n';
std::cout << "alice >= eve returns " << (alice >= eve) << '\n';

输出结果为

alice == bob returns false
alice != bob returns true
alice <  bob returns true
alice <= bob returns true
alice >  bob returns false
alice >= bob returns false
 
alice == eve returns true
alice != eve returns false
alice <  eve returns false
alice <= eve returns true
alice >  eve returns false
alice >= eve returns true

std::swap(std::list)

功能描述

std::list特化 std::swap算法。

函数原型

template< class T, class Alloc >
void swap( std::list<T, Alloc>& lhs,
           std::list<T, Alloc>& rhs ); //C++17 前
template< class T, class Alloc >
void swap( std::list<T, Alloc>& lhs,
           std::list<T, Alloc>& rhs ) noexcept(); //C++17 起

交换 lhsrhs 的内容。调用lhs.swap(rhs)

示例

std::list<int> a1{1, 2, 3}, a2{4, 5};

auto it1 = std::next(a1.begin()); //*it1 = 2
auto it2 = std::next(a2.begin()); //*it2 = 5

int &ref1 = a1.front(); // ref1 = 1
int &ref2 = a2.front(); // ref1 = 4

std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印结果为2 5 1 4

std::swap(a1, a2);

// 此时a1 = {4, 5},a2 = {1, 2, 3}
std::cout << *it1 << ' ' << *it2 << ' ' << ref1 << ' ' << ref2 << '\n';
// 打印结果仍为2 5 1 4

/*注:
      交换后迭代器与引用保持与原来的元素关联,
      例如尽管 'a1' 中值为 2 的元素被移动到 'a2' 中,
      原来指向它的 it1 仍指向同一元素。*/

std::erase, std::erase_if (std::list)

功能描述

函数主要用来擦除所有满足特定判别标准的元素。

函数原型

//从容器中擦除所有比较等于value的元素,
//等价于 return c.remove_if([&](auto& elem) { return elem == value; });
template< class T, class Alloc, class U >
typename std::list<T, Alloc>::size_type
    erase(std::list<T, Alloc>& c, const U& value); //C++20 起

//从容器中擦除所有满足pred的元素,pred为应该擦除元素则返回true的一元谓词。
//等价于 return c.remove_if(pred);
template< class T, class Alloc, class Pred >
typename std::list<T, Alloc>::size_type
    erase_if(std::list<T, Alloc>& c, Pred pred); //C++20 起

返回值为被擦除的元素数。

示例

std::list<int> c{1, 2, 3, 4, 6};
// 擦除c中的值等于3的元素
auto erased1 = std::erase(c, 3); // erased1 = 1
// 此时c = {1, 2, 4, 6}

// 擦除c中的偶数
auto erased2 = std::erase_if(c, [](int n)
                             { return n % 2 == 0; }); // erased2 = 3
// 此时c = {1}

3. 总结

list容器的优势和劣势:

优势

  • 采用动态内存分配,不会造成内存浪费和溢出。
  • 执行插入和删除操作十分方便、高效。修改指针即可,不需要移动大量元素。

劣势

  • 空间(指针域)和时间(遍历)额外耗费比较大。

文章首发公众号:iDoitnow如果喜欢话,可以关注一下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/791524.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

扩展 Python 的 unittest 框架

目录 前言&#xff1a; 如何控制 unittest 用例执行的顺序呢&#xff1f; 方式 1&#xff0c;通过 TestSuite 类的 addTest 方法&#xff0c;按顺序加载测试用例 方式 2&#xff0c;通过修改函数名的方式 那就造个轮子吧 前言&#xff1a; Python 的 unittest 模块提供了…

做好防雷检测的意义和作用

防雷检测是指对雷电防护装置的性能、质量和安全进行检测的活动&#xff0c;是保障人民生命财产和公共安全的重要措施。我国对防雷检测行业有明确的国家标准和管理办法&#xff0c;要求从事防雷检测的单位和人员具备相应的资质和能力&#xff0c;遵守相关的技术规范和规程&#…

Cpp 01 — namespace命名空间、C++的输入与输出、缺省参数、函数重载、引用、隐式类型转换

前言&#xff1a;本文章主要用于个人复习&#xff0c;追求简洁&#xff0c;感谢大家的参考、交流和搬运&#xff0c;后续可能会继续修改和完善。 因为是个人复习&#xff0c;会有部分压缩和省略。 一、namespace命名空间 C使用命名空间(namespace)来避免命名冲突。 在定义一个…

初阶C语言-分支和循环语句(下)

“花会沿途盛开&#xff0c;以后的路也是。” 今天我们一起来继续学完分支语句和循环语句。 分支和循环 3.循环语句3.4 do...while()循环3.4.1 do语句的用法 3.5关于循环的一些练习3.6 goto语句 3.循环语句 3.4 do…while()循环 3.4.1 do语句的用法 do循环语句;//当循环语句…

【unity】ShaderGraph学习笔记

【unity】ShaderGraph学习笔记 创建ShaderGraph 创建URP的shaderGraph文件 在Project面板里Create→ShaderGraph→URP→这里主要有几个选项 Lit Shader Graph&#xff1a;有光照三维着色器 Unlit Shader Graph&#xff1a;无光照三维着色器 Sprite Custom Lit Shader Gra…

【解决方案】医疗行业资产定位管理方案-蓝牙资产定位方案

蓝牙资产定位追踪技术已经开始应用于多种行业的资产、设备管理方面&#xff0c;有不少机构、企业开始尝试使用实时蓝牙定位系统对其各类资产、机器设备、工具、材料、产品等进行精准定位&#xff0c;提高各类资产、设备的管理和使用效率&#xff0c;从而节省运营成本。 医疗行业…

汽配企业建设数字化工厂的步骤是什么

随着信息技术的迅猛发展&#xff0c;汽车行业也面临着数字化转型的迫切需求。汽配企业作为汽车产业链上重要的一环&#xff0c;也需要积极采取措施&#xff0c;建设数字化工厂系统&#xff0c;以适应市场竞争的变化。下面将介绍建设汽配企业数字化工厂的具体步骤。 第一步&…

在niso ii中读写EPCS时出现“Can‘t open flash device”

在niso ii中读写EPCS时出现“Can’t open flash device”&#xff0c;即alt_flash_open_dev(EPCS_FLASH_NAME)函数返回0。 Cant open flash device函数alt_flash_open_dev(EPCS_FLASH_NAME)打开epcs不成功&#xff0c;返回为0&#xff0c;试了几次硬件重新烧入、重启开发板还是…

呕心沥血解决xampp启动mysql异常停止due to a blocked port, missing dependencies问题

Error: MySQL shutdown unexpectedly.<br> This may be due to a blocked port, missing dependencies 这一行就是我问题的开始。 原因是因为&#xff0c;之前一直使用xampp用作本地mysql的启动管理&#xff0c;是个很好用的工具&#xff0c;但是近日想要给一个项目配…

STM32速成笔记—串口IAP

本文涉及到串口通信和Flash知识&#xff0c;对于这部分知识不熟悉的小伙伴可以到博主STM32速成笔记专栏查看。 文章目录 一、串口IAP简介1.1 什么是IAP1.2 STM32下载程序 二、串口IAP有什么作用三、启动流程3.1 正常启动流程3.2 加入IAP后的启动流程 四、必备知识4.1 修改程序运…

日常开发中Git遇到的问题-记录贴

日常开发中Git遇到的问题 前言场景一&#xff1a;clone代码并进行开发完毕后&#xff0c;进行代码合并时要求在新分支下提交代码场景二&#xff1a;远程分支被删除后&#xff0c;本地分支缓存需要更新&#xff0c;防止提交错误 前言 本篇博客只是用来记录平时开发过程之中用gi…

从头开始:数据结构和算法入门(时间复杂度、空间复杂度)

目录 文章目录 前言 1.算法效率 1.1 如何衡量一个算法的好坏 1.2 算法的复杂度 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见时间复杂度计算 3.空间复杂度 4.常见复杂度对比 总结 前言 C语言的学习篇已经结束&#xff0c;今天开启新的篇章——数据结构和算…

Hive视图

hive的视图 简介 hive的视图简单理解为逻辑上的表hive只支持逻辑视图&#xff0c;不支持物化视图视图存在的意义 对数据进行局部暴露&#xff08;涉及隐私的数据不暴露&#xff09;简化复杂查询 创建视图&#xff1a; create view if not exists v_1 as select uid,movie f…

国内十大精准的现货黄金价格走势图软件最新排名(综合版)

选择国内现货黄金价格走势图软件时&#xff0c;需要考虑几个因素。首先&#xff0c;软件的稳定性和可靠性至关重要。应选择有良好声誉和长期稳定运行的平台&#xff0c;以确保价格数据的准确性和及时性。其次&#xff0c;要选择功能齐全的软件。较为优秀的软件应该提供多种技术…

C++学习——类和对象(三)

接着我们就继续学习我们C当中的相关的知识。 一&#xff1a;初始化列表 还记得我们之前讲过的构造函数吗&#xff1f;我们在构造函数的函数体里面可以对对象当中的属性进行初始化。但是作为我们的构造函数来说&#xff0c;初始化的方式并不只是在构造函数体当中进行赋值。我们…

Junit4+MultiThreadedTestRunner 并发测试

目录 前言&#xff1a; 具体步骤&#xff1a; 前言&#xff1a; 在进行软件测试时&#xff0c;我们需要确保应用程序在不同的并发情况下仍能正常运行。 最近要对一个类里的方法&#xff0c;进行压力测试。下面讲一下写出的 Junit4 的并发测试代码吧。如果要复用的话&#x…

通达信一看就懂的成交量指标公式_通达信公式

用法说明&#xff1a;黄色为立桩量&#xff0c;绿色长竖条为逃顶提示&#xff0c;蓝色与红箭头为突破&#xff0c;红色为牛&#xff0c;紫色为立桩三天法则成立&#xff0c;笑脸为寻底&#xff0c;棕色与绿箭头为破位&#xff0c;淡红色黑中线涨停&#xff0c;深绿色黑中线跌停…

yolov7裂缝检测

B站视频笔记。 1.首先到Github上找RoboFlow的仓库地址 该教程提供了传统算法比如Resnet、YOLO等&#xff0c;还有包含一些较新的算法。 2.通过Colab打开例程 可以直接通过Colab打开,还支持其他的打开方式&#xff0c;这里提供三种方式。 提示&#xff1a;点击授权即可。 …

dy设备deviceid iid注册分析

清楚缓存&#xff0c;重新打开app, 点击同意按钮&#xff0c;会触发设备注册&#xff1b; 很明显是一个post包&#xff0c;device_register 可以看到请求体加密了 那么 请求体是什么呢&#xff1f; 很老版本思路&#xff1a;都是直接明文注册 较老版本思路&#xff1a;在反编译…

二、SQL-5.DQL-8).案例练习

1、查询年龄为20,21,22,23岁的员工信息 select * from emp where age in(20, 21, 22, 23) and gender 女; 2、查询性别为男&#xff0c;并且年龄在20-40岁&#xff08;含&#xff09;以内的姓名为三个字的员工 select * from emp where gender 男 && age between 2…