题目链接
题目描述
小苯收到 条文章浏览记录,每条记录都是一个用户的 ID 字符串。同一个用户可能会浏览多次,产生多条记录。
需要按照这些记录的出现顺序,输出所有独立用户的 ID,每个 ID 只输出其第一次出现的那一次。
解题思路
这个问题要求我们对一个数据流进行去重,同时保持每个元素首次出现的相对顺序。这是一个经典的“首次出现”或“顺序去重”问题。
解决这个问题的关键在于,当我们处理每条浏览记录时,需要能快速判断“这个用户 ID 之前是否已经出现过?”。
哈希集合(Hash Set) 是解决此类问题的理想数据结构。它可以在平均 的时间内完成插入和查找操作。
具体的算法步骤如下:
-
创建一个空的哈希集合,例如
seen_users
,用来存放所有已经遇到过的、独一无二的用户 ID。 -
读取总的记录条数
。
-
循环
次,按顺序处理每一条浏览记录:
a. 读取当前的用户 ID 字符串
current_id
。b. 检查
seen_users
集合中是否已经包含current_id
。c. 如果不包含,说明这是该用户第一次浏览。此时,我们应该: i. 将
current_id
输出。 ii. 将current_id
添加到seen_users
集合中,以记录我们已经处理过这个用户。d. 如果包含,说明这个用户之前已经浏览过,我们直接忽略这条记录,继续处理下一条。
-
循环结束后,所有首次浏览的用户 ID 就已经按顺序输出完毕了。
根据用户的偏好,在 C++ 中我们将使用 std::set
来实现。
代码
#include <iostream>
#include <string>
#include <set>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
set<string> seen_users;
for (int i = 0; i < n; ++i) {
string user_id;
cin >> user_id;
// set::count() 返回 0 或 1
if (seen_users.count(user_id) == 0) {
cout << user_id << "\n";
seen_users.insert(user_id);
}
}
return 0;
}
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Set<String> seenUsers = new HashSet<>();
for (int i = 0; i < n; i++) {
String userId = sc.next();
if (!seenUsers.contains(userId)) {
System.out.println(userId);
seenUsers.add(userId);
}
}
}
}
import sys
def solve():
try:
n_str = sys.stdin.readline().strip()
if not n_str: return
n = int(n_str)
seen_users = set()
for _ in range(n):
user_id = sys.stdin.readline().strip()
if user_id not in seen_users:
print(user_id)
seen_users.add(user_id)
except (IOError, ValueError):
return
solve()
算法及复杂度
-
算法:模拟 + 哈希集合
-
时间复杂度:令
为浏览记录的总条数,
为用户 ID 的平均长度。
- 对于哈希集合(
HashSet
, Pythonset
),每次插入和查找的平均时间复杂度是(字符串哈希和比较所需的时间)。总时间复杂度为
。
- 对于基于平衡树的集合(C++
std::set
),每次操作的复杂度是,其中
是当前集合中唯一用户的数量。总时间复杂度为
。
- 对于哈希集合(
-
空间复杂度:
,其中
是独立用户的总数。我们需要存储所有不重复的用户 ID。