美文网首页IT狗工作室
C++数据结构-关联容器

C++数据结构-关联容器

作者: 铁甲万能狗 | 来源:发表于2019-09-28 10:50 被阅读0次

map类提供了一个(排序的)关联数组。 在使用map容器之前,必须包含<map>头文件。

map用于填充了键值对,该键值对可以是任何容器接受的类型。 由于类型与键和值都相关联,因此必须在“<key,value>”符号中指定两种类型. key字段代表键的类型,value字段代表值的类型。 例如,可以如下定义其中key是string且,value是int类型的映射:

map<string,int>

键(key)用于访问它关联的值(value),例如,电话簿使用人的名字作为key,并使用电话号码以及可能的其他信息(例如,邮政编码,地址,职业)作为值.
由于map容器对键进行排序,因此必须定义键的operator <,并且必须谨慎地使用。例如,将指针作为key通常是一个坏主义,因为对指针进行排序与对这些指针所指向的值进行排序不同。 除了键,值类型外,第三个类型定义了比较类,用于比较两个键。 默认情况下,比较类是std :: less <KeyType>,使用键类型的operator <比较两个键值。 因此,对于键类型KeyType和值类型ValueType,映射的类型定义如下所示:

map<KeyType, ValueType, std::less<KeyType>>

使用类类型作为map容器的key

默认情况下,std :: map使用“ operator <”作为键的排序条件。 对于int和std :: string等默认数据类型,默认情况下,operator <是可用的,但对于用户定义的类,operator <默认情况下不可用。因此,要将用户定义的类对象用作std :: map中的键,我们应该选择其中一个:

  • 默认排序标准,即为我们的class定义的operator <
  • 应该为std::map分配外部排序条件,即可以比较用户定义类的两个对象的比较器。

下面我们用一个类似超市的结算小程序来作为本小节的示例,我们在Customer类中定义了一个map<Good, double> cart用于表示,消费者往购物车放入的商品(用Good类表示),以及对应商品的数量(用double表示)。

Customer类接口

#ifndef CUSTOMER_HH
#define CUSTOMER_HH
#include "../header/good.hh"
#include <iostream>
#include <map>
#include <string>

class Customer
{
    std::string d_fullname; //用户名
    double d_pay;           //应付金额
    double d_bonus;         //奖励积分
    double d_count;         //购买数量
    double d_discnt;        //让利总计
    std::map<Good, double> cart;

public:
    Customer(std::string, double);
    //购买
    void buy(const Good, double);
    //支付
    void payment();
    //结帐信息
    void display_info();

private:
    //折扣
    void discount(const Good &, double);
};
#endif

Good类接口

#ifndef GOOD_HH
#define GOOD_HH
#include <string>
class Good
{
    std::string d_name;
    double d_price;

public:
    Good();
    Good(std::string, double);
    double price() const;
    void set_price(double price);
    std::string name() const;

    bool operator<(const Good &obj) const;
};
#endif

方案1:重载operator <用于key的排序

示例中的Good类是具有d_price属性,类Customer的属性cart,它正是std :: map容器,我们将Good类作为cart中的key。我们将在Good类中重写将重载 operator <。如下实现所示

bool Good::operator<(const Good &obj) const
{
    if (obj.d_price < this->d_price)
    {
        return true;
    }
}

Customer接口实现

#include "../header/customer.hh"

Customer::Customer(std::string user, double bonus)
{
    d_bonus = bonus;
    d_fullname = user;
}

void Customer::buy(const Good com, double count)
{
    cart[com] = count;
}

void Customer::discount(const Good &com, double count)
{
    double curPay = com.price() * count;
    double curDis;
    if (d_bonus < 400)
    {
        curDis = curPay * 0;
    }
    else if (d_bonus <= 500)
    {
        curDis = curPay * 0.03;
    }
    else if (d_bonus <= 500)
    {
        curDis = curPay * 0.04;
    }
    else if (d_bonus <= 600)
    {
        curDis = curPay * 0.05;
    }
    else if (d_bonus <= 700)
    {
        curDis = curPay * 0.06;
    }
    else if (d_bonus <= 800)
    {
        curDis = curPay * 0.07;
    }

    d_discnt += curDis;
    d_pay += curPay;
}

void Customer::payment()
{
    std::map<Good, double>::iterator be = cart.begin();
    std::map<Good, double>::iterator ed = cart.end();
    while (be != ed)
    {
        discount(be->first, be->second);
        be++;
    }
}

void Customer::display_info()
{

    std::map<Good, double>::iterator be = cart.begin();
    std::map<Good, double>::iterator ed = cart.end();

    std::cout << std::endl;
    std::cout << "客户名:" << d_fullname << std::endl;
    std::cout << "品名"
              << "\t"
              << "单价"
              << "\t"
              << "数量"
              << "\t"
              << "小结" << std::endl;
    Good c;
    std::string name;
    double price;
    double count;

    while (be != ed)
    {
        c = be->first;
        name = c.name();
        price = c.price();
        count = be->second;
        std::cout << name << "\t" 
                << price << "\t"
                << count << "\t" 
                << price * count << std::endl;
        be++;
    }
    std::cout << "金额:" << d_pay << "RMB" << std::endl;
    std::cout << "优惠:" << d_discnt << "RMB" << std::endl;
    std::cout << "应付" << d_pay - d_discnt << "RMB" << std::endl;
    std::cout << std::endl;
}

Good类接口的实现

