【map应用】组合键统计

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


现有一些电线,有管径、材质、长度三个属性,一次遍历可以实现相同管径和材质总长分类统计吗

一、概述

可以实现,一次遍历完全能完成按相同管径和材质分类统计总长的需求。

核心逻辑是在遍历过程中,同时以"管径+材质"为组合条件进行分组,并实时累加每组的长度,无需多次遍历数据。

实现的关键逻辑

  1. 选择合适的数据结构

    需使用一个能存储"键-值"对的结构,其中键(Key) 是"管径+材质"的组合(如元组形式),值(Value) 是该组合对应的累计总长。常见的结构包括Python中的字典(dict)、Java中的HashMap等。

  2. 单次遍历中的操作步骤

    遍历每条线时,执行以下3步操作,全程仅扫描一次数据:

    • 提取当前线的"管径"和"材质",组合成唯一键。
    • 检查该键是否已存在于数据结构中。
    • 若存在,将当前线的长度累加到对应的值上;若不存在,新增该键,并将当前线的长度设为初始值。

示例场景(以Python为例)

假设线材数据格式为列表,每个元素是包含"管径、材质、长度"的字典,代码逻辑如下:

python 复制代码
# 模拟线材数据
wires = [
    {"管径": 5, "材质": "铜", "长度": 10},
    {"管径": 5, "材质": "铜", "长度": 20},
    {"管径": 8, "材质": "铝", "长度": 15},
    {"管径": 5, "材质": "铝", "长度": 5}
]

# 用于统计的字典,键为(管径, 材质),值为总长
total_length = {}

# 一次遍历完成统计
for wire in wires:
    key = (wire["管径"], wire["材质"])  # 组合键
    if key in total_length:
        total_length[key] += wire["长度"]  # 累加长度
    else:
        total_length[key] = wire["长度"]  # 初始化新组

# 输出结果:{(5, '铜'): 30, (8, '铝'): 15, (5, '铝'): 5}
print(total_length)

二、组合统计

以下是C++实现代码,使用std::map存储"管径+材质"组合与总长度的映射关系,通过一次遍历完成统计:

cpp 复制代码
#include <iostream>
#include <vector>
#include <map>
#include <string>

// 定义线材结构体,包含管径、材质、长度三个属性
struct Wire {
    int diameter;    // 管径(假设为整数,如需小数可改为double)
    std::string material;  // 材质
    double length;   // 长度(支持小数)
};

int main() {
    // 模拟待统计的线材数据
    std::vector<Wire> wires = {
        {5, "铜", 10.5},
        {5, "铜", 20.3},
        {8, "铝", 15.0},
        {5, "铝", 5.7},
        {8, "铝", 8.2},
        {5, "铜", 12.1}
    };

    // 用于统计的映射表:键为(管径, 材质)组合,值为该组合的总长度
    std::map<std::pair<int, std::string>, double> totalLengthMap;

    // 一次遍历完成统计
    for (const auto& wire : wires) {
        // 构建组合键(管径+材质)
        auto key = std::make_pair(wire.diameter, wire.material);
        // 累加长度(若键不存在,会自动初始化并赋值)
        totalLengthMap[key] += wire.length;
    }

    // 输出统计结果
    std::cout << "统计结果(管径, 材质):总长度" << std::endl;
    for (const auto& entry : totalLengthMap) {
        std::cout << "(" << entry.first.first << ", " << entry.first.second 
                  << "):" << entry.second << std::endl;
    }

    return 0;
}

代码说明:

  1. 数据结构选择

    使用std::map作为存储容器,键类型为std::pair<int, std::string>(组合管径和材质),值类型为double(存储总长度)。map会自动根据键的顺序排序,若无需排序可改用std::unordered_map(需自定义哈希函数,稍复杂)。

  2. 核心逻辑

    遍历所有线材时,通过std::make_pair生成"管径+材质"的唯一键,直接对map中该键对应的值做累加操作(map会自动处理"键不存在时初始化"的逻辑),全程仅遍历一次数据。

  3. 灵活性

    若管径为小数(如8.5mm),可将Wire中的diameter改为double,同时修改map的键类型为std::pair<double, std::string>即可。

