刷题汇总

目录:

1、容器储水问题

2、给定一个数组arr,返回所有子数组的累加和中,最大的累加和

3、生成窗口最大值数组

4、给定两个字符串str1和str2,判断str2是不是由str1循环右移得到的

5、给定一个字符串str,和一个整数k,返回str向右循环右移k位后的结果

6、给定一个字符串类型的数组strs,找到一种拼接方式,使得把所有字符串拼起来之后形成的字符串具有最低的字典序。

7、会议室宣讲问题

8、最大的leftMax与rightMax之差的绝对值

 

【题目】

给定一个数组代表一个容器,比如[3,1,2,4],

代表0位置是一个宽度为1,高度为3的直方图。

代表1位置是一个宽度为1,高度为1的直方图。

代表2位置是一个宽度为1,高度为2的直方图。

代表3位置是一个宽度为1,高度为4的直方图。

所有直方图的底部都在一条水平线上,且紧靠着。

把这个图想象成一个容器,这个容器可以装3格的水。

给定一个没有负数的数组arr,返回能装几格水?

【思路】

  不能把求解方向放在计算形成多少个波峰与波谷之间,如果当两边界都很高时该方案会失效。

  方向就放在单看每个位置上面能够留下多少水上。对一个位置,看其左右两边所有位置的最大值,得到max左和max右,利用较小的max减去当前位置的高度,差值就是能够留下的水量,差值为负数则不能够留下水。

时间复杂度O(N),空间复杂度O(N)的方法:

  查询数据比较麻烦的时候可以往预处理数组上引。当有一个暴力解的时候,想想暴力点的目的是什么,是否我们能够有一些方法可以提前生成暴力点所需要的数据,以便不需要暴力求解。比方说上题,需要知道当前i位置的max左和max右,就需要遍历0~i-1位置和i+1~N-1位置,找出两个max值,每个位置都需要如此,则代价是N²。我们提前遍历数据,生成0-~i-1位置的max左值,存进辅助数组1,生成i~N-1max右值,存进辅助数据2,等下需要暴力求max的时候,直接根据当前i位置,在辅助数组中进行max的取值即可避免暴力,以一定的空间复杂度换取时间复杂度的降低。

最优解:

时间复杂度O(N),空间复杂度O(1)的方法:

  准备左指针和右指针,左指针指向1位置,右指针指向N-2位置,max左记录左指针之前的最大值,max右记录右指针之后的最大值。初始max左是0位置,初始max右是N-1位置。对比max左右,对较小一方,使用其max减去指针指向数值,当为负数时证明当前数值比max大,则更新max,否则累加装水量。完毕之后重复对比max,继续选择较小的一边执行上述操作。

【Code】 

public static int getWater(int[] arr)
    {
        if(arr==null || arr.length<3)
            return -1;
        int value = 0;
        int leftMax = arr[0];
        int rightMax = arr[arr.length-1];
        int l = 1;
        int r = arr[arr.length-2];
        while(l<=r)
        {
            if(leftMax<=rightMax)
            {
                value+=Math.max(0, leftMax-arr[l]);
                leftMax = Math.max(leftMax, arr[l++]);
            }
            else {
                value+=Math.max(0, rightMax-arr[r]);
                rightMax = Math.max(rightMax, arr[r--]);
            }
        }
        return value;
    }

【题目】

给定一个数组arr,返回所有子数组的累加和中,最大的累加和

【最优解】时间复杂度o(n),空间复杂度o(1)

  设置一个当前指针cur,变量max记录最大累加和。cur从0位置出发,累加上i位置的数,如果比max大,则更新max,如果累加后cur变成负数,则将cur重置为0,继续对下个位置进行累加,但不重置max

