标题简介与问题概述
探险家小扣在每次探险后都会留下一串记载,记载中包含了他访问过的营地名单。这些营地名以字符串形式出现,通过”->”符号衔接。咱们的使命是剖析小扣的探险记载,找出他在哪一次探险中发现了最多的新营地。
探险记载是一个字符串数组,其中每个字符串代表一次探险的途径。初始探险的记载包含了小扣已知的一切营地。在随后的探险中,假如记载包含了新的营地,那么这些营地被视为新发现。咱们需求确定在哪次探险中,小扣发现了最多的新营地,并回来那次探险的索引。假如一切探险都没有发现新营地,咱们则回来-1。
在这个问题中,每个营地都是独一无二的,即使是大小写不同也被视为不同的营地。每个营地的称号都对错空的,确保了至少存在一个字符。
此问题的挑战在于怎么有效地解析这些记载,并准确地辨认出新发现的营地。
原始处理计划剖析
为了找出小扣在哪次探险中发现了最多的新营地,咱们需求对探险记载进行剖析。原始的处理计划可能是按照以下步骤进行:
- 初始化一个调集originCamp,用于存储第一次探险已知的一切营地。
- 遍历expeditions数组,从第二个元素开始,因为第一个元素代表的是已知营地。
- 关于每个后续的探险记载,使用字符串切割操作,将营地称号切割开来。
- 遍历切割后得到的营地称号数组,检查每个称号是否在originCamp调集中。
- 假如发现了不在调集中的新营地,将其添加到调集中,并更新本次探险的新发现营地计数。
- 记载新发现营地数量最多的探险索引和对应的新营地数量。
完结遍历后,回来新发现营地数量最多的探险索引,假如没有新发现的营地,则回来-1。
这个计划的首要问题在于,它需求执行大量的字符串切割操作和调集成员检查,这在探险记载较多或营地称号较长时,会导致功率低下。
参阅代码如下:
class Solution {
public int adventureCamp(String[] expeditions) {
// 关于起点的地址树立HashSet
Set<String> originCamp = new HashSet<>();
if (expeditions[0].length() > 0) {
String[] camps = expeditions[0].trim().split("->");
for (String camp : camps) {
originCamp.add(camp);
}
}
// 记载结果
int res = -1;
int mx = 0;
for (int i = 1; i < expeditions.length; i ) {
if (expeditions[i].length() == 0) {
continue;
}
String[] camps = expeditions[i].trim().split("->");
int hasCamp = 0;
for (String camp : camps) {
// 若既不在开始营地,也不在访问过的营地
boolean originFlag = originCamp.contains(camp);
if (!originFlag) {
hasCamp ;
originCamp.add(camp);
}
}
if (hasCamp > mx) {
res = i;
mx = hasCamp;
}
}
return res;
}
}
优化计划:哈希值
在处理“探险营地”问题的原始处理计划中,咱们直接操作字符串来辨认新发现的营地。这种方法在数据量较小的情况下是可行的,但随着数据量的增加,字符串比较的开销也随之增大,这直接影响了算法的功能。为了提高功率,咱们将处理计划从直接处理字符串转变为处理哈希值。
哈希值处理的优势
处理哈希值比较于处理完整的字符串有两个首要优势:
- 功能提升:哈希值一般为整数,整数之间的比较远比长字符串之间的比较愈加高效。
- 空间节省:在存储和操作时,整数占用的空间远小于长字符串。
应对哈希抵触
但是,直接使用标准的hashCode方法可能会引发哈希抵触,即不同的字符串可能发生相同的哈希值。以Java默许的hashCode算法为例。咱们给出代码:
public int hashCode() {
int h = hash; // 默以为0
if (h == 0 && value.length > 0) {
char val[] = value; // 字符串的字符数组
for (int i = 0; i < value.length; i ) {
h = 31 * h val[i];
}
hash = h;
}
return h;
}
在本题中,选用默许的hashCode方法会导致hash抵触。为了处理这个问题,咱们选用了自定义哈希算法。
应用自定义哈希算法
咱们将选用以下步骤以应用自定义哈希算法:
- 设计一个自定义哈希函数,该函数能够依据营地称号生成唯一的哈希值。
- 创建一个哈希表来存储营地称号与其哈希值的映射。
- 在处理探险记载时,首先通过自定义哈希函数核算每个营地称号的哈希值。
- 使用核算出的哈希值来更新已知营地调集,并统计新发现的营地数量。
- 依据新发现的营地数量更新记载,并维护最多新营地发现的探险索引。
- 遍历完结后,回来新营地发现最多的探险索引,若无新发现,则回来-1。
改善后的代码如下:
class Solution {
// 获取字符串的哈希值,以'->'切割
private List<Long> getStringHshs(String str) {
int i = 0;
long hsh = 0;
char[] chs = str.toCharArray();
List<Long> hshs = new ArrayList<>();
if (chs.length > 0) {
for (; i < chs.length; ) {
if (chs[i] == '-') {
i = 2;
hshs.add(hsh);
hsh = 0;
} else {
hsh = hsh * 199 chs[i];
i ;
}
}
}
if (hsh != 0) {
hshs.add(hsh);
}
return hshs;
}
public int adventureCamp(String[] expeditions) {
// 关于起点的地址树立HashSet
Set<Long> originCamp = new HashSet<>();
if (expeditions[0].trim().length() > 0) {
List<Long> hshs = getStringHshs(expeditions[0]);
for (Long hsh : hshs) {
originCamp.add(hsh);
}
}
// 记载结果
int res = -1;
int mx = 0;
for (int i = 1; i < expeditions.length; i ) {
if (expeditions[i].length() == 0) {
continue;
}
List<Long> hshs = getStringHshs(expeditions[i]);
System.out.println();
int hasCamp = 0;
for (Long hsh : hshs) {
// 若既不在开始营地,也不在访问过的营地
boolean originFlag = originCamp.contains(hsh);
if (!originFlag) {
hasCamp ;
originCamp.add(hsh);
}
}
if (hasCamp > mx) {
res = i;
mx = hasCamp;
}
}
return res;
}
}
总结
通过引入自定义哈希算法,在处理时刻上有了明显的改变。
原始计划处理探险记载的时刻为187毫秒,而通过优化后,时刻缩短至96毫秒。这一改善减少了近一半的执行时刻,证明了优化的有效性。