力扣网---贪心算法(JAVA实现)---中等(一)

分类记录力扣网刷题的笔记。也算对自己的督促。

目录

  • 55,跳跃游戏
  • 134,加油站
  • 376,摆动序列
  • 406,根据身高重建队列
  • 452,用最少数量的箭引爆气球
  • 649,Dota参议院
  • 861.翻转矩阵后的得分

55,跳跃游戏

问题描述:
给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。

示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

测试用例与解答:

    public static void main(String[] args) {
        int[] nums={2,3,1,1,4};
        System.out.println(canJump(nums));

        int[] nums2={3,2,1,0,4};
        System.out.println(canJump(nums2));

        int[] nums3={2,0,0};
        System.out.println(canJump(nums3));
    }

    public static  boolean canJump(int[] nums) {
       for (int i = 0; i < nums.length; i++) {
            //判断是否到底了
            if(i==nums.length-1)return true;

            if(nums[i]==0){//需要跳过的位置
                int k=1;
                boolean flag=false;
                while (i-k>=0){
                    if(nums[i-k]>k){
                        flag=true;
                        k=nums.length;
                    }else {
                        k++;
                    }
                }
                if(!flag)return false; //表示是否跳过 0
            }
        }
         return true;
    }

134,加油站

问题描述:
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明:

如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:

输入: 
gas  = [1,2,3,4,5]
cost = [3,4,5,1,2]

输出: 3

解释:3 号加油站(索引为 3)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。
因此,3 可为起始索引。

示例 2:

输入: 
gas  = [2,3,4]
cost = [3,4,3]

输出: -1

解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

问题解答:

 public int canCompleteCircuit(int[] gas, int[] cost) {
        int[] nums=new int[gas.length];
        for (int i = 0; i < gas.length; i++) {
                nums[i]=gas[i]-cost[i];

        }

        int nowGas=0;
        for (int i = 0; i < nums.length; i++) {
            nowGas=nums[i];
            int index=i;
            while (nowGas>=0){
                if(index<nums.length-1){
                    index+=1;
                }else {
                    index=0;
                }
                nowGas+=nums[index];
                if(index==i){ //找到了加油站
                    return i;
                }
            }
        }
        return -1;
    }

376,摆动序列

问题描述:
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。

例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。

给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。

示例 1:

输入: [1,7,4,9,2,5]
输出: 6 
解释: 整个序列均为摆动序列。

示例 2:

输入: [1,17,5,10,13,15,10,5,16,8]
输出: 7
解释: 这个序列包含几个长度为 7 摆动序列,其中一个可为[1,17,10,13,10,16,8]

示例 3:

输入: [1,2,3,4,5,6,7,8,9]
输出: 2

问题解答:


     public static void main(String[] args) {
        int[] nums={1,7,4,9,2,5};
        System.out.println(wiggleMaxLength(nums));

        int[] nums2={1,17,5,10,13,15,10,5,16,8};
        System.out.println(wiggleMaxLength(nums2));


        int[] nums4={0,0};
        System.out.println(wiggleMaxLength(nums4)); // 1

        int[] nums5={3,3,3,2,5};
        System.out.println(wiggleMaxLength(nums5)); // 3


         int[] nums6={1,2,5,3,6,2,3,3,5,5,2};
         System.out.println(wiggleMaxLength(nums6)); // 1,5,3,6,2,5,2

    }
    public static  int wiggleMaxLength(int[] nums) {
        int down = 1, up = 1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] > nums[i - 1])
                up = down + 1;
            else if (nums[i] < nums[i - 1])
                down = up + 1;
        }
        return nums.length == 0 ? 0 : Math.max(down, up);

    }

406,根据身高重建队列

问题描述:

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

示例 1:

输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
解释:
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 01 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0123 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。

示例 2:

输入:people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
输出:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]

提示:

1 <= people.length <= 2000
0 <= hi <= 106
0 <= ki < people.length
题目数据确保队列可以被重建

