list 基本概念

list链表是一种在物理存储上非连续,逻辑连续的存储结构,不同节点之间通过指针链接

链表的组成:由一系列node节点组成

node节点的组成:

  • 数据域:存放数据元素
  • 指针域:存放下一个node的地址

STL中的链表是一个双向循环链表,因此

  • 每个node的指针域存放两个指针,prv指向前一个节点,next指向后一个节点
  • 头节点的prv指向尾节点,尾节点的next指向头节点,形成循环
push_front                                     push_back
      front()                               back()
       ------       ------      ------      ------ 
      | data |     | data |    | data |    | data |
      |------|     |------|    |------|    |------|
      | prv  | ↘↗| prv  | ↘↗| prv  |↘↗| prv  |
     -| next | ↗↘| next | ↗↘| next |↗↘| next |--
     | ------       ------      ------      ------  |
     |-------------------------------------------
     ---------------------------------------------↑
pop_front                                       pop_back
        begin()            insert()                  end()

由于链表的内存存储并不是连续的内存空间,只能通过指针来向前或向后查询,无法随机读取,因此链表的迭代器为 双向迭代器

list优点:

  • 添加数据只需要申请必要的大小,不会造成内存浪费和溢出
  • 链表执行插入和删除操作很方便,只需要修改指针而不用搬运数据

list缺点:

  • 无法随机访问,空间(存放指针)和时间(指针间接寻址遍历)额外开销大

注意:list有一个重要的性质,插入和删除的操作不会使原来的迭代器失效,这在vector中是不成立的(元素移动,新空间移动)

STL中list和vector都是非常常用的,各有优缺点

list 构造函数

  • list<T> lst; // 默认构造函数

  • list(beg, end); // 构造函数将[beg, end)区间中的元素拷贝给本身

  • list(n, elem); // 构造函数将n个elem拷贝给本身。

  • list(const list &lst); // 拷贝构造函数

  • 示例

    // list的构造函数
    /**
     * list<T> lst;             // 默认构造函数
     * list(beg, end);          // 构造函数将[beg, end)区间中的元素拷贝给本身。
     * list(n, elem);           // 构造函数将n个elem拷贝给本身。
     * list(const list &lst);   // 拷贝构造函数
     */
    #include <iostream>
    #include <list>
    
    using namespace std;
    
    void print_list(const list<int>& l)
    {
        for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
        {
            cout << *it << " ";
        }
        cout << endl;
    }
    
    void test0()
    {
        // 1. 默认构造
        list<int> l1;
        l1.push_back(10);
        l1.push_back(20);
        l1.push_back(30);
        l1.push_back(40);
        cout << "l1 = ";
        print_list(l1);
    
        // 2. 将[beg, end)区间中的元素拷贝给本身。
        list<int> l2(l1.begin(), l1.end());
        cout << "l2 = ";
        print_list(l2);
    
        // 3. 构造函数将n个elem拷贝给本身。
        list<int> l3(3, 66);
        cout << "l3 = ";
        print_list(l3);
    
        // 4. 拷贝构造
        list<int> l4(l3);
        cout << "l4 = ";
        print_list(l4);
    }
    
    int main()
    {
        test0();
        return 0;
    }
    

list 赋值和交换

  • list& operator=(const list &lst); // 重载等号操作符

  • assign(beg, end); // 将[beg, end)区间中的数据拷贝赋值给本身。

  • assign(n, elem); // 将n个elem拷贝赋值给本身。

  • swap(lst); // 与另一个list交换

  • 示例

    // list的赋值和交换
    /**
     * list& operator=(const list &lst);    // 重载等号操作符
     * assign(beg, end);                    // 将[beg, end)区间中的数据拷贝赋值给本身。
     * assign(n, elem);                     // 将n个elem拷贝赋值给本身。
     * swap(lst);                           // 将lst与本身的元素互换。
     */
    #include <iostream>
    #include <list>
    
    using namespace std;
    
    void print_list(const list<int>& l)
    {
        for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
        {
            cout << *it << " ";
        }
        cout << endl;
    }
    
    void test0()
    {
        list<int> l1;
        // 1. assign(n, elem)
        l1.assign(5, 66);
        cout << "l1 = ";
        print_list(l1);
    
        list<int> l2;
        // 2. assign(beg, end)
        l2.assign(l1.begin(), l1.end());
        cout << "l2 = ";
        print_list(l2);
    
        list<int> l3;
        // 3. operator=
        l3 = l1;
        cout << "l3 = ";
        print_list(l3);
    
        list<int> l4;
        // 4. assign(n, elem)
        l4.assign(3, 88);
        cout << "l4 = ";
        print_list(l4);
        
        // 5. swap
        l1.swap(l4);
        cout << "after swap" << endl;
        cout << "l1 = ";
        print_list(l1);
        cout << "l4 = ";
        print_list(l4);
        
    }
    
    int main()
    {
        test0();
        return 0;
    }
    

