红包运气排行榜 | 豆包MarsCode AI刷题

112 阅读11分钟

一、题目描述:

小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")));
    }
}

屏幕截图 2024-11-19 180304.png 好叭,看了评测不通过的原因,是因为我没有考虑用户名相同的情况,也就是当用户名相同时,需要先将对应的金额全部相加,得到新的列表,再进行比较,看来还是得考虑全面一点。修改原有代码的不足之处,得到可以正确运行的代码,如下:

二、代码

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<>();:这种方式明确地指定了mapHashMap类型。如果在后续的代码中,你需要调用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是一种线性的数据结构,它存储的是一系列的元素。例如,ArrayListLinkedList都是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实现类(如HashMapTreeMap等)有自己内部的Entry实现。例如,HashMapEntry实现是基于哈希表的节点结构,而TreeMapEntry实现是基于红黑树节点结构。这些实现类内部的Entry结构细节不同,但都遵循Entry接口的规范,对外提供一致的getKeygetValue等方法来访问键 - 值对。

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],从而实现了两个元素的交换。

这篇文章就总结到这里!!!❤❤❤