输出结果:

复制代码
统计结果(管径, 材质):总长度
(5, 铜):42.9
(5, 铝):5.7
(8, 铝):23.2

(结果为示例数据中相同管径和材质的长度累加值)

三、单属性统计

以下是按材质分类统计总长度的C++代码,核心逻辑仍是一次遍历 ,通过std::map(或std::unordered_map)以"材质"为键存储总长度,实现高效统计:

cpp 复制代码
#include <iostream>
#include <vector>
#include <map>
#include <string>

// 线材结构体(同之前定义,包含管径、材质、长度)
struct Wire {
    int diameter;    // 管径(可按需改为double)
    std::string material;  // 材质
    double length;   // 长度
};

int main() {
    // 模拟线材数据
    std::vector<Wire> wires = {
        {5, "铜", 10.5},
        {5, "铜", 20.3},
        {8, "铝", 15.0},
        {5, "铝", 5.7},
        {8, "铝", 8.2},
        {5, "铜", 12.1}
    };

    // 统计映射表:键为材质,值为该材质的总长度
    std::map<std::string, double> materialTotalLength;

    // 一次遍历完成统计
    for (const auto& wire : wires) {
        // 以材质为键,累加长度(键不存在时自动初始化为0)
        materialTotalLength[wire.material] += wire.length;
    }

    // 输出统计结果
    std::cout << "各材质总长度统计:" << std::endl;
    for (const auto& entry : materialTotalLength) {
        std::cout << entry.first << ":" << entry.second << std::endl;
    }

    return 0;
}

代码说明:

  1. 数据结构选择

    使用std::map<std::string, double>,键为材质字符串(如"铜"、"铝"),值为该材质的累计总长度。若无需按材质排序,可改用std::unordered_map(效率更高,无需排序逻辑)。

  2. 核心逻辑

    遍历每条线材时,直接以wire.material作为键,对map中对应的值做累加。map会自动处理"键不存在"的情况(初始值为0,再累加当前长度),全程仅需一次遍历。

  3. 结果验证

    示例数据中:

    • 铜的总长度 = 10.5 + 20.3 + 12.1 = 42.9
    • 铝的总长度 = 15.0 + 5.7 + 8.2 = 28.9

输出结果:

复制代码
各材质总长度统计:
铜:42.9
铝:28.9

如需调整数据类型(如管径为小数、长度为整数),只需修改Wire结构体中对应字段的类型即可,统计逻辑无需变动。

四、说明一

c 复制代码
std::map<std::string, double> materialTotalLength;
std::map会排序吗,构建出map的顺序和原始数据是不是有关系

在C++中,std::map会自动排序 的,其内部元素的顺序与原始数据的插入顺序无关 ,仅由键(key)的大小关系决定。

具体说明:

  1. std::map的排序特性
    std::map是一种有序关联容器 ,它内部通过红黑树(一种平衡二叉搜索树)实现,会自动根据键的大小对元素进行排序。默认情况下,排序规则由std::less<Key>决定(即"小于"比较),对于std::string类型的键,就是按照字典序(lexicographical order) 排序的。

    例如,对于字符串键"铝""铜",在字典序中(取决于字符编码,通常中文按Unicode码点比较),"铜"会排在"铝"前面(实际编码中'铜'的Unicode值小于'铝'),因此std::map会将"铜"对应的键值对放在"铝"之前,无论原始数据中哪个材质先出现。

  2. 与原始数据顺序的关系
    std::map的元素顺序不受插入顺序影响 。无论你先插入"铝"还是先插入"铜",最终map中都会按照键的排序规则重新排列。

    举例:

    若原始数据中材质出现的顺序是 铝 → 铜 → 铝 → 铁,插入std::map后,最终顺序会按字典序排列(例如 铜 → 铝 → 铁,具体取决于字符串实际比较结果),与原始插入顺序无关。

  3. 如何避免排序?

    若需要保持与插入顺序相关的"无序"状态(或不需要排序以提高性能),可以使用std::unordered_map。它是无序关联容器,内部通过哈希表实现,元素顺序不固定(通常与插入顺序无关,但也不保证排序),且插入/查找效率更高(平均O(1))。