list 大小操作

  • size(); // 返回容器中元素的个数

  • empty(); // 判断容器是否为空

  • resize(num); // 重新指定容器的长度为num,若容器变长,则以默认值填充新位置。

  • resize(num, elem); // 重新指定容器的长度为num,若容器变长,则以elem值填充新位置。

  • 示例

    // list的大小操作
    /**
     * size();              // 返回容器中元素的个数
     * empty();             // 判断容器是否为空
     * resize(num);         // 重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
     * resize(num, elem);   // 重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
     * 
     */
    #include <iostream>
    #include <list>
    
    using namespace std;
    
    void print_list(const list<int>& l)
    {
        for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
        {
            cout << *it << " ";
        }
        cout << endl;
    }
    
    void test0()
    {
        list<int> l1;
        l1.push_back(10);
        l1.push_back(20);
        l1.push_back(30);
        l1.push_back(40);
        cout << "the size of l1 is: " << l1.size() << endl;
        cout << "l1 = ";
        print_list(l1);
    
        // 1. resize(num)
        l1.resize(10);
        cout << "the size of l1 is: " << l1.size() << endl;
        cout << "l1 = ";
        print_list(l1);
    
        // 2. resize(num, elem)
        l1.resize(15, 100);
        cout << "the size of l1 is: " << l1.size() << endl;
        cout << "l1 = ";
        print_list(l1);
    }
    
    int main()
    {
        test0();
        return 0;
    }
    

list 插入和删除

  • push_back(elem); // 尾插

  • pop_back(); // 尾删

  • push_front(elem); // 头插

  • pop_front(); // 头删

  • insert(pos, elem); // 在pos位置插入elem元素的拷贝,返回新数据的位置。

  • insert(pos, n, elem); // 在pos位置插入n个elem元素的拷贝,返回新数据的位置。

  • insert(pos, beg, end); // 在pos位置插入[beg, end)区间中的数据,返回新数据的位置

  • erase(beg, end); // 删除[beg, end)区间中的数据,返回下一个数据的位置。

  • erase(pos); // 删除pos位置的数据,返回下一个数据的位置。

  • remove(elem); // 删除容器中所有与elem值匹配的元素。

  • clear(); // 清空

  • 示例

    // list的插入和删除
    /**
     * push_back(elem);             // 尾插
     * pop_back();                  // 尾删
     * push_front(elem);            // 头插
     * pop_front();                 // 头删
     * insert(pos, elem);           // 在pos位置插入elem元素的拷贝,返回新数据的位置。
     * insert(pos, n, elem);        // 在pos位置插入n个elem元素的拷贝,返回新数据的位置。
     * insert(pos, beg, end);       // 在pos位置插入[beg, end)区间中的数据,返回新数据的位置。
     * erase(beg, end);             // 删除[beg, end)区间中的数据,返回下一个数据的位置。
     * erase(pos);                  // 删除pos位置的数据,返回下一个数据的位置。
     * remove(elem);                // 删除容器中所有与elem值匹配的元素。
     * clear();                     // 清空
     */
    #include <iostream>
    #include <list>
    
    using namespace std;
    
    void print_list(const list<int>& l)
    {
        for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
        {
            cout << *it << " ";
        }
        cout << endl;
    }
    
    void test0()
    {
        list<int> l1;
        l1.push_back(20);
        l1.push_back(20);
        l1.push_back(30);
        l1.push_front(100);
        l1.push_front(200);
        l1.push_front(300);
        cout << "l1 = ";
        print_list(l1);
    
        // 2. pop_back
        l1.pop_back();
        l1.pop_front();
        cout << "l1 = ";
        print_list(l1);
    
        // 3. insert(pos, elem)
        l1.insert(l1.begin(), 1000);
        cout << "l1 = ";
        print_list(l1);
    
        // 4. insert(pos, n, elem)
        l1.insert(l1.end(), 2, 2000);
        cout << "l1 = ";
        print_list(l1);
    
        // 5. erase(pos)
        l1.erase(l1.begin());
        cout << "l1 = ";
        print_list(l1);
    
        // 6. remove(elem)
        l1.remove(20);
        cout << "l1 = ";
        print_list(l1);
    
        // 7. clear()
        l1.clear();
        cout << "l1 = ";
        print_list(l1); 
    }
    
    int main()
    {
        test0();
        return 0;
    }
    

