华为24秋招笔试0821:数据重删 |哈希表 字符串

【华为】20240821_1_数据重删

Problem:

数据重删是一种节约存储空间的技术,通常情况下,在数据存储池内是有很多重复的数据块,重删则是将这些重复的数据块找出并处理的技术。简单地说重删,就是将N份重复的数据块仅保留1份,并将N-1份数据的地址指针指向唯一的那一份。 我们输入一串存储的数据,用N表示数据个数,用K表示数据块的大小,设计一个方法判断当前数据块是否和前面的数据块有重复,两个数据块内容完全一样则表示重复,如果重复则将这个数据块删除,并且在第一个出现数据块的后面增加重复数据的计数,输出经过重删之后的数据内容。
8 输入数据的个数 2 数据块的大小 1 2 3 4 1 2 3 4 依次是数据值
注意: 输入的数据值都是正整数 1 <= K <= 10^6 1<= N <= 10^2

Solution:

Tag:哈希表 字符串
复杂度:时间 O(n) , 空间 O(n)
思路:使用getline获取完整数据流,按照空格进行切割,然后以k为步长组合数据块,组合的过程中使用hashmap记录次数
#include<bits/stdc++.h>
using namespace std;
int main()
{
	string input;
	int n, k;
	cin >> n >> k;// 读取数据块总数(n)和块大小(k)
	cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 清除输入缓冲区残留的换行符
	getline(cin, input); // 获取完整输入数据流
	// ================== 数据分割阶段 ==================
	istringstream iss(input);
	vector<string> words;
	string word;
	unordered_map<string, int> hashmap;// 存储数据块指纹及其出现次数
	// 将输入字符串按空格分割为单词列表
	while (iss >> word) {
		words.push_back(word);
	}
	// ================== 数据块处理阶段 ================== 
	vector<string> mergeGroups;// 分割后的数据块
	for (int i = 0; i < words.size(); i += k) { // 以k为步长遍历所有单词
		string group;
		for (int j = 0; j < k && i + j < words.size(); j++) {  // 合并k个单词组成数据块(处理末尾不完整块)
			group += words[i + j];
			group += " ";
		}
		if (!group.empty()) {
			group.pop_back();// 移除末尾多余的空格符
			if (hashmap.find(group) == hashmap.end()) mergeGroups.push_back(group);// 只记录首次出现的数据块,保证顺序
		}
		hashmap[group]++; // 更新数据块计数
	}
	for (int i = 0; i < mergeGroups.size(); i++) {
		// 输出格式:<唯一数据块> <重复次数>
		cout << mergeGroups[i] << ' ' << hashmap[mergeGroups[i]] << ' ';
	}
	cout << endl;
}

optimization:

unordered_map 的键为长字符串,可能导致内存碎片化,若 K 较大,可对每组的每个单词单独哈希,再合并为组哈希(如异或或加法),不过对于这道题不需要这么复杂, O(n)的复杂度够用。

Extend:

1.字符串分割 istringstream

空格分割
示例:
#include <sstream>//头文件
string input;
istringstream iss(input);
while (iss >> word) { words.push_back(word); }//使用>>运算符按空格分隔提取数据,支持自动类型转换:
int a; double b; std::string c;
iss >> a >> b >> c; // a=123, b=45.6, c="test"

利用istringstream流按空格自动分割字符串,>>原生支持空格分割,>>会自动跳过前导空格,但不会跳过尾部空格

自定义分隔符(逗号、冒号等)

string csv = "apple,banana,orange";
istringstream iss(csv);
string fruit;
while (getline(iss, fruit, ',')) {  // 指定分隔符为逗号
    std::cout << fruit << std::endl;
}
// 输出:apple\nbanana\norange

2.输入缓冲处理

缓冲机制

输入流(如cin)采用缓冲区临时存储输入数据,数据从键盘输入后首先存入缓冲区,程序再从缓冲区读取,这种机制减少了硬件交互频率,提升效率。

常见缓冲区问题场景
1.混合输入方法

使用cin >>后接getline()时,cin残留的回车符会导致getline()直接读取空字符串

int num; 
cin >> num;          // 输入"123\n"
string s; 
getline(cin, s);     // s将为空,读取残留的'\n'
2.输入类型不匹配

若要求输入整数时用户输入字符,流状态置为failbit,后续输入将直接跳过

int x;
cin >> x;           // 输入"abc"
if (cin.fail()) {   // 检测到错误状态
    cin.clear();    // 必须先清除状态
    cin.ignore(1000, '\n'); // 再清理缓冲区
}
3.残留数据干扰

多次读取操作时,缓冲区未清理的数据可能导致意外结果

char a, b;
cin.get(a);         // 输入"XY"
cin.get(b);         // b='Y'而非等待新输入
缓冲区清理方法
1.cin.ignore()
  • 功能:跳过指定数量字符或直到遇到终止符
cin.ignore(numeric_limits<streamsize>::max(), '\n');// 清除整行残留

numeric_limits::max()表示最大忽略字符数(通常足够大),适用于清除类型错误后的无效数据残留

2.cin.sync()
  • 功能:清空缓冲区全部内容
  • 局限性:部分编译器(如Visual Studio)不支持该功能
posted @ 2025-04-17 15:23  Sylvia_lee  阅读(29)  评论(0)    收藏  举报