问题解答:

      public static void main(String[] args) {

        int[][] people={{7,0},{4,4},{7,1},{5,0},{6,1},{5,2}};
        reconstructQueue(people); //[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]

        int[][] people2={{6,0},{5,0},{4,0},{3,2},{2,2},{1,4}};
        reconstructQueue(people2); // [[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]

    }

    public static int[][] reconstructQueue(int[][] people) {

//        int[][] res=new int[people.length][2];
//        Arrays.sort(people, new Comparator() {
//            @Override
//            public int compare(int[] o1, int[] o2) {
//                if (o1[0]==o2[0]) return o2[1]-o1[1];
//                return o1[0]-o2[0];
//                }
//        });
//        int[] d=new int[people.length];
//        for (int i = 0; i 
//             int[] list=people[i];
//             int k= list[1];
//             for (int j = 0; j 
//                 if(k<=0&&d[j]==0){
//                     res[j]=list;
//                     d[j]=1;
//                     break;
//                 }
//                 if(d[j]==0){
//                     k--;
//                     if(j==people.length-1&&d[j]==0){
//                         res[j]=list;
//                     }
//                 }
//             }
//        }
//        return res;


        //第二次实现:模仿官网
        Arrays.sort(people, new Comparator<int[]>() {
            @Override
            public int compare(int[] person1, int[] person2) {
                if (person1[0]==person2[0]) return person1[1]-person2[1];
                return person2[0]-person1[0];
                }
        });
        List<int[]> ans = new ArrayList<int[]>();
        for (int[] person : people) {
            ans.add(person[1], person);//使用List自带方法,添加到指定位置,后面的向后移动一位。
        }
//        System.out.println("");
//        for (int i = 0; i 
//           System.out.print(Arrays.toString(ans.get(i)));
//      }
        return ans.toArray(new int[ans.size()][]);



    }

452,用最少数量的箭引爆气球

问题描述:
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。

一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。

示例 1:

输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:对于该样例,x = 6 可以射爆 [2,8],[1,6] 两个气球,以及 x = 11 射爆另外两个气球

示例 2:

输入:points = [[1,2],[3,4],[5,6],[7,8]]
输出:4

示例 3:

输入:points = [[1,2],[2,3],[3,4],[4,5]]
输出:2

示例 4:

输入:points = [[1,2]]
输出:1

示例 5:

输入:points = [[2,3],[2,3]]
输出:1

提示:

0 <= points.length <= 104
points[i].length == 2
-231 <= xstart < xend <= 231 - 1

问题解答:

 public int findMinArrowShots(int[][] points) {
      if(points.length==0){
            return 0;
        }
        Arrays.sort(points, new Comparator<int[]>() {
            @Override
            public int compare(int[] point1, int[] point2) {
                if(point1[1]>point2[1]){
                    return 1;
                }else if(point1[1]<point2[1]){
                    return -1;
                }else{
                    return 0;
                }

            }
        });

        int pos=points[0][1];
        int ans=1;
        for (int[] ballon:points){
            if(ballon[0]>pos){
                pos=ballon[1];
                ++ans;
            }
        }
        return ans;
    }

649,Dota参议院

问题描述:
Dota2 的世界里有两个阵营:Radiant(天辉)和 Dire(夜魇)

Dota2 参议院由来自两派的参议员组成。现在参议院希望对一个 Dota2 游戏里的改变作出决定。他们以一个基于轮为过程的投票进行。在每一轮中,每一位参议员都可以行使两项权利中的一项:

禁止一名参议员的权利:

参议员可以让另一位参议员在这一轮和随后的几轮中丧失所有的权利。

宣布胜利:

      如果参议员发现有权利投票的参议员都是同一个阵营的,他可以宣布胜利并决定在游戏中的有关变化。

给定一个字符串代表每个参议员的阵营。字母 “R” 和 “D” 分别代表了 Radiant(天辉)和 Dire(夜魇)。然后,如果有 n 个参议员,给定字符串的大小将是 n。

以轮为基础的过程从给定顺序的第一个参议员开始到最后一个参议员结束。这一过程将持续到投票结束。所有失去权利的参议员将在过程中被跳过。

假设每一位参议员都足够聪明,会为自己的政党做出最好的策略,你需要预测哪一方最终会宣布胜利并在 Dota2 游戏中决定改变。输出应该是 Radiant 或 Dire。

示例 1:

输入: "RD"
输出: "Radiant"
解释:  第一个参议员来自  Radiant 阵营并且他可以使用第一项权利让第二个参议员失去权力,因此第二个参议员将被跳过因为他没有任何权利。然后在第二轮的时候,第一个参议员可以宣布胜利,因为他是唯一一个有投票权的人

示例 2:

输入: "RDD"
输出: "Dire"
解释: 
第一轮中,第一个来自 Radiant 阵营的参议员可以使用第一项权利禁止第二个参议员的权利
第二个来自 Dire 阵营的参议员会被跳过因为他的权利被禁止
第三个来自 Dire 阵营的参议员可以使用他的第一项权利禁止第一个参议员的权利
因此在第二轮只剩下第三个参议员拥有投票的权利,于是他可以宣布胜利

注意:

给定字符串的长度在 [1, 10,000] 之间.

