java找出键最大的键值_在键值表中寻找最大值的键

引言

如果要寻找一段文本中出现频率最高的单词,或者出现频率最高的字符,那么首先要按单词或者字符出现的次数建立一个键值表,然后在这个键值表中寻找最大值的键。

方案一

在键值表中寻找最大值的键的最自然的方案如下所示:

1 T FindMaxItem(IDictionaryitems)2 {3 var key = default(T);4 var max = int.MinValue;5 foreach (var kvp initems)6 if (kvp.Value >max)7 {8 key =kvp.Key;9 max =kvp.Value;10 }11 returnkey;12 }

这个方案使用变量 max 来跟踪最大值,使用变量 key 来跟踪最大值对应的键。

方案二

如果把方案一中的变量 key 和 max 组合到一个 Tuple 类中,就得到方案二:

1 T FindMaxItem(IDictionaryitems)2 {3 var pair = Tuple.Create(default(T), int.MinValue);4 foreach (var kvp initems)5 if (kvp.Value >pair.Item2)6 pair =Tuple.Create(kvp.Key, kvp.Value);7 returnpair.Item1;8 }

方案二和方案一没有本质区别,但是通过使用变量 pair 代替变量 key 和 max,将相关的变量 key 和 max 组合在一起,应该说这个重构使代码更优雅了一点。

方案三

实际上还可以使用 KeyValuePair 结构代替 Tuple 类,就得到方案三:

1 T FindMaxItem(IDictionaryitems)2 {3 var pair = new KeyValuePair(default(T), int.MinValue);4 foreach (var kvp initems)5 if (kvp.Value >pair.Value)6 pair =kvp;7 returnpair.Key;8 }

方案三和方案二的区别仅在于变量 pair 的数据类型不同。由于循环变量 kvp 的数据类型就是 KeyValuePair,所以在方案三中第 6 行就可以直接将 kvp 赋值给 pair 。这个重构又使代码更优雅了一点。

方案四

上述三个方案都使用变量同时跟踪最大值和相应的键,其实只需要跟踪最大值对应的键就行了,这就得到方案四:

1 T FindMaxItem(IDictionaryitems)2 {3 var key =items.First().Key;4 foreach (var kvp initems)5 if (kvp.Value >items[key])6 key =kvp.Key;7 returnkey;8 }

这里要注意几点:

因为我们需要寻找键值表中最大值对应的键,所以需要一个变量 key 来跟踪这个键。

上述程序第 5 行使用 items[key] 来得到变量 key 对应的值,以便判断是否有新的最大值。

良好实现的键值表数据结构中,通过键检索相应的值的操作的时间复杂度应该是接近 O(1)。不然,这个算法的效率就大大地有问题了。

变量 key 的初值必须是键值表中的某一项,具体到方案四中,就是键值表中的第一项。如若不然,第 5 行中的 items[key] 就会出问题了。

几点说明

1. 这些方案中 FindMaxItem 方法的参数的类型是 IDictionary 接口,这样就可以适用于 Dictionary、SortedDictionary 和 SortedList 这些数据类型。

1.1 SortedDictionary 和 Sorted 中的 Sorted 是对于 Key 的排序,而不是对于 Value 的排序,所以在这个场合和未排序的 Dictionary 是没有区别的。

1.2 IDictionary 接口中 Key 是不允许为 null,也不允许重复的。但 Value 没有这个限制,既可以为 null,也可以重复。

2. 这些方案中的 FindMaxItem 方法可适用于多种场合。

2.1 如果是在一段文本中寻找出现频率最高的单词,T 的类型是 string,这是一个引用类型。

2.2 如果是在一段文本中寻找出现频率最高的字符,T 的类型是 char,这是一个值类型。

3. 如果键值表是空表的话,前三个方案会返回 default(T),而方案四会抛出 InvalidOperationException 异常。

3.1 如果需要方案四的行为和前三个方案相同,则在方案四的第 3 行使用 FirstOrDefault 方法代替 First 方法就行了。

3.2 如果需要前三个方案的行为和方案四相同,则在前三个方案一开始增加一行以下语句就行了:

if (items.Count == 0) throw new InvalidOperationException("The items is empty.");

4. 前三个方案中 FindMaxItem 方法的参数类型也可以从 IDictionary 接口改为 IEnumerable> 接口。

4.1 在 3.2 中的 IDictionary 接口的(实际上是 IColletion 接口的) Count 属性也就需要改为 IEnumerable> 接口的 Count() 扩展方法了。

4.2 实际上 IDictionary 接口也实现了 IEnumerable> 接口,所以在 3.2 中可以直接使用 Count() 扩展方法代替 Count 属性。

4.3 这样做并不会降低效率,因为如果 source 的类型实现了 ICollection 接口的话,则 Count() 扩展方法会将该接口的 Count 属性用于获取元素计数。

5. 如果键值表中的最大值不只一个,这些方案将返回第一个达到最大值的键。

6. 在前三个方案中,如果键值表中的值只有 int.MinValue,且没有 default(T) 的键的话,这三个方案将错误地返回 default(T)。

6.1 这可以通过将循环中比较语句中的“>”改为“>=”来避免。当然,如果键值表中的最大值不只一个,这些方案将返回最后一个达到最大值的键。

6.2 在方案一中,还可以将 int.MinValue 改为 long.MinValue 来解决。

6.3 在方案二中,除了将 int.MinValue 改为 long.MinValue 外,在第 6 行还需要将 kvp.Value 强制转换为 long 类型。

7. 在方案四中,将第 3 行的 First 方法改为 Last 方法也行,但 Last 方法可能比 First 方法更慢。并且也会微妙地影响方案四的行为,特别是在键值表中的最大值有多个的情况下。

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值