一、题目描述:
小C参与了一场抢红包的游戏,现在他想要对所有参与抢红包的人进行一次运气排名。排名规则如下:抢到的金额越多,排名越靠前;如果两个人抢到的金额相同,则按照他们抢红包的顺序进行排名。比如,如果小C和小U抢到的金额相同,但小C比小U先抢,则小C排在小U前面。
PS:
乍一看,感觉题目并不是很难,直接创建一个哈希表(key是用户名,value是金额)来存储用户和金额,然后根据题目要求先比较金额,金额相同时再比较索引值。
但是哈希表(HashMap)在 Java 中是无序的数据结构。这意味着它不能直接按照我们期望的规则(如先比较值的大小,值相同再比较键在原列表中的顺序)进行排序。所以在将s和x存储在哈希表中之后,再用List存储键值对,就可以实现题目要求。
哈希表(HashMap)可以很方便地将 s 中的元素(作为键)和 x 中的对应元素(作为值)进行关联存储。这使得后续在处理数据时,能够快速通过键(也就是 s 中的元素)获取到与之对应的值(也就是 x 中的元素)。例如,在排序过程中,当比较两个键值对时,能够直接通过 map.get(entry.getKey()) 这样的方式获取到对应的值进行比较,而不需要再去通过复杂的方式去查找 s 和 x 中对应位置的元素来确定值的大小关系等。
于是便有了下面的代码段:(自信点击运行,结果解答错误!!!)
import java.util.*;
public class Main {
public static List<String> solution(int n, List<String> s, List<Integer> x) {
// write code here
//利用哈希表比较key和value
HashMap<String,Integer> map = new HashMap<>();
for (int i=0;i<n;i++){
map.put(s.get(i), x.get(i));
}
//创建一个List存储键值对
List<HashMap.Entry<String,Integer>> resultList = new ArrayList<>(map.entrySet());
//使用冒泡排序对列表的键值对进行排序
for(int i=0;i<resultList.size()-1;i++){
for(int j=0;j<resultList.size()-i-1;j++){
HashMap.Entry<String,Integer> entry1 = resultList.get(j);
HashMap.Entry<String,Integer> entry2 = resultList.get(j+1);
//比较值(金额)
if(entry2.getValue()>entry1.getValue()){
//交换值(从大到小排序)
resultList.set(j,entry2);
resultList.set(j+1, entry1);
}
//值相等时,比较出现的顺序
else if(entry1.getValue()==entry2.getValue()){
//比较entey1,entry2的key值在列表s中的位置,比较索引,索引大的在后面,所以将j位置设为索引小的
if(s.indexOf(entry1.getKey())>s.indexOf(entry2.getKey())){
resultList.set(j,entry2);
resultList.set(j+1, entry1);
}
}
}
}
//提取resultList的键到新的列表中并输出
ArrayList<String> resuArrayList = new ArrayList<>();
for (HashMap.Entry<String,Integer> entry : resultList){
resuArrayList.add(entry.getKey());
}
return resuArrayList;
}
public static void main(String[] args) {
System.out.println(solution(4, Arrays.asList("a", "b", "c", "d"), Arrays.asList(1, 2, 2, 1)).equals(Arrays.asList("b", "c", "a", "d")));
System.out.println(solution(3, Arrays.asList("x", "y", "z"), Arrays.asList(100, 200, 200)).equals(Arrays.asList("y", "z", "x")));
System.out.println(solution(5, Arrays.asList("m", "n", "o", "p", "q"), Arrays.asList(50, 50, 30, 30, 20)).equals(Arrays.asList("m", "n", "o", "p", "q")));
}
}
好叭,看了评测不通过的原因,是因为我没有考虑用户名相同的情况,也就是当用户名相同时,需要先将对应的金额全部相加,得到新的列表,再进行比较,看来还是得考虑全面一点。修改原有代码的不足之处,得到可以正确运行的代码,如下:
二、代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
public static List<String> solution(int n, List<String> s, List<Integer> x) {
// 利用哈希表比较key和value
HashMap<String, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) {
String key = s.get(i);
int value = x.get(i);
if (map.containsKey(key)) {
// 如果key已存在,将value相加
map.put(key, map.get(key) + value);
} else {
map.put(key, value);
}
}
// 创建一个List存储键值对
List<Map.Entry<String, Integer>> resultList = new ArrayList<>(map.entrySet());
// 使用冒泡排序对列表的键值对进行排序
for (int i = 0; i < resultList.size() - 1; i++) {
for (int j = 0; j < resultList.size() - i - 1; j++) {
Map.Entry<String, Integer> entry1 = resultList.get(j);
Map.Entry<String, Integer> entry2 = resultList.get(j + 1);
// 比较值(金额)
if (entry2.getValue() > entry1.getValue()) {
// 交换值(从大到小排序)
resultList.set(j, entry2);
resultList.set(j + 1, entry1);
}
// 值相等时,比较出现的顺序
else if (entry1.getValue().equals(entry2.getValue())) {
// 比较entey1,entry2的key值在列表s中的位置,比较索引,索引大的在后面,所以将j位置设为索引小的
if (s.indexOf(entry1.getKey()) > s.indexOf(entry2.getKey())) {
resultList.set(j, entry2);
resultList.set(j + 1, entry1);
}
}
}
}
// 提取resultList的键到新的列表中并输出
ArrayList<String> resuArrayList = new ArrayList<>();
for (Map.Entry<String, Integer> entry : resultList) {
resuArrayList.add(entry.getKey());
}
return resuArrayList;
}
public static void main(String[] args) {
System.out.println(solution(4, Arrays.asList("a", "b", "c", "d"), Arrays.asList(1, 2, 2, 1)).equals(Arrays.asList("b", "c", "a", "d")));
System.out.println(solution(3, Arrays.asList("x", "y", "z"), Arrays.asList(100, 200, 200)).equals(Arrays.asList("y", "z", "x")));
System.out.println(solution(5, Arrays.asList("m", "n", "o", "p", "q"), Arrays.asList(50, 50, 30, 30, 20)).equals(Arrays.asList("m", "n", "o", "p", "q")));
}
}
三、相关知识点总结
ps:我之前一直疑惑的一个点:
Map<String,Integer> map = new HashMap<>();
HashMap<String,Integer> map = new HashMap<>();
这二者到底有什么区别?
答案是: HashMap<String,Integer> map = new HashMap<>();:这种方式明确地指定了map是HashMap类型。如果在后续的代码中,你需要调用HashMap特有的方法(而这些方法不是Map接口所定义的),那么这种声明方式是合适的。例如,HashMap有一个putAll(Map<? extends K,? extends V> m)方法,它允许从另一个Map中批量添加键值对,并且还有一些与哈希表内部实现相关的方法(如rehash等,虽然一般不直接调用)。
Map<String,Integer> map = new HashMap<>();:这种方式使用了接口Map来声明变量map。这种声明方式的优点是提高了代码的灵活性和可维护性。如果在后续的开发过程中,你发现HashMap不再适合你的需求(例如,你可能需要一个具有不同性能特性的Map实现,如TreeMap用于按键排序,或者LinkedHashMap用于保持插入顺序),你可以很容易地将HashMap替换为其他Map实现,而不需要修改使用map变量的其他代码(只要你只使用了Map接口中定义的方法)。
Map<String,Integer> map = new HashMap<>();遵循了面向接口编程的原则。在这种编程风格中,变量的类型通常声明为接口而不是具体的实现类。这样做可以降低代码的耦合度,使代码更容易扩展和维护。例如,如果你编写一个方法,它接受一个Map作为参数,那么这个方法可以处理任何实现了Map接口的类的实例,而不仅仅是HashMap。
1.Map和List的区别
(1) 数据结构特点
- Map
键值对存储:Map是一种键值对的数据结构,每个键(key)对应一个值(value)。例如,在HashMap中,键是唯一的,通过键可以快速地访问到对应的值。
无序性(通常情况):一般来说,Map中的键值对是没有顺序的。虽然有些实现类如LinkedHashMap可以维护插入顺序,但大多数Map实现(如HashMap)不保证顺序。
- List
线性存储:List是一种线性的数据结构,它存储的是一系列的元素。例如,ArrayList和LinkedList都是List的实现类,它们存储的元素是有顺序的,每个元素都有一个索引。
有序性:List中的元素是按照插入顺序存储的,通过索引可以访问到特定位置的元素。
(2)操作方法
- Map
主要操作:
添加元素:使用put(key, value)方法来添加键值对。例如:map.put("key1", 1);
获取元素:通过get(key)方法获取对应键的值。例如:int value = map.get("key1");
删除元素:可以使用remove(key)方法删除指定键的键值对。例如:map.remove("key1");
遍历方式:<1>分别获取键的集合(keySet)或值的集合(values)来进行遍历。如下:
《1》先使用KeySet()获取所有键的集合,再根据键获取对应值
Map<String,Integer> map = new HashMap<>();
for (String key : map.KetSet()){
Integer value = get(key);
System.out.Println("key is" +key+ ",value is" +value);
}
《2》使用values()获取所有值的集合,但是此种方法不能获取到键值
for (Integer value : map.values()){
System.out.Println("value is"+value);
}
<2> 也可以通过获取键值对的集合(entrySet)来遍历Map,示例代码如下 使用Entry接口遍历Map
- List
主要操作:
添加元素:可以使用add(element)方法在列表末尾添加元素,例如:list.add("element1");,也可以使用add(index, element)在指定索引位置添加元素。
获取元素:通过get(index)方法获取指定索引位置的元素,例如:String element = list.get(0);
删除元素:使用remove(index)或remove(element)方法来删除元素,例如:list.remove(0);或list.remove("element1");
遍历方式: - 可以使用普通的for循环通过索引来遍历,例如:
for (int i = 0; i < list.size(); i++) {
String element = list.get(i);
// 处理元素
}
也可以使用增强的for循环来遍历,例如:
for (String element : list) {
// 处理元素
}
(3) 应用场景
- Map
查找操作频繁的场景:当需要根据某个特定的标识(键)来快速查找对应的数据(值)时,Map是很好的选择。例如,在存储用户ID和用户信息的对应关系时,使用Map可以方便地通过用户ID快速获取用户信息。
去重场景:如果有一组数据需要去重,并且每个数据都有一个相关联的值,可以将数据作为键,相关联的值作为值存储在Map中,利用键的唯一性实现去重。
- List
有序数据存储:当数据有顺序要求,例如存储一个任务列表、文章段落等,List是合适的选择。
数据的顺序操作:如果需要对数据进行顺序相关的操作,如在某个位置插入元素、获取指定位置的元素等,List更能满足需求。例如,在实现一个队列或栈的数据结构时,List可以作为基础的数据存储方式。
2.Entry接口
(1)Map接口与Entry接口的关系
在Java中,Map接口用于存储键 - 值对。而Entry接口是Map接口的一个内部接口,它表示Map中的一个键 - 值对。每一个Entry对象包含一个键(key)和与之关联的值(value)。 例如,当你有一个HashMap(它实现了Map接口),HashMap内部通过Entry对象来管理键 - 值对。
(2)Entry接口的主要方法
getKey()方法:用于获取Entry对象中的键。
getValue()方法:用于获取Entry对象中的值。
setValue(V value)方法(在可变Map中):这个方法用于修改Entry对象中关联的值。
(3) 遍历Map使用Entry接口
通常可以通过entrySet()方法来获取Map中所有Entry对象的集合,然后使用循环(如for - each循环)来遍历这些Entry对象。示例:
Map<String,Integer> resultMap = new HashMap<>();
for(Map.Entry<String,Integer> entry : resultMap.entrySet()){
String name = entry.getKey();
int amount = entry.getValue();
}
Entry接口的实现类 不同的Map实现类(如HashMap、TreeMap等)有自己内部的Entry实现。例如,HashMap的Entry实现是基于哈希表的节点结构,而TreeMap的Entry实现是基于红黑树节点结构。这些实现类内部的Entry结构细节不同,但都遵循Entry接口的规范,对外提供一致的getKey、getValue等方法来访问键 - 值对。
2.冒泡排序
(1)基本原理
冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序排列时),就像气泡从水底向上浮一样。
(2)示例代码(升序排列)
以下是一个简单的Java代码实现冒泡排序:
java public class BubbleSort {
public static void main(String[] args) {
int[] array = {5, 4, 3, 2, 1};
// 外层循环控制排序轮数
for (int i = 0; i < array.length - 1; i++) {
// 内层循环控制每一轮的比较次数
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
// 交换元素位置
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
for (int num : array) {
System.out.print(num + " ");
}
}
}
解释:
外层循环 for (int i = 0; i < array.length - 1; i++):这一层循环控制排序的轮数。对于一个长度为n的数组,最多需要n - 1轮排序就可以完成整个数组的排序。因为在n - 1轮排序后,最大的n - 1个元素都已经被移动到了正确的位置,剩下的一个元素自然也就在正确的位置了。
内层循环 for (int j = 0; j < array.length - 1 - i; j++):这一层循环控制每一轮中比较的次数。在每一轮排序中,由于经过前面轮次的排序,最大的元素已经“浮”到了数组的末尾,所以每一轮比较的次数都可以减少。例如,第一轮需要比较n - 1次,第二轮需要比较n - 2次,以此类推。
条件判断 if (array[j]> array[j + 1]):这个条件用于比较相邻的两个元素。如果当前元素大于下一个元素,就交换它们的位置,这样较大的元素就会向后移动。
交换元素位置的代码int temp = array[j]; array[j]= array[j + 1]; array[j + 1]= temp;:这是一个经典的交换两个变量值的操作。通过一个临时变量temp,先将array[j]的值保存起来,然后将array[j + 1]的值赋给array[j],最后再将保存起来的array[j]的值(也就是原来的array[j]的值)赋给array[j + 1],从而实现了两个元素的交换。
这篇文章就总结到这里!!!❤❤❤