20 | 散列表(下):为什么散列表和链表经常会一起使用
- 2023-12-09 北京
本文字数:3075 字
阅读完需:约 10 分钟
今天讲的几个散列表和链表结合使用的例子里,我们用的都是双向链表。如果把双向链表改成单链表,还能否正常工作呢?为什么呢?
使用双向链表:
正常工作: 散列表使用双向链表可以正常工作。
插入和删除操作: 双向链表支持在链表中的任意位置快速插入和删除操作,这在处理冲突时可能是有利的。
遍历方向: 双向链表允许以两个方向遍历链表,这在某些情况下可能是有用的。
使用单链表:
正常工作: 散列表使用单链表同样可以正常工作。
空间效率: 单链表相对于双向链表占用更少的空间,因为不需要额外的指向前一个节点的引用。
简化实现: 如果在特定情况下不需要双向遍历链表,使用单链表可能会简化实现。
选择考虑的因素:
内存占用: 如果内存占用是关键因素,可以考虑使用单链表。
操作复杂性: 如果需要在链表中的任意位置进行频繁的插入和删除操作,双向链表可能更合适。
需求和设计选择: 具体的需求和设计选择可能会影响选择使用单链表还是双向链表。
假设猎聘网有 10 万名猎头,每个猎头都可以通过做任务(比如发布职位)来积累积分,然后通过积分来下载简历。假设你是猎聘网的一名工程师,如何在内存中存储这 10 万个猎头 ID 和积分信息,让它能够支持这样几个操作:
根据猎头的 ID 快速查找、删除、更新这个猎头的积分信息;
查找积分在某个区间的猎头 ID 列表;
查找按照积分从小到大排名在第 x 位到第 y 位之间的猎头 ID 列表。
源码
为了支持这样的操作,你可以考虑使用一种数据结构,例如跳表(Skip List)或有序的平衡树,来存储猎头的 ID 和积分信息。这样的数据结构能够保持有序性,并且对于查找、删除、更新等操作具有较好的性能。
package com.controller.bootweb.demo.dsa;
import java.util.*;
class Headhunter {
String id;
int points;
public Headhunter(String id, int points) {
this.id = id;
this.points = points;
}
}
public class HeadhunterSystem {
private TreeMap<Integer, List<String>> pointsMap; // 通过积分建立索引
private HashMap<String, Headhunter> headhuntersMap; // 通过ID快速查找猎头信息
public HeadhunterSystem() {
pointsMap = new TreeMap<>();
headhuntersMap = new HashMap<>();
}
// 添加猎头信息
public void addHeadhunter(String id, int points) {
Headhunter headhunter = new Headhunter(id, points);
// 更新积分Map
pointsMap.computeIfAbsent(points, k -> new ArrayList<>()).add(id);
// 更新猎头Map
headhuntersMap.put(id, headhunter);
}
// 删除猎头信息
public void removeHeadhunter(String id) {
Headhunter headhunter = headhuntersMap.get(id);
if (headhunter != null) {
// 从积分Map中移除
pointsMap.get(headhunter.points).remove(id);
// 从猎头Map中移除
headhuntersMap.remove(id);
}
}
// 更新猎头积分
public void updatePoints(String id, int newPoints) {
removeHeadhunter(id); // 先移除再重新添加
addHeadhunter(id, newPoints);
}
// 查找积分在某个区间的猎头ID列表
public List<String> findHeadhuntersInPointsRange(int start, int end) {
List<String> result = new ArrayList<>();
// 使用 TreeMap 的 subMap 方法找到积分区间的子集
NavigableMap<Integer, List<String>> subMap = pointsMap.subMap(start, true, end, true);
// 合并子集中的所有ID
for (List<String> ids : subMap.values()) {
result.addAll(ids);
}
return result;
}
// 查找按照积分从小到大排名在第x位到第y位之间的猎头ID列表
public List<String> findHeadhuntersByRank(int x, int y) {
List<String> result = new ArrayList<>();
// 遍历积分Map,记录排名
int currentRank = 1;
for (List<String> ids : pointsMap.values()) {
for (String id : ids) {
if (currentRank >= x && currentRank <= y) {
result.add(id);
}
currentRank++;
}
}
return result;
}
public List<String> findHeadhuntersByRankOptimized(int x, int y) {
List<String> result = new ArrayList<>();
int currentRank = 1;
for (List<String> ids : pointsMap.values()) {
for (String id : ids) {
if (currentRank > y) {
// 已经超过排名范围,结束遍历
return result;
}
if (currentRank >= x) {
// 在排名范围内,添加到结果列表
result.add(id);
}
currentRank++;
}
}
return result;
}
// 根据ID查找猎头信息
public Headhunter findHeadhunterById(String id) {
return headhuntersMap.get(id);
}
// 获取所有猎头ID
public Iterable<String> getAllHeadhunters() {
return headhuntersMap.keySet();
}
// 获取所有积分信息
public Iterable<Integer> getAllPoints() {
return pointsMap.keySet();
}
}
package com.controller.bootweb.demo.dsa;
import java.util.List;
public class TestHeadhunterSystem {
public static void main(String[] args) {
HeadhunterSystem headhunterSystem = new HeadhunterSystem();
// 添加猎头信息
headhunterSystem.addHeadhunter("hunter1", 100);
headhunterSystem.addHeadhunter("hunter2", 150);
headhunterSystem.addHeadhunter("hunter3", 120);
headhunterSystem.addHeadhunter("hunter4", 180);
headhunterSystem.addHeadhunter("hunter5", 90);
// 打印所有猎头ID和积分
System.out.println("All Headhunters:");
for (String id : headhunterSystem.getAllHeadhunters()) {
System.out.println("ID: " + id + ", Points: " + headhunterSystem.findHeadhunterById(id).points);
}
// 查找积分在某个区间的猎头ID列表
int startRange = 100;
int endRange = 150;
List<String> headhuntersInRange = headhunterSystem.findHeadhuntersInPointsRange(startRange, endRange);
System.out.println("\nHeadhunters in Points Range (" + startRange + " to " + endRange + "):");
System.out.println(headhuntersInRange);
// 查找按照积分从小到大排名在第x位到第y位之间的猎头ID列表
int rankX = 2;
int rankY = 4;
List<String> headhuntersByRank = headhunterSystem.findHeadhuntersByRankOptimized(rankX, rankY);
System.out.println("\nHeadhunters by Rank (" + rankX + " to " + rankY + "):");
System.out.println(headhuntersByRank);
}
}
鲁米
生活黑客35 2019-06-11 加入
起点不重要,迭代很重要,就需要保持充分的开放和积累;而信息越充分,结果越可靠,又要求随时调整、不断逼近真相。
评论