问题解答:

 public static void main(String[] args) {
        System.out.println(predictPartyVictory("RD"));
        System.out.println(predictPartyVictory("RDD"));
    }

    //贪心思路:每一个人都禁止下一位不同阵营参议员的权力,当只有一个阵营游戏结束
    public static String predictPartyVictory(String senate) {
        int rNum=0;
        int dNum=0;
        for (int i = 0; i < senate.length(); i++) {
            if('R'==senate.charAt(i)) rNum++;
            else dNum++;
        }

        int[] people=new int[senate.length()];
        int deleteR=0,deleteD=0,index=0;
        while (true){
            if (people[index]==0){
                //判断是否有人限制了该参议员权力
                boolean radiant='R'==senate.charAt(index);
                boolean hasPower=true;
                if (radiant&&deleteR>0){
                    people[index]=1;
                    deleteR--;
                    hasPower=false;
                }
                if(!radiant&&deleteD>0){
                    people[index]=1;
                    deleteD--;
                    hasPower=false;
                }
                //该参议院使用权力,限制下一位不同阵营参议员
                if(hasPower&&radiant){
                    if(dNum--==0)return "Radiant";  //该参议院宣布获胜
                    deleteD++;
                }else if(hasPower&&!radiant){
                    if(rNum--==0) return "Dire";
                    deleteR++;
                }

            }
            index++;
            if(index==senate.length())index=0;
       }
    }

861.翻转矩阵后的得分

问题描述
有一个二维矩阵 A 其中每个元素的值为 0 或 1 。

移动是指选择任一行或列,并转换该行或列中的每一个值:将所有 0 都更改为 1,将所有 1 都更改为 0。

在做出任意次数的移动后,将该矩阵的每一行都按照二进制数来解释,矩阵的得分就是这些数字的总和。

返回尽可能高的分数。

示例:

输入:[[0,0,1,1],[1,0,1,0],[1,1,0,0]]
输出:39
解释:
转换为 [[1,1,1,1],[1,0,0,1],[1,1,1,1]]
0b1111 + 0b1001 + 0b1111 = 15 + 9 + 15 = 39

提示:
1 <= A.length <= 20
1 <= A[0].length <= 20
A[i][j] 是 0 或 1

解决方法:

public static void main(String[] args) {

//        int[][] b={{1,1,1,1},{1,0,0,1},{1,1,1,1}};
//        System.out.println(sumList(b));

        int[][] A={{0,0,1,1},
                   {1,0,1,0},
                   {1,1,0,0}};

//        System.out.println(matrixScore(A));

        int[][] A2={{0,1},{0,1},{0,1},{0,0}};
        System.out.println(matrixScore(A2));

    }

    //翻转所有 0的数量大于列或行的
    //开头第一位肯定为 1
    public static int matrixScore(int[][] A) {

        int count2=0;
        boolean flag;

        //判断行是否需要翻转,翻转第一位数为1.
        for (int i = 0; i <A.length ; i++) {
            if(A[i][0]==0){
                System.out.println("//翻转行");
                A=reserveList(A,i,0); //翻转行
            }
        }
        while (true){
            flag=true;
            for (int j = 0; j <A[0].length ; j++) {
                count2=0;
                for (int i = 0; i <A.length ; i++) {
                    if(A[i][j]==0){
                        count2+=1;
                        if(count2*2>A.length){
                            System.out.println("翻转列");
                            A=reserveList(A,-1,j);//翻转列
                            flag=false;
                            count2=0;
                            continue;
                        }
                    }
                }
            }
            if(flag)  break;
        }

        //计算值大小
        return sumList(A);
    }

    //计算值
    public static Integer sumList(int[][] list){
        int sum=0;
        for (int i = 0; i <list.length ; i++) {
            for (int j = list[i].length-1; j >=0 ; j--) {
                if(list[i][j]==1){
                    sum+=Math.pow(2,list[i].length-j-1);
                }
            }
        }
        return sum;
    }


    //a>=0 翻转行
    //b>=0 翻转列
    public static int[][]  reserveList(int[][] list,int a,int b){
        if(a>=0){
            for (int i = 0; i <list[0].length ; i++) {
                list[a][i]=list[a][i]==0?1:0;
            }
        }else if(b>=0){
            for (int i = 0; i <list.length ; i++) {
                list[i][b]=list[i][b]==0?1:0;
            }
        }

        for (int i = 0; i <list.length ; i++) {
            for (int j = 0; j <list[i].length ; j++) {
                System.out.print(list[i][j]+" ");
            }
            System.out.println(" ");
        }
        return list;
    }

你可能感兴趣的:(算法学习,算法,贪心算法,leetcode)