Candy(LeetCode)

题目:

There are N children standing in a line. Each child is assigned a rating value.

You are giving candies to these children subjected to the following requirements:

  • Each child must have at least one candy.
  • Children with a higher rating get more candies than their neighbors.

What is the minimum candies you must give?

题目分析:题目假设,有N个小孩子站成一条线。每个小孩子都有个分数rating。现在,你要给每个小孩子发糖。每个小孩子至少得分到一颗糖。对于两个相邻的小孩子,得分高的孩子必须得到糖的数目要比的得分低得孩子的糖要多。注意!当两个相邻的小孩子,得分一样时,其中一个小孩子可以比另一个孩子得到的糖要多。问你最少得发多少颗糖?


思路:

  1. 题目规定,每个孩子必须有一个糖,所以先给每个孩子都发一颗糖。
  2. 解题的大致思路是:
    • 得分最少的孩子A肯定只有一颗糖,是不会改变的。
    • 查看他附近的两个(或者一个,或者零个)孩子,如果得分比A高,但现在有的糖也只有一个。让这个A邻近的孩子的糖数,变为在A的基础上加1,变为2.
    • 然后去寻找得分第二少的孩子B。。。。得分第i少的孩子I。。。
    • 对于得分第i少的孩子I,同样查看I的近邻,如果近邻得分比I高,但是糖数没有多过I。让这个近邻的糖数变为在I的基础上加一个。
    • 如此循环,直到得分最高的孩子近邻的糖数被确定(其实,得分次高的孩子被处理过之后,整个糖数就确定了)
  3. 现在的问题是,每次都从剩下的孩子中找到分数最少的。花费的总时间应该是O(n^2)。之前用递归写过这样的算法,LeetCode上提示TimeLimitedExceed错误。
  4. 解决方法是,对孩子的ratings备份,然后进行排序,同时记录下他们的下标index的变化之后的位置
    • 例如,假设ratingsBackup=[5,2,4], index=[0,1,2];排完序之后,ratingsBackup=[2,4,5],index=[1,2,0]
    • 这样,我们从变化之后的index中直到,下标为1的孩子最低,即ratings[1]最小,其次ratings[2],最后ratings[0]
    • 有了index,我们就可以按照孩子的得分从小到大,遍历每一个孩子,然后用2中的方法解决问题
  5. 排序算法的使用
    • 为了减少复杂度,所以先对ratings备份进行了排序。所以排序算法必须得小于O(n^2),否则没有意义了。。。
    • 这里我选择了归并排序(merge sort),归并排序的算法在wiki上可以找到,这里不赘述
    • 值得注意的是,在排序的同时,要记得同时记录孩子们下标的改变(代码中的index)
    • 代码中的mergeSortFindIndex方法就是完成这个任务的。
    • 而merge方法又是被mergeSortFindIndex方法调用,用来完成归并算法中的归并操作的



注意点:

  • 在归并操作(merge方法)中,当对两个有序数组A和B进行归并时(A和B分别为ratings数组中的一段),需要临时存放在一个长度为A.length+B.length的数组C中。
  • 而每次进行归并操作,都新建一个数组C非常耗时。可以在开头声明一个static数组ratingsTemp,大小等同于rantings.length。
  • 在归并A和B时,只需要取A和B在ratingsTemp中对应的部分来用就行了。
  • 这样就避免了,多次创建数组
  • 同样适用于归并操作中的index



代码:

public class Solution {
    static int[] ratingsTemp;
    static int[] indexTemp;
    public int candy(int[] ratings) {
        int[] index = new int[ratings.length];
        int[] ratingsBackup = new int[ratings.length];
        int[] result = new int[ratings.length];
        ratingsTemp = new int[ratings.length];
        indexTemp = new int[ratings.length];
        for (int i = 0; i < ratings.length; i++){
            index[i] = i;
            ratingsBackup[i] = ratings[i];
            result[i] = 1;
        }
        mergeSortFindIndex(ratingsBackup,index,0,ratings.length-1);
        
        for (int i = 0; i < index.length; i++){
            int t = index[i];
            if (t > 0){
                if(ratings[t-1] > ratings [t] && result[t-1] <= result[t]){
                    result[t-1] = result[t] +1;
                }
            }
            if (t < index.length-1){
                if (ratings[t+1] > ratings[t] && result[t+1] <= result[t]){
                    result[t+1] = result[t] + 1;
                }
            }
        }
        
        int sum = 0;
        for (int i = 0; i < result.length; i++){
            sum += result[i];
        }
        return sum;
    }
    
    private static void mergeSortFindIndex(int[] ratings, int[] index, int begin, int end){
        if (end - begin < 1){
            return;
        } 
        mergeSortFindIndex(ratings, index, begin, (begin+end)/2);
        mergeSortFindIndex(ratings, index, (begin+end)/2+1, end);
        merge(ratings, index, begin, (begin+end)/2, (begin+end)/2+1, end);
    }
    
    private static void merge(int[] ratings, int[] index, int begin1, int end1, int begin2, int end2){
        int i = begin1;
        int i1 = begin1;
        int i2 = begin2;
        while (i1 <= end1 && i2 <= end2){
            if (ratings[i1] <= ratings[i2]){
                ratingsTemp[i] = ratings[i1];
                indexTemp[i] = index[i1];
                i1++;
                i++;
            }else {
                ratingsTemp[i] = ratings[i2];
                indexTemp[i] = index[i2];
                i2++;
                i++;
            }
        }
        if (i1 <= end1){
            while (i1 <= end1){
                ratingsTemp[i] = ratings[i1];
                indexTemp[i] = index[i1];
                i1++;
                i++;
            }
        }else{
            while (i2 <= end2){
                ratingsTemp[i] = ratings[i2];
                indexTemp[i] = index[i2];
                i2++;
                i++;
            }
        }
        for (i = begin1; i <= end2; i++){
            ratings[i] = ratingsTemp[i];
            index[i] = indexTemp[i];
        }
    }
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值