leetCode每日十题---排序(四)

题目描述1

在这里插入图片描述

笔者解答1.1

class Solution {
      public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> IList=new ArrayList<List<Integer>>();
        if(k>n)return IList;
        int[] match=new int[n+1];
        match[0]=1;
        int i;
        for(i=1;i<=n;i++)
           match[i]=0;
        List<Integer> minlist=new ArrayList<Integer>();
        circle(IList,n,1,match,k,minlist,0);
        return IList;
    }
    public static void circle(List<List<Integer>> IList,int n,int ii,int[] match,int k,List<Integer> minlist,int size){
      int i=ii;
      if(k==0&&size==minlist.size()){
          IList.add(new ArrayList<>(minlist));//这里是重点
      }else{
          for(;i<=n;i++){
              if(match[i]==0)
              {
                  minlist.add(i);
                  match[i]=1;
                  circle(IList,n,i+1,match,k-1,minlist,size+1);
                  match[i]=0;
                  minlist.remove(minlist.size()-1);
              }
          }
      }
    }
}

笔者分析1.2

初看这题确实挺简单的,思路也清晰,回溯呗。但自己写起代码来真的是毛病多多,虽然看别人代码再来看这题总会有种“不过如此”的感觉。下面说一说我在写这几十行代码出现的一些问题吧。递归函数里面的参数是为了减少时间开销多加的,有些大可不必,说重点,虽然每次生成的minlist里面的数据符合答案,但它貌似没有传进IList,或者说它是以空集的身份传进去的,导致最后return 的结果只有几个空集。也试过用静态变量,但结果一样。在写的时候我一直有个疑问,这个疑问也是导致最后结果错误的根本原因,我只给minlist开辟过一次空间,为什么能多次传入到IList。最后也是在网上查找了解决方法,形参终归是形参啊,最后传入到IList中要新分配空间(虽然有想过这一步,但一直不知道在哪开辟空间合适),将形参拷贝到一个新的空间传入。解决!!!

题目描述2

在这里插入图片描述

笔者解答2.1

class Solution {
    public List<Integer> countSmaller(int[] nums) {
       int n=nums.length;
       List<Integer> IList=new ArrayList<Integer>();
       if(n==0)return IList;
       List<Integer> sort=new ArrayList<Integer>();
       int i;
       for(i=n-1;i>=0;i--){
           int count=0;
            for(int j=0;j<sort.size();j++){
                  if(nums[i]>sort.get(j))
                     count++;
                  else 
                     break;   
              }
           IList.add(count);
           sort.add(nums[i]);
           Collections.sort(sort);
       }
       sort.clear();
       for(i=IList.size()-1;i>=0;i--){
           sort.add(IList.get(i));
       }
       return sort;
    }
}

笔者分析2.2

最简单的思路大家都清楚,遍历每个位置,和后面的元素依次比较,统计比当前位置小的个数,时间复杂度为O(n*n)。既然定义为困难,当然不能这么写,所以也是想了些其它办法,从后往前遍历,遍历的同时,将已遍历的元素排序,虽然在下一个元素统计数量的时候会比普通方法快些,但新增的排序时间复杂度貌似也拖了后腿,所以这种方法在最后一个用例超时了。

class Solution {
  private int[] index;
  private int[] helper;
  private int[] count;

  public List<Integer> countSmaller(int[] nums) {
    List<Integer> res = new ArrayList<>(nums.length);

    index = new int[nums.length];
    helper = new int[nums.length];
    count = new int[nums.length];
    for (int i = 0; i < index.length; i++) {
      index[i] = i;
    }

    merge(nums, 0, nums.length - 1);

    for (int i = 0; i < count.length; i++) {
      res.add(i, count[i]);
    }
    return res;
  }

  private void merge(int[] nums, int s, int e) {
    if (s == e || s > e) return;
    int mid = (s + e) >> 1;

    if (s < mid) {
      merge(nums, s, mid);
    }

    if (mid + 1 < e) {
      merge(nums, mid + 1, e);
    }

    int i = s, j = mid + 1;
    int hi = s;
    while (i <= mid && j <= e) {
      if (nums[index[i]] <= nums[index[j]]) {
        // 右侧出
        helper[hi++] = index[j++];
      } else {
        // 左侧出 计数
        count[index[i]] += e - j + 1;
        helper[hi++] = index[i++];
      }
    }

    while (i <= mid) {
      //左侧出
      helper[hi++] = index[i++];
    }

    while (j <= e) {
      // 右侧出
      helper[hi++] = index[j++];
    }

    for (int k = s; k <= e; k++) {
      index[k] = helper[k];
    }
  }
}

题目描述3

在这里插入图片描述

