”寻找最大葫芦“ | 豆包MarsCode AI刷题

185 阅读3分钟

问题简述:

葫芦:由五张牌组成,三张一样的a和两张一样的b

在一个数组中找到最大的“葫芦”,并且五张牌的和不能超过题目中设置的值max

注:1>13>12...>2

思路总述

按倒序排列的map集合 + 双指针

思路详解

1、导入相关的包

import java.util.Map;   // //通过 `Map` 接口来引用任何实现了该接口的类,比如 `HashMap`、`TreeMap` 或 `LinkedHashMap`。
import java.util.HashMap;   
import java.util.LinkedHashMap;  //维护元素的顺序
import java.util.TreeMap;  //`TreeMap` 基于红黑树实现,自动按键的自然顺序(或根据提供的比较器)进行排序。
import java.util.Comparator;

2、声明相关变量

//声明数组中除了1的其他数字作为键要存放进的map集合,其中值为各个数字出现的次数,并按逆序排列
Map<Integer, Integer> map = new TreeMap<>(Comparator.reverseOrder());
//存放1的map集合
Map<Integer, Integer> map1 = new HashMap<>();
//用来按特定顺序合并上面两个集合
Map<Integer, Integer> mergedMap = new LinkedHashMap<>();
// 用于存放结果
int[] result = new int[]{0, 0}; 
//用于统计5张牌数字和
int sum = 0;

3、将数组中的数字存放进对应的map集合中

 for (int i=0;i<array.length;i++) {
        if(array[i] ==1){
        //当遍历到的数字是1时,存放进map1中,注意map1中是否已经有了为1的键
            if(map1.containsKey(1)){       
                  map1.put(1,map1.get(1) + 1);
             }else{
                    map1.put(1,1);
             }
         }else{
         //遍历到其他数时,存放进map集合中
                map.put(array[i], map.getOrDefault(array[i], 0) + 1);
         }
     }

涉及到的方法:

  • containsKey():用来判断map集合中是否有指定的某个键,并返回true或false;其基于哈希表实现,故时间复杂度为O(1)

  • getOrDefault():map.getOrDefault(key, defaultValue)Map 接口中的一个方法;

    这个题中,它尝试从 map 中获取与 array[i] 相关的值(即该元素的出现次数)。如果 map 中存在 array[i] 这个键,返回其对应的值(出现次数); 如果 map 中不存在这个键,则返回默认值 0。这用于处理第一次遇到该元素的情况。

4、合并两个集合(map、map1)到集合mergedMap中去

mergedMap.putAll(map1);
mergedMap.putAll(map);

有小伙伴可能会疑惑:为什么不直接将map合并到map1中,而要设置第三个集合去存放合并后的结果呢?

解答:这样的话会破坏map集合中原来的逆序顺序,即使 TreeMap 中的键是按照逆序排列的,HashMap 不会保持这种顺序。它将根据自己的哈希算法来存储map中的那些键。而第三个集合声明的是LinkedHashMap,恰好能维护元素的顺序

5、开始遍历集合

使用双指针a和b 思路:a指针先不动,b指针开始遍历,只要有符合条件的b直接输出(只要保证a尽可能大就可以);除非b遍历完了,都没有符合条件的,再开始移动a指针指向下一个,b再从头开始遍历,依次类推...

//-----------------外层循环寻找合适的最大的a值--------------------------------
for (Map.Entry<Integer, Integer> entry : mergedMap.entrySet()) {
       //a指针拿到的键和值
       int key1 = entry.getKey();
       int count1 = entry.getValue();
       //每次遍历集合去拿b的值无果后将sum清零重新开始
       sum=0;
       // 检查当前键的值是否不少于3张并且当前键*3后是否超出max,如果超出直接不取
       if (count1 >= 3 && sum + key1 * 3 <= max) {
            sum += key1 * 3;
            result[0] = key1;
            //-------------------------- 内层循环查找合适的最大b值-----------------------------
            for (Map.Entry<Integer, Integer> entry2 : mergedMap.entrySet()) {
              //b指针拿到的键和值
               int key2 = entry2.getKey();
               int count2 = entry2.getValue();
               // 检查当前键的值是否不少于2张并且当前键*2后是否超出max,如果超出直接不取
               if (key2 != key1 && count2 >= 2 && sum + key2 * 2 <= max) {
                        result[1] = key2; // 记录b
                        return result; // 找到组合后直接返回
                }
            }
                // 不需要 break,因为我们可能找到更优组合
       }
 }

注意:a如果比对方大后,就直接表示比对方大了,只有当a相等才会去比较b

6、结尾

 return new int[]{0, 0}; // 如果没有有效组合,返回 {0, 0}