提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
现有一些电线,有管径、材质、长度三个属性,一次遍历可以实现相同管径和材质总长分类统计吗
一、概述
可以实现,一次遍历完全能完成按相同管径和材质分类统计总长的需求。
核心逻辑是在遍历过程中,同时以"管径+材质"为组合条件进行分组,并实时累加每组的长度,无需多次遍历数据。
实现的关键逻辑
-
选择合适的数据结构
需使用一个能存储"键-值"对的结构,其中键(Key) 是"管径+材质"的组合(如元组形式),值(Value) 是该组合对应的累计总长。常见的结构包括Python中的字典(dict)、Java中的HashMap等。
-
单次遍历中的操作步骤
遍历每条线时,执行以下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;
}
代码说明:
-
数据结构选择 :
使用
std::map作为存储容器,键类型为std::pair<int, std::string>(组合管径和材质),值类型为double(存储总长度)。map会自动根据键的顺序排序,若无需排序可改用std::unordered_map(需自定义哈希函数,稍复杂)。 -
核心逻辑 :
遍历所有线材时,通过
std::make_pair生成"管径+材质"的唯一键,直接对map中该键对应的值做累加操作(map会自动处理"键不存在时初始化"的逻辑),全程仅遍历一次数据。 -
灵活性 :
若管径为小数(如
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;
}
代码说明:
-
数据结构选择 :
使用
std::map<std::string, double>,键为材质字符串(如"铜"、"铝"),值为该材质的累计总长度。若无需按材质排序,可改用std::unordered_map(效率更高,无需排序逻辑)。 -
核心逻辑 :
遍历每条线材时,直接以
wire.material作为键,对map中对应的值做累加。map会自动处理"键不存在"的情况(初始值为0,再累加当前长度),全程仅需一次遍历。 -
结果验证 :
示例数据中:
- 铜的总长度 = 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)的大小关系决定。
具体说明:
-
std::map的排序特性
std::map是一种有序关联容器 ,它内部通过红黑树(一种平衡二叉搜索树)实现,会自动根据键的大小对元素进行排序。默认情况下,排序规则由std::less<Key>决定(即"小于"比较),对于std::string类型的键,就是按照字典序(lexicographical order) 排序的。例如,对于字符串键
"铝"和"铜",在字典序中(取决于字符编码,通常中文按Unicode码点比较),"铜"会排在"铝"前面(实际编码中'铜'的Unicode值小于'铝'),因此std::map会将"铜"对应的键值对放在"铝"之前,无论原始数据中哪个材质先出现。 -
与原始数据顺序的关系
std::map的元素顺序不受插入顺序影响 。无论你先插入"铝"还是先插入"铜",最终map中都会按照键的排序规则重新排列。举例:
若原始数据中材质出现的顺序是
铝 → 铜 → 铝 → 铁,插入std::map后,最终顺序会按字典序排列(例如铜 → 铝 → 铁,具体取决于字符串实际比较结果),与原始插入顺序无关。 -
如何避免排序?
若需要保持与插入顺序相关的"无序"状态(或不需要排序以提高性能),可以使用
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++标准库预定义的,其排序规则为:
- 优先比较
pair的第一个元素 (此处为int类型的管径):
若两个pair的第一个元素(管径)不相等,则直接根据第一个元素的大小决定顺序(小的在前)。 - 若第一个元素相等,再比较第二个元素 (此处为
std::string类型的材质):
此时按第二个元素(字符串)的字典序(std::string的operator<规则,通常是字符编码的顺序)决定顺序。
举例说明
假设有以下std::pair<int, std::string>类型的键:
(5, "铜")、(5, "铝")、(8, "铝")、(3, "铁")
排序步骤:
- 先比较第一个元素(管径):
- 3 < 5 < 8,因此
(3, "铁")排在最前,(8, "铝")排在最后。
- 3 < 5 < 8,因此
- 对于管径相同的
(5, "铜")和(5, "铝"),比较第二个元素(材质字符串):- 字符串比较按字符编码(如Unicode),假设
"铜"的编码值小于"铝",则(5, "铜")排在(5, "铝")之前。
- 字符串比较按字符编码(如Unicode),假设
最终排序结果为:
(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类型的材质字典序排列。
排序结果与元素的插入顺序无关,仅由键的两个元素的大小关系决定。