#include "../header/good.hh"

Good::Good(){};
Good::Good(std::string name, double price)
{
    d_name = name;
    d_price = price;
}

void Good::set_price(double value)
{
    d_price = value;
}

double Good::price() const
{
    return d_price;
}

bool Good::operator<(const Good &obj) const
{
    if (obj.d_price < this->d_price)
    {
        return true;
    }
}

std::string Good::name() const
{
    return d_name;
}

调用示例

#include "./customer.cpp"
#include "./good.cpp"
int main(int argc, char const *argv[])
{
    Good a{"香蕉", 5.7};
    Good b{"奇异果", 13.4};
    Good c{"榴莲", 13.5};
    Good d{"苹果", 5.7};

    Customer p1{"Lisa", 400};
    Customer p2{"Mary", 501};
    Customer p3{"Chey", 600};
    Customer p4{"May", 800};

    p1.buy(a, 23);
    p1.buy(c, 12);
    p1.buy(d, 32);

    p2.buy(c, 34);
    p2.buy(a, 113);
    p2.buy(b, 17);

    p1.payment();
    p2.payment();

    p1.display_info();
    p2.display_info();

    return 0;
}

程序输出

正如,我们在map容器中指定键中的类类型的operator <的数据成员比较细节,可以让程序正常输出。另外,从这个示例,可以知道map容器在日常的分类统计应用开发中,能够用简洁的调用代码就能实现。

方案2:
现在我们要更改键的排序标准,即针对Good的d_name属性,而不是d_price属性。我们这次重写bool operator(const T &left,const T &right) const,即

bool Good::operator(const Good &c1,const Good &c2) const
{
      return (c1.name()>c2.name());
}

只要将方案1的示例代码中的实现,替换为方案2中的比较器bool operator(const T &left,const T &right) const的具体实现,也可以令程序正常运行,这里就不重复展示了。

Operator[]操作

operator[]默认在“查找/创建”模式下运行,即调用时,它将尝试查找具有给定键key的元素,并且可以在以下两个方式进行操作,即

  • 如果找到任何带有该键的元素,则它将返回其值的引用。如上面的示例代码。由于返回的是一个值的引用,而引用是一个左值,因此也可通过该“[键]”的形式去修改对应键上的值。
    void Customer::buy(const Good com, double count)
    {
        cart[com] = count;
    }
    
  • 如果map中没有找到该键,则它将在map使用该键创建一个新元素,并在其value字段中分配value_type的默认值。 然后它将返回新创建的元素值的引用。

基于value字段的查找操作

默认情况下,map容器的[]操作符号可能无法满足我们的需求,那么基于的查找需求,我们就需要自行实现这个查找算法。

我们通过传递一个vector对象的引用给search_by_value()函数,将符合value参数的对应结果存放到vector容器中。另外,这个算法使用了模板技术可以应用于所有用户定义的类型。

#include <vector>

template <typename K, typename V>
bool search_by_value(std::vector<K> &vc, std::map<K, V> ct, V value)
{
    bool isFind = false;
    auto it = ct.begin();
    auto ed = ct.end();
    while (it != ed)
    {
        if (it->second == value)
        {
            isFind = true;
            vc.push_back(it->first);
        }
    }
    return isFind;
}

基于key字段的查找

上面基于operator[]的操作虽然有查找的功能,但它在没有找到对应key字段的值,会自动创建一个对应查找key字段中对象的默认值,通常这不是我们需要的结果,那么,我们应该使用map::find函数。这个find函数很简单,我就不复述这些应用示例了。

相关文章

  • C++常用容器

    C++ 有两类常用容器,分别是顺序容器和关联容器,顺序容器例如vector,list,queue,关联容器例如ma...

  • C++数据结构-关联容器

    map类提供了一个(排序的)关联数组。 在使用map容器之前,必须包含 头文件。 map用于填充了键值对,该键值对...

  • C++boolan part3_week1

    C++容器的介绍及使用 C++中的容器大致可以分为两个大类:顺序容器和关联容器。顺序容器中有包含有顺序容器适配器。...

  • C++ 关联容器

    11.1关联容器概述 关联容器有map和set两大类,map是关键字和值得映射,set是关键字的简单集合,它们分别...

  • stl

    容器: c++中有两种类型的容器:顺序容器和关联容器,顺序容器主要有:vector、list、deque等。其中v...

  • 414. Third Maximum Number

    此题主要是C++中set的使用。set关联式容器: 实现了红黑树的平衡二叉检索树的数据结构,插入元素时,它会自动调...

  • 22. C++ STL pair类模板

    在C++关联容器的基础是pair 类模板,我们先了解 STL 中的 pair 类模板,因为关联容器的一些成员函数的...

  • C++容器、类型转换、异常与文件流操作

    C++容器、类型转换、异常与文件流操作 [TOC] 容器 容器,就是用来存放东西的盒子。常用的数据结构包括:数组a...

  • C/C++容器、类型转换、异常与文件流操作

    C++容器、类型转换、异常与文件流操作 [TOC] 容器容器,就是用来存放东西的盒子。 常用的数据结构包括:数组a...

  • JNI基础 -- C++基础知识(容器)

    C++ 中有两种容器 1.序列式容器 2.关联式容器 这两种容器都在stl标准模板库中 序列式容器 序列式容器:元...

网友评论

    本文标题:C++数据结构-关联容器

    本文链接:https://www.haomeiwen.com/subject/pwsauctx.html