list 数据存取

  • front(); // 返回第一个元素。

  • back(); // 返回最后一个元素。

  • 不可以使用at和[ ],因为list不支持随机访问。

  • 示例

    // list的数据存取
    /**
     * front(); // 返回第一个元素。
     * back(); // 返回最后一个元素。
     * 不可以使用at和[],因为list不支持随机访问。
     */
    #include <iostream>
    #include <list>
    
    using namespace std;
    
    void test0()
    {
        list<int> l1;
        l1.push_back(10);
        l1.push_back(20);
        l1.push_back(30);
        l1.push_front(100);
        l1.push_front(200);
        l1.push_front(300);
    
        // 1. front()
        cout << "the first element of l1 is: " << l1.front() << endl;
    
        // 2. back()
        cout << "the last element of l1 is: " << l1.back() << endl;
    
        // 3. at(can not be used)
        // cout << "the first element of l1 is: " << l1.at(0) << endl;
    
        // 4. [](can not be used)
        // cout << "the first element of l1 is: " << l1[0] << endl;
    }
    
    int main()
    {
        test0();
        return 0;
    }
    

list 反转和排序

  • reverse(); // 反转

  • sort(); // 排序 - 成员函数

  • 对于不支持随机访问的迭代器,容器会提供相应的排序成员函数

  • 默认升序,可以通过传入回调函数,实现降序排序

  • 对于自定义数据类型,标准库并没有提供默认实现,需要传入回调函数,实现自定义的排序规则(见下一节自定义排序)

  • 示例

    // list的反转和排序
    /**
     * reverse();   // 反转
     * sort();      // 排序 - 成员函数
     * 对于不支持随机访问的迭代器,容器会提供相应的排序成员函数
     * 默认升序,可以通过传入回调函数,实现降序排序
     * 对于自定义数据类型,标准库并没有提供默认实现,需要传入回调函数,实现自定义的排序规则(见07_list自定义排序)
     */
    #include <iostream>
    #include <list>
    
    using namespace std;
    
    bool my_compare(int num1, int num2)
    {
        return num1 > num2;
    }
    
    void print_list(const list<int>& l)
    {
        for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
        {
            cout << *it << " ";
        }
        cout << endl;
    }
    
    void test0()
    {
        list<int> l1;
        l1.push_back(10);
        l1.push_back(20);
        l1.push_back(30);
        l1.push_front(100);
        l1.push_front(200);
        l1.push_front(300);
        cout << "l1 = ";
        print_list(l1);
    
        // reverse
        l1.reverse();
        cout << "after reverse, l1 = ";
        print_list(l1);
    
        // sort
        l1.sort();
        cout << "after sort, l1 = ";
        print_list(l1);
    
        // 降序 sort
        l1.sort(my_compare);
        cout << "after sort(my_compare), l1 = ";
        print_list(l1);
    
    }
    
    int main()
    {
        test0();
        return 0;
    }
    

list 自定义数据排序

  • bool compare(const Person& p1, const Person& p2); // 比较函数

  • 对于自定义数据类型,标准库并没有提供默认实现,需要传入回调函数,实现自定义的排序规则

  • 案例描述:将Person自定义数据类型进行排序,Person中属性有姓名,年龄,身高

  • 排序规则:按照年龄进行升序,如果年龄相同按照身高进行降序

  • 示例

    // list自定义数据排序
    /**
     * bool compare(const Person& p1, const Person& p2); // 比较函数
     * 对于自定义数据类型,标准库并没有提供默认实现,需要传入回调函数,实现自定义的排序规则
     * 案例描述:将Person自定义数据类型进行排序,Person中属性有姓名,年龄,身高
     * 排序规则:按照年龄进行升序,如果年龄相同按照身高进行降序
     */
    #include <iostream>
    #include <list>
    
    using namespace std;
    
    class person
    {
    public:
        person(string name, int age, int height) : m_name(name), m_age(age), m_height(height) {}
        
        string m_name;
        int m_age;
        int m_height;
    };
    
    void print_list(const list<person>& l)
    {
        for (list<person>::const_iterator it = l.begin(); it != l.end(); it++)
        {
            cout << "name: " << (*it).m_name << " age: " << (*it).m_age << " height: " << (*it).m_height << endl;
        }
        cout << "--------------------------------" << endl;
    }
    
    bool my_compare(const person &p1, const person &p2)
    {
        // 先判断年龄 年龄一致则判断身高
        if (p1.m_age != p2.m_age)
        {
            // 年龄升序
            return p1.m_age < p2.m_age;
        }
        else
        {
            // 年龄降序
            return p1.m_height > p2.m_height;
        }
    }
    
    void test0()
    {
        list<person> l;
        // push
        l.push_back(person("Mask", 18, 180));
        l.push_back(person("Jack", 20, 175));
        l.push_back(person("Rose", 19, 170));
        l.push_back(person("Lily", 18, 160));
        l.push_back(person("Lucy", 18, 165));
        print_list(l);
        
        // sort
        l.sort(my_compare);
        print_list(l);
    }
    
    int main()
    {
        test0();
        return 0;
    }
    

更多推荐