【LeetCode每日一题】2022-10-08 870. 优势洗牌(田忌赛马)Java实现


题目

在这里插入图片描述

我的思路和代码 超时…

用一个res数组存储最后的结果,其中的数字就是num1数组中的数字

用一个vis数组标记已经使用了num1数组中的哪些数字

先对num1数组排序,然后遍历num2数组,如果num1当前位置 i 上的数字大于num2当前位置 j 上的数字,那么,res数组的 j 位置上存储 num1数组 i 位置上的数字,并且,vis[i] 赋值为1,表示num1数组第 i 个位置上的数字已经使用过了。

当把num2数组遍历一遍以后,如果num1中数组中还有数字未使用,那么就可以在res数组中任意找位置填入

代码超时…

import java.util.Arrays;

class Solution {


    public int[] advantageCount(int[] nums1, int[] nums2) {
        Arrays.sort(nums1);

        int len = nums1.length;

        int[] res = new int[len];
        Arrays.fill(res, -1);

        int[] vis = new int[len];
        Arrays.fill(vis, 0);

        int index = 0;
        for (int j = 0; j < len; j++) {
            for (int i = 0; i < len; i++) {
                if (vis[i] == 0 && nums1[i] > nums2[j]) {
                    vis[i] = 1;
                    res[j] = nums1[i];
                    break;
                }
            }
        }

        int i = 0;
        while (Arrays.stream(vis).sum() != len) {
            if (res[i] == -1) {
                for (int j = 0; j < len; j++) {
                    if (vis[j] == 0) {
                        vis[j] = 1;
                        res[i] = nums1[j];
                        break;
                    }
                }
            }
            i++;
        }
        return res;
    }
}

官方思路 贪心算法 排序(lambda表达式)+双指针

思路

首先,分别将数组num1和num2排序,随后,只需要不断判断这两个数组的首个元素:

  • 如果num1的首个元素大于num2的首个元素,那么就将它们在答案中对应起来,同时从数组中移除这两个元素【增加一点优势】

  • 如果num1的首个元素小于等于num2的首个元素,那么移除num1的首个元素

当num1中没有元素时,遍历结束

这样做的正确性在于:

  • 对于第一种情况,由于num1是有序的,那么num1的任意元素大于num2的首个元素

    • 如果我们不与num2的首个元素配对,由于num2是有序地之后的元素会更大,这样并不划算

    • 如果与num2的首个元素配对,使用num1的首个元素,可以使得剩余的元素尽可能的大,之后可以获得更多的【优势】

因为num1的首个元素大于num2的首个元素,num1是有序的,所以num1的任意元素都大于num2的首个元素,num1越往后,与num2的首个元素差别越大,所以,我们就选取与num2的首个元素最接近、又比num2首个元素大的那个元素,贪心思想

  • 对于第二种情况,由于num2是有序的,那么num1中的首个元素小于等于num2中的任意元素,因此,num1的首个元素无法增加任何【优势】,可以直接移除

在本题中,由于num1中的每一个元素都要与num2中的元素配对,而我们是按照顺序考虑num2中的元素的。因此,在遍历结束后,num2中剩余的元素实际上是原先num2的一个后缀。因此当num1的首个元素无法配对时,我们给它配对num2的尾元素即可,并将该尾元素移除。

关于移除这一说法

在实际的代码编写中,我们无需真正的移除元素。

对于num1,可以使用一个循环依次遍历其中的每个元素

对于num2,可以使用双指针,left和right

如果num1中的首个元素可以增加优势,就配对left对应的元素并向右移动一个位置

如果无法配对,就配对right对应的元素,并向左移动一个位置

代码和结果

在这里插入图片描述

import java.util.Arrays;
import java.util.Comparator;

class Solution {
    public int[] advantageCount(int[] nums1, int[] nums2) {
        int n = nums1.length;
        Integer[] idx1 = new Integer[n];
        Integer[] idx2 = new Integer[n];
        for (int i = 0; i < n; ++i) {
            idx1[i] = i;
            idx2[i] = i;
        }

        //记录数组是如何排序的
        Arrays.sort(idx1, Comparator.comparingInt(i -> nums1[i]));
        Arrays.sort(idx2, (i, j) -> nums2[i] - nums2[j]);

        int[] ans = new int[n];
        int left = 0, right = n - 1;
        for (int i = 0; i < n; ++i) {
            //idx1[i]作为索引,i从[0,n),num1[idx1[i]]是有序的
            //同样,idx2[left]作为索引,left从0开始,num2[idx2[left]]也是有序的

            //如果num1中的首个元素大于num2中的首个元素,那么就将它们在答案中对应起来
            if (nums1[idx1[i]] > nums2[idx2[left]]) {
                ans[idx2[left]] = nums1[idx1[i]];
                ++left;
            } else {
                //当num1中的首个元素无法配对时,把它配对给num2的尾元素即可
                ans[idx2[right]] = nums1[idx1[i]];
                --right;
            }
        }
        return ans;
    }
}

他人思路 TreeMap

TreeMap

  • TreeMap存储key-value对时,需要根据key-value进行排序,

  • TreeMap可以保证所有的key-value处于有序状态

  • TreeSet底层使用红黑树结构存储数据

  • TreeMap对key的排序

    • 自然排序

      • TreeMap的所有的key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则会抛出ClassCastException
    • 定制排序

      • 创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中所有key进行排序。此时,不需要Map的key实现Comparable接口
  • TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0

思路

  • 若num1中有大于num2的数字,选择最小的大于num2的数字

  • 若num1中没有大于num2的数字,则选择num1中最小的数字

采用TreeMap存放num1中的数字,然后遍历num2,找num2中每个元素匹配的num1中的元素

代码和结果

在这里插入图片描述

import java.util.TreeMap;

class Solution {
    public int[] advantageCount(int[] nums1, int[] nums2) {

        TreeMap<Integer, Integer> map = new TreeMap<>();

        int len = nums1.length;

        int[] res = new int[len];

        for (int j : nums1) {
            map.put(j, map.getOrDefault(j, 0) + 1);
        }

        Integer key;

        //遍历nums2
        for (int i = 0; i < len; i++) {

            //找比nums2[i]大,但是与nums2[i]差距最小的num1中的数字
            key = map.higherKey(nums2[i]);

            //没找到
            if (key == null) {
                key = map.firstKey();
            }

            res[i] = key;
            if (map.get(key) == 1) {
                map.remove(key);
            } else {
                map.put(key, map.get(key) - 1);
            }
        }

        return res;
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值