21天学通C++读书笔记(十八:STL 集合类)

1. 简介

  • 容器set 和multiset 让程序员能够在容器中快速查找键,键是存储在一维容器中的值
  • set 和multiset之间的区别在于,后者可存储重复的值,而前者只能存储唯一的值
  • 为实现快速搜索,set 和multiset 的内部结构像二叉树,这意味着将元素插入到set 或multiset时将对其进行排序,以提高查找速度 (插入时对元素进行排序,默认升序排列)
  • 位于set 中特定位置的元素不能替换为值不同的新元素,这是因为set 将把新元素同内部树中的其他元素进行比较,进而将其放在其他位置

2. STL set 和multiset 的基本操作

2.1 实例化std::set 对象
#include <iostream>
#include <set>

// 二元降序排序谓词
template <typename T>
struct SortDescending {
    bool operator()(const T& lhs, const T& rhs) const {
        return (lhs > rhs);
    }
};

int main () {
    using namespace std;
    
    // 使用默认排序谓词,即std::less<T>
    set<int> setInts1;
    multiset<int> msetInts1;
    
    // 二元降序排序
    set<int, SortDescending<int> > setInts2;
    multiset<int, SortDescending<int> > msetInts2;
    
    set<int> setInts3(setInts1);
    multiset<int> msetInts3(setInts1.cbegin(), setInts1.cend()); 
    return 0;
}
2.2 在set 或multiset 中插入元素
  • 要在这两种容器中插入元素,都可使用成员函数insert(),这个函数接受要插入的值或容器的指定范围
  • mulitset::count()确定multiset 包含多少个这样的元素
setInts.insert (-1);
msetInts.insert (setInts.begin (), setInts.end ());
2.3 在STL set 或multiset 中查找元素
auto elementFound = setInts.find (-1);
// Check if found...
if (elementFound != setInts.end ()) {
    cout << "Element " << *elementFound << " found!" << endl;
} else {
    cout << "Element not found in set!" << endl;
}
2.4 删除STL set 或multiset 中的元素
#include <set>
#include <iostream>
using namespace std;
   
template<typename T>
void DisplayContents (const T& Input) {
    for (auto element = Input.cbegin(); element != Input.cend (); ++element) {
        cout << *element << ' ';
    }
    cout << endl;
}

typedef multiset<int> MSETINT;

int main () {
    MSETINT msetInts{ 43, 78, 78, -1, 124 };
    
    cout << "multiset contains " << msetInts.size () << " elements: ";
    DisplayContents(msetInts);
    
    cout << "Enter a number to erase from the set: ";
    int input = 0;
    cin >> input;
    
    cout << "Erasing " << msetInts.count(input); // 使用了count()来确定有多少个元素包含特定的值
    cout << " instances of value " << input << endl;
    msetInts.erase(input);
    
    cout << "multiset now contains " << msetInts.size() << " elements: ";
    DisplayContents(msetInts);
    return 0;
}
2.5 使用set 容器的实际应用
  • 基于菜单的电话簿的简单实现,它让用户能够插入、查找、删除和显示人名和电话号码
  • std::set 排序是在插入元素时进行的
#include <set>
#include <iostream>
#include <string>
using namespace std;

template<typename T>
void DisplayContents(const T& container) {
    for (auto iElement = container.cbegin();
         iElement != container.cend();
         ++iElement) {
        cout << *iElement << endl;
    }
    cout << endl;
}

struct ContactItem
{
    string name;
    string phoneNum;
    string displayAs;

    ContactItem(const string& nameInit, const string& phone) {
        this->name = nameInit;
        this->phoneNum = phone;
        this->displayAs = (name + ": " + phoneNum);
    }
    // 利用set::find()找到给定联系人列表项
    bool operator == (const ContactItem& itemToCompare) const {
        return (this->name < itemToCompare.name);
    }
    // 用于排序
    bool operator < (const ContactItem& itemToCompare) const {
        return (this->name < itemToCompare.name);
    }
    operator const char*() const {
        return displayAs.c_str();
    }
};

int main () {
    set<ContactItem> setContacts;
    setContacts.insert(ContactItem("Jack Welsch", "+1 7889 879 879"));
    setContacts.insert(ContactItem("Bill Gates", "+1 97 7897 8799 8"));
    setContacts.insert(ContactItem("Angi Merkel", "+49 23456 5466"));
    setContacts.insert(ContactItem("Vlad Putin", "+7 6645 4564 797"));
    setContacts.insert(ContactItem("John Travolta", "91 234 4564 789"));
    setContacts.insert(ContactItem("Ben Affleck", "+1 745 641 314"));
    DisplayContents(setContacts);

    cout << "Enter a name you wish to delete: ";
    string inputName;
    getline(cin, inputName);

    auto contactFound = setContacts.find(ContactItem(inputName, ""));
    if (contactFound != setContacts.end()) {
        setContacts.erase(contactFound);
        cout << "Displaying contents after erasing " << inputName << endl;
        DisplayContents(setContacts);
    } else {
        cout << "Contact not found" << endl;
    }
    return 0; 
}

3. 使用STL set 和multiset 的优缺点

  • 优点
    • 对需要频繁查找的应用程序来说,STL set 和multiset 很有优势,因为其内容是经过排序的,因此查找速度更快
  • 缺点
    • 容器在插入元素时进行排序,因此插入元素时有额外开销,因为需要对元素进行排序
    • 在vector 中,可以使用新值替换迭代器(如std::find()返回的迭代器)指向的元素;但set 根据元素的值对其进行了排序,因此不能使用迭代器覆盖元素的值

4. STL 散列集合实现std::unordered_set 和std::unordered_multiset

  • 使用基于散列的实现插入和排序时间固定的方式,使用散列函数来计算排序索引
  • 将元素插入散列集合时,首先使用散列函数计算出一个唯一的索引,再根据该索引决定将元素放到哪个桶(bucket)中
#include <unordered_set>
#include <iostream>
using namespace std;
   
template <typename T>
void DisplayContents(const T& cont) {
    for (auto element = cont.cbegin(); element != cont.cend(); ++element) {
        cout<< *element << ' ';
    }
    cout << endl;
    
    cout << "Number of elements, size() = " << cont.size() << endl;
    cout << "Bucket count = " << cont.bucket_count() << endl;
    cout << "Max load factor = " << cont.max_load_factor() << endl;
    cout << "Load factor: " << cont.load_factor() << endl << endl;
}

int main() {
    unordered_set<int> usetInt{ 1, -3, 2017, 300, -1, 989, -300, 9 };
    DisplayContents(usetInt);
    usetInt.insert(999);
    DisplayContents(usetInt);
    
    cout << "Enter int you want to check for existence in set: ";
    int input = 0;
    cin >> input;
    auto elementFound = usetInt.find(input);
    
    if (elementFound != usetInt.end()) {
        cout << *elementFound << " found in set" << endl;
    } else {
        cout << input << " not available in set" << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值