笔者解答3.1

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
      int n=intervals.length;
      if(intervals.length==0){
            int[][] res = new int[1][];
            res[0] = newInterval;
            return res;
        }
      int max=intervals[n-1][1]>newInterval[1]?intervals[n-1][1]:newInterval[1];
      int min=intervals[0][0]<newInterval[0]?intervals[0][0]:newInterval[0];
      int[] str=new int[max+1];
      int i;
      int match=1;
      for(i=0;i<n;i++){
          for(int j=intervals[i][0];j<=intervals[i][1];j++)
            {
                  str[j]=match;
            }
            match++;
      }   
      int temp_match=str[newInterval[1]];
      int temp_i=newInterval[1];
      while(temp_match!=0&&str[temp_i]==temp_match){
          str[temp_i]=-1;
          temp_i++;
          if(temp_i==max+1)break;
      }
      temp_match=str[newInterval[0]];
      temp_i=newInterval[0];
      while(temp_match!=0&&str[temp_i]==temp_match){
          str[temp_i]=-1;
          temp_i--;
          if(temp_i==-1)break;
      }
      for(i=newInterval[0];i<=newInterval[1];i++)
             str[i]=-1;      
      boolean catch_left=false;
      List<Integer> list=new ArrayList<Integer>();
      int count=0;
      int last_number=-2;
      for(i=min;i<=max;i++){
          if(!catch_left&&str[i]!=last_number&&str[i]!=0){
              list.add(i);
              last_number=str[i];
              catch_left=true;
          }
          else if((catch_left&&str[i]!=last_number)||(i==max&&str[i]!=0)){
              if(i!=max){
              catch_left=false;
              list.add(i-1);
              i--;
              }else
              list.add(i); 
               count++;
          } 
      }
      int[][] result=new int[count][2];
      count=0;
      for(i=0;i<list.size();i=i+2){
          result[count][0]=list.get(i);
          result[count][1]=list.get(i+1);
          count++;
      }         
      return result;
    }
}

笔者分析3.2

连我都觉得自己的代码没法看了,因为一直在根据答案调bug,漏洞太多了,代码补来补去。大致思路是,每个区间在一无限长的线段上都有自己特定的值,添加新的区间后,最后来计算该线段上不同值的区间范围,但一个2147483647把我给整懵了,这方法也算是废了。不过,需要考虑各种各样的情形的算法,检测到一半就该抛弃了,太就题论题了,而且也称不上算法,if,else到处都是。学习一下别人的方法吧。说实话,下面的代码才叫算法嘛。。。。在看看自己的,,,,代码习惯马上改!!!

class Solution {
    public int[][] insert(int[][] intervals, int[] newInterval) {
        int len = intervals.length;
        int[][] res = new int[len + 1][2];
        System.arraycopy(intervals, 0, res, 0, len);
        res[len] = newInterval;
        int[][] ans = new int[len + 1][2];
        int index = -1;
        Arrays.sort(res, (o1, o2) -> (o1[0] - o2[0]));
        for(int[] re: res){
            if(index == -1 || re[0] > ans[index][1]){
                ans[++index] = re;
            }else{
                ans[index][1] = Math.max(re[1], ans[index][1]);
            }
        }
        return Arrays.copyOf(ans, index + 1);
    }
}

题目描述4

在这里插入图片描述

笔者解答4.1

class Solution {
    public int jobScheduling(int[] startTime, int[] endTime, int[] profit) {
     List<List<Integer>> IList=new ArrayList<List<Integer>>();
     int i;
     int n=startTime.length;
     for(i=0;i<n;i++){
         List<Integer> list=new ArrayList<>();
         list.add(startTime[i]);
         list.add(endTime[i]);
         list.add(profit[i]);
         IList.add(list);
     }
     Collections.sort(IList,(o1,o2)->{
         return o1.get(1)-o2.get(1);
     });
     int[] max_money=new int[n];
     max_money[0]=IList.get(0).get(2);
     for(i=1;i<n;i++){
         int start_time=IList.get(i).get(0);
         int j=i-1;
         while(j>=0){
             if(start_time>=IList.get(j).get(1))
                break;
             j--;   
         }
         if(j==-1){
             int add_nowmoney=IList.get(i).get(2);
             int not_nowmoney=max_money[i-1];
             max_money[i]=add_nowmoney>not_nowmoney?add_nowmoney:not_nowmoney;
         }
         else{
             int add_nowmoney=(max_money[j]+IList.get(i).get(2));
             int not_nowmoney=max_money[i-1];
             max_money[i]=add_nowmoney>not_nowmoney?add_nowmoney:not_nowmoney;
         }
     }
     int max=-1;
     for(i=0;i<n;i++){
         if(max_money[i]>max)
          max=max_money[i];
     }
     return max;
    }
}

笔者分析4.2

好久没有体会到这种刷题的成就感了,用简洁的代码和明确的思路来解决一个看似复杂的题,太快乐了!!想这种多决策问题,每个阶段的决策都受前一阶段的影响,自然而然想到动态规划,接下来便是如何将该题一步步转换成动态规划模型了,一定证明自己的猜想。这次代码应该比上次可读性高些了吧。还有,对于复杂的题目,千万不要整体理解题目,不要想有多少个if,要分析步骤。

总结

有点小开心,不只是因为高效的写出了哪题,而是彻底理解了归并排序,平时很多题目都可以用该方法来接,而且我也是在这里吃了很多次亏,通过图形推敲,明白了哪些情况下适合用归并排序,并如何正确的书写归并算法,舒服!!今晚我会写一篇有关归并排序的详解以及其应用的博客,理解大家应该都懂,但重在应用!!!给排序画个圆满句号!每日打卡第十二天,以下图为证
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赶路的苟狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值