【证明】

  对一个数组,找到其最大最长子数组,记为arr,arr的前缀部分累加和一定为负数,因为如果arr的某段前缀如果为0或者正数,这一段一定会被加进arr中,但目前事实是arr并不包含该段前缀,则证明该部分的累加和一定为负数。且要求最大的累加和的话,要从0开始对arr的第一个位置进行累加。这就是为什么最优解当cur加完某个位置变成负数时要将cur重置为0的原因。

  同理反推,在arr数组当中的某一段前缀累加和必定为正数,如果不为正数,则应该抛弃这段前缀才对,但事实是这一段前缀被包含进了最大最长子数组arr中,所以必定为正数。最优解的这个流程不会错过最大的累加和的这个答案。

【Code】

public static int maxValue(int[] arr)
    {
        if(arr==null||arr.length==0)
            return 0;
        int max = Integer.MIN_VALUE;
        int cur = 0;
        for(int i = 0;i<arr.length-1;i++)
        {
            cur+=arr[i];
            max = Math.max(cur, max);
            cur = cur<0?0:cur;
        }
        return max;
    }

生成窗口最大值数组

【题目】

有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。
例如,数组为[4,3,5,4,3,3,6,7],窗口大小为3时:
[4 3 5] 4 3 3 6 7 窗口中最大值为5
4 [3 5 4] 3 3 6 7 窗口中最大值为5
4 3 [5 4 3] 3 6 7 窗口中最大值为5
4 3 5 [4 3 3] 6 7 窗口中最大值为4
4 3 5 4 [3 3 6] 7 窗口中最大值为6
4 3 5 4 3 [3 6 7] 窗口中最大值为7
如果数组长度为n,窗口大小为w,则一共产生n-w+1个窗口的最大值。
请实现一个函数。
输入:整型数组arr,窗口大小为w。
输出:一个长度为n-w+1的数组res,res[i]表示每一种窗口状态下的最大值。
以本题为例,结果应该返回{5,5,5,4,6,7}。

【思路】

1、明确窗口内最大/小值的更新结构

2、使用双端队列(可以从头进头出,尾进尾出):队列里从头到尾数据是严格从大到小的。窗口内的最大值就是头部元素。

1)加数规则:

默认先从尾进,之后对每个元素,对比队列内的数据,如果尾有元素比即将入队的元素小,则将其出队,直到队尾的元素比即将入队的元素大或者出空为止(相等也出队),然后再将新元素入队(即保证队列内从头到尾都是从大到小的顺序)

2)减数规则:

对左边界L,往右移动,对比移动后的新位置下标与队列中队头元素的下标,观察是否过期,如过期则出队头元素,如无则不动。

 

时间复杂度O(N),空间复杂度O(1):

具体解法:对这个窗口,右边加一个,左边减一个,然后使用一个数组记录当前队头元素,当右边走到超过数组末尾时返回。

注意,不保证每个更新的时候都是O(1),存在某个时刻单次可能是O(N),但平均下来是O(1)。

因为每个元素只可能入队和出队一次。

【Code】

public static int[] getMaxWindownums(int[] arr,int w)
    {
        if(arr==null||w<1||arr.length<w)
            return null;
        //w为窗口大小
        LinkedList<Integer> winMax = new LinkedList<>();
        int[] res = new int[arr.length-w+1];//窗口滑动到末会产生的最大值数量
        int index = 0;
        for(int i = 0;i<arr.length;i++)
        {
            //对比当前arr[i]与双向链表队尾元素,如大则持续出队列
            while(!winMax.isEmpty() && arr[winMax.peekLast()] <= arr[i] )
                winMax.pollLast();
            winMax.add(i);//进队
            //如果队头位置过期,则将其出队
            if(winMax.peekFirst()==i-w)
                winMax.pollFirst();
            if(i>=w-1)
                res[index++] = arr[winMax.peekFirst()];
        }
        return res;
    }

【题目】

想想一个字符串其实是个循环数组,可以循环右移。

比如”abc”,

向右循环右移一位,得到”cab”,

向右循环右移两位,得到“bca”,

向右循环右移三位,得到“abc”,

给定两个字符串str1和str2,判断str2是不是由str1循环右移得到的

【思路】

  首先判断str1str2长度是否相等,如不等则一定不是,如是则判断str2是否在str1+str1中,是则是。