代码示例验证:

cpp 复制代码
#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> myMap;

    // 按 "铝" → "铜" → "铁" 的顺序插入
    myMap["铝"] = 1;
    myMap["铜"] = 2;
    myMap["铁"] = 3;

    // 输出map中的元素顺序(按键的字典序)
    std::cout << "std::map 中的顺序:" << std::endl;
    for (const auto& pair : myMap) {
        std::cout << pair.first << " "; // 输出:铜 铁 铝(取决于实际编码的字典序)
    }

    return 0;
}

输出结果中,元素顺序由"铝""铁""铜"的字典序决定,而非插入时的"铝" → "铜" → "铁"

总结:

  • std::map会自动排序,顺序由键的比较规则(默认字典序)决定。
  • 排序结果与原始数据的插入顺序无关
  • 若需无序/与插入顺序弱相关的容器,优先使用std::unordered_map

五、说明二

c 复制代码
std::map<std::pair<int, std::string>, double> totalLengthMap;又是怎样排序的

std::map<std::pair<int, std::string>, double>的排序规则由其键类型std::pair<int, std::string>的比较逻辑决定,遵循字典序(lexicographical order) 排序,具体规则如下:

核心排序逻辑:先比第一个元素,再比第二个元素

std::pair的比较运算符(operator<)是C++标准库预定义的,其排序规则为:

  1. 优先比较pair的第一个元素 (此处为int类型的管径):
    若两个pair的第一个元素(管径)不相等,则直接根据第一个元素的大小决定顺序(小的在前)。
  2. 若第一个元素相等,再比较第二个元素 (此处为std::string类型的材质):
    此时按第二个元素(字符串)的字典序(std::stringoperator<规则,通常是字符编码的顺序)决定顺序。

举例说明

假设有以下std::pair<int, std::string>类型的键:
(5, "铜")(5, "铝")(8, "铝")(3, "铁")

排序步骤:

  1. 先比较第一个元素(管径):
    • 3 < 5 < 8,因此(3, "铁")排在最前,(8, "铝")排在最后。
  2. 对于管径相同的(5, "铜")(5, "铝"),比较第二个元素(材质字符串):
    • 字符串比较按字符编码(如Unicode),假设"铜"的编码值小于"铝",则(5, "铜")排在(5, "铝")之前。

最终排序结果为:
(3, "铁")(5, "铜")(5, "铝")(8, "铝")

本质原因

std::map默认使用std::less<Key>作为比较器,而std::less<Key>会调用Key类型的operator<。对于std::pair<T1, T2>,标准库已实现operator<,逻辑就是上述的"先比第一个元素,再比第二个元素"的字典序。

总结

std::map<std::pair<int, std::string>, double>的排序与键的两个元素都相关:

  • 先按int类型的管径升序排列
  • 管径相同的,再按std::string类型的材质字典序排列

排序结果与元素的插入顺序无关,仅由键的两个元素的大小关系决定。

相关推荐
txinyu的博客2 小时前
解析muduo源码之 TimeZone.h & TimeZone.cc
linux·服务器·网络·c++
爱吃生蚝的于勒2 小时前
【Linux】零基础学习命名管道-共享内存
android·linux·运维·服务器·c语言·c++·学习
陳10302 小时前
C++:继承
开发语言·c++
txinyu的博客2 小时前
解析muduo源码之 atomic.h
服务器·c++
xiaoye-duck2 小时前
C++ string 类使用超全攻略(下):修改、查找、获取及常见实用接口深度解析
开发语言·c++·stl
程序员老舅2 小时前
【无标题】
c++·嵌入式·八股文·c++八股文·八股文面试题·c++面经·c++面试题
码界奇点2 小时前
基于DDD与CQRS的Java企业级应用框架设计与实现
java·开发语言·c++·毕业设计·源代码管理
Frank_refuel2 小时前
C++STL之set和map的接口使用介绍
数据库·c++·算法
闻缺陷则喜何志丹2 小时前
【模拟】P9670 [ICPC 2022 Jinan R] Frozen Scoreboard|普及+
c++·算法·模拟·洛谷