如何判断str2是否在str1+str1中?使用KMP算法。

【Code】

public static boolean isright(String str1,String str2)
    {
        if(str1.length()!=str2.length()||str1==null||str2==null)
            return false;
        String str = str1+str1;
        return str.contains(str2);//或手动实现kmp
    }

【题目】

给定一个字符串str,和一个整数k,返回str向右循环右移k位后的结果

【思路】

将字符串在k位置分割成左右两半,先将左右分别逆序,再整体逆序即得到。

底层问题:如何将一个字符数组逆序(不用额外空间):对两端位置进行互换,然后往中间方向移动

【Code】

public static void reverse(char[] arr,int star,int end)
    {
        char temp = 0;
        while(star<end)
        {
            temp = arr[star];
            arr[star] = arr[end];
            arr[end] = temp;
            star++;end--;
        }
    }
    public static String moveK(String str,int k)
    {
        //先按k划分str。进行左右分别逆序,再整体进行逆序
        char[] arr = str.toCharArray();
        reverse(arr, 0, k-1);
        reverse(arr, k, arr.length-1);
        //整体逆序
        reverse(arr, 0, arr.length-1);
        return arr.toString();
    } 

贪心策略:

【题目】

给定一个字符串类型的数组strs,找到一种拼接方式,使得把所有字符串拼起来之后形成的字符串具有最低的字典序。

【思路】

1、字典序:

1)当长度相等时,从左第一个元素开始往右对比字符的阿斯克码大小。

2)当长度不等时,将长度较小的串末尾补0至长度相等再进行比较。

2、贪心策略:

str1,str2)两个字符串,如果字典序str1+str2<=str2+str1,则返回str1,排序位置将str1放在str2前面

3、不具备传递性的排序策略一定是错的。

4、首先需要证明提出的排序策略是有传递性的。

【Code】

public static String lowestString(String[] strs)
    {
        if(strs==null || strs.length == 0)
            return "";
        Arrays.sort(strs,new strComparator());
        String reS = "";
        for(int i = 0;i<strs.length-1;i++)
        {
            reS+=strs[i];
        }
        return reS;
    }
    //比较规则
    public static class strComparator implements Comparator<String>
    {
        public int compare(String a,String b)
        {
            return (a+b).compareTo(b+a);
        }
    }

【题目】

一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。给你每一个项目开始的时间和结束的时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程,要求会议室进行的宣讲的场次最多。返回这个最多的宣讲场次。

public static class Program {
        public int start;
        public int end;

        public Program(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }

    public static class ProgramComparator implements Comparator<Program> {

        @Override
        public int compare(Program o1, Program o2) {
            return o1.end - o2.end;
        }

    }

    public static int bestArrange(Program[] programs, int start) {
        Arrays.sort(programs, new ProgramComparator());
        int result = 0;
        for (int i = 0; i < programs.length; i++) {
            if (start <= programs[i].start) {
                result++;
                start = programs[i].end;
            }
        }
        return result;
    }

 

【题目】

最大的leftMax与rightMax之差的绝对值

给定一个长度为N(N>1)的整型数组arr,可以划分成左右两个部分,左部分为arr[0..K],右部分为arr[K+1..N-1],K可以取值的范围是[0,N-2]。求这么多划分方案中,左部分中的最大值减去右部分最大值的绝对值中,最大是多少?例如:[2,7,3,1,1],当左部分为[2,7],右部分为[3,1,1]时,左部分中的最大值减去右部分最大值的绝对值为4。当左部分为[2,7,3],右部分为[1,1]时,左部分中的最大值减去右部分最大值的绝对值为6。还有很多划分方案,但最终返回6。

public static int maxABS(int[] arr) {
        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++) {
            max = Math.max(arr[i], max);
        }
        return max - Math.min(arr[0], arr[arr.length - 1]);
    }

 

posted @ 2021-02-20 19:00  γGama  阅读(144)  评论(0编辑  收藏  举报