左神数据结构与算法(中级提升)——03

题目二十一:接雨水问题(看当前i位置情况)

给定一个数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器,请返回容器能装多少水。

eg:arr=[3,1,2,5,2,4],根据值画出的直方图就是容器形状,该容器可以装下5格水

eg:arr=[4,5,1,3,2],该容器可以装下2格水

看当前i位置和左侧的最大值以及右侧最大值之间的比较,左右两侧同时进行

package class04;

/**
 * 给定一个数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器,请返回容器能装多少水。
 * eg:arr=[3,1,2,5,2,4],根据值画出的直方图就是容器形状,该容器可以装下5格水
 * eg:arr=[4,5,1,3,2],该容器可以装下2格水
 */
public class Code05_RainWaterProblrm {

    //看i位置的左侧最大值,右侧最大值,判断其可以接多少雨水
    //左右两边同时进行判断,那边的最大值小,进行那边的判断
    public int getMaxRain(int[] arr){  //arr中元素均是非负的
        if(arr == null || arr.length < 3){
            return 0;
        }
        //arr[0] 和 arr[arr.length - 1]位置是没有办法接雨水的,直接略过即可
        int maxLeft = arr[0];
        int maxRight = arr[arr.length - 1];
        int left = 1;
        int right = arr.length - 2;
        int rainAll = 0;  //记录雨水的总量
        while (left <= right){
            if(maxLeft >= maxRight){  //右边的最大值小,先进行右边的判断
                rainAll += Math.max(maxRight - arr[right],0);
                maxRight = Math.max(maxRight,arr[right]);
                right--;
            }else { //左边的最大值小,先判断左边
                rainAll += Math.max(maxLeft - arr[left],0);
                maxLeft = Math.max(maxLeft,arr[left]);
                left++;
            }
        }
        return rainAll;
    }
}

题目二十二:数组左右两侧最大值差值(归结到0,n-1位置)

给定一个数组arr长度为N,你可以把任意长度大于0且小于N的前缀作为左部分,剩下的作为右部分。但是每种划分下都有左部分的最大值和右部分的最大值,请返回最大的,左部分最大值减去右部分最大值的绝对值。

package class04;

public class Code06_MaxArrayLeftRightSub {

    //先找出数组的最大值,在判断左边第一个和右边第一个的差值即可
    public int getSubMaxLeftRight(int[] arr){
        if(arr == null || arr.length == 0){
            return -1;
        }
        int max = Integer.MIN_VALUE;
        for(int i : arr){
            max = Math.max(max,i);
        }
        //当最大值包含在左侧时,需要找到右侧相对较小的最大值,而右侧一定会包含arr[arr.length - 1]
        // 当右侧最后一个数为右侧最大值时,右侧怎么变化,左右两侧最大值差值也是 max - arr[arr.length - 1]
        // 当右侧最后一个数不是右侧最大值时,那么左右两个的最大差值会变小,是不会取到的
        // 同理,最大值包含在右侧,只需要找左侧相对较小的最大值,而arr[0]是一定包含的
        // 因此,最后只需要判断arr[0] 和 arr[arr.length - 1]两个数的大小,再返回即可
        return arr[0] > arr[arr.length - 1] ? max - arr[arr.length - 1] : max - arr[0];
    }
}

题目二十三:旋转词(KMP算法)

如果一个字符串为str,把字符串str前面任意的部分挪到后面形成的字符串叫作str的旋转词。比如str=”12345”,str的旋转词有”12345”、”23451”、”34512”、”45123”和”51234”。给定两个字符串a和b,请判断a和b是否互为旋转词。

eg:a=”cdab”,b=”abcd” ,返回true

a=”1ab2”,b=”ab12” ,返回false

a=”2ab1”,b=”ab12” ,返回true

先判断两个字符串长度是否相等,不相等,直接返回false

相等,先生成a+a,再判断b是不是a+a的字串,是返回true

package class04;

/**
 * 如果一个字符串为str,把字符串str前面任意的部分挪到后面形成的字符串叫作str的旋转词。
 * 比如str=”12345”,str的旋转词有”12345”、”23451”、”34512”、”45123”和”51234”。
 * 给定两个字符串a和b,请判断a和b是否互为旋转词。
 * eg:a=”cdab”,b=”abcd” ,返回true
 * a=”1ab2”,b=”ab12” ,返回false
 * a=”2ab1”,b=”ab12” ,返回true
 */
public class Code07_RotatingWord {

    public boolean isRotatingWord(String str1,String str2){
        if(str1 == null && str2 == null){
            return true;
        }
        if((str1 == null && str2 != null) || (str1 != null && str2 == null)
                || str1.length() != str2.length()){ //长度不同,肯定不互为旋转词
            return false;
        }
        //生成str1+str1,判断str2是不是str1+str1的字串(通过KMP算法加速)
        //是字串,那么就互为旋转词,不是字串,就不互为旋转词
        String str = str1 + str1;
        return isSubString(str,str2);
    }

    //str1和str2均不是null
    //判断str2是不是str1的子串
    public boolean isSubString(String str1,String str2){
        int[] next = getStr2Next(str2);  //获得str2的next数组
        char[] char1 = str1.toCharArray();
        char[] char2 = str2.toCharArray();
        int i1 = 0;
        int i2 = 0;
        while(i1 < char1.length && i2 < char2.length){
            if(char1[i1] == char2[i2]){
                i1++;
                i2++;
            }else if(i2 == 0){ //next[i2] == -1; //判断子字符串的判断是否回到了第一个元素,无法再往前跳了
                //当子字符串回到第一个元素,说明当前str1中判断的元素已经不满足,要后移再继续判断
                i1++;
            }else {
                i2 = next[i2];
            }
        }
        return i2 == str2.length() ? true : false;
    }

    //获得最大相同字符串下标位置的数组
    public int[] getStr2Next(String str){
        char[] chars = str.toCharArray();
        int N = chars.length;
        int[] next = new int[N];
        //前两个位置认为规定,0位置前面没有为-1,1位置值是0,字符相同也是0,不相同也回到起始位置0
        next[0] = -1;
        next[1] = 0;
        int i = 2;
        int j = next[i - 1];
        for (;i < chars.length;i++){
            if (chars[i - 1] == chars[j]){
                next[i] = ++j;
            }else if(j > 0) {
                j = next[j];
            }else {
                next[i] = 0;
            }
        }
        return next;
    }
}

题目二十四:咖啡杯问题

给一个数组arr=[3,2,7],数组中每个元素代表对应咖啡机的工作效率(冲咖啡的时间),咖啡机每次只能冲一杯咖啡。N代表要喝咖啡的人,喝咖啡时间为0,a代表只有一台洗咖啡杯的机器,每次只能洗一杯,时间为a。b表示咖啡杯不洗,自然挥发也能变干净的时间。

问:这些人从冲咖啡到最后一个咖啡杯干净,至少需要多少时间?

package class04;

import org.junit.Test;

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

/**
 * 1.现根据arr求出n个人每个人拿到咖啡的最少时间 drink
 * 2.根据每个人拿到咖啡的时间,求出用咖啡机还是自然干的时间
 */
public class Code08_CofferMakerProblems {

    //通过小根堆的方式,求出每个人拿到咖啡的时间
    public class CofferMaker{
        public int timePoint;
        public int workTime;

        public CofferMaker(int timePoint,int workTime){
            this.timePoint = timePoint;
            this.workTime = workTime;
        }
    }

    public class CofferMakerComparator implements Comparator<CofferMaker> {
        @Override
        public int compare(CofferMaker o1, CofferMaker o2) {
            return (o1.timePoint + o1.workTime) - (o2.timePoint + o2.workTime);
        }
    }

    //arr 咖啡机工作时间,N 多少个人喝咖啡
    public int[] getPeopleDrinkTime(int[] arr,int N){
        int[] drink = new int[N];
        PriorityQueue<CofferMaker> priorityQueue = new PriorityQueue<>(new CofferMakerComparator());
        //先把这些机器加入小根堆,开始工作的初始时间都是0,工作时长为数组中的数值
        for(int i : arr){
            priorityQueue.add(new CofferMaker(0,i));
        }
        //开始获取每个人得到咖啡的时间
        for (int i = 0;i < drink.length;i++){
            CofferMaker cur = priorityQueue.poll();
            System.out.println(cur.timePoint + " : " + cur.workTime);
            drink[i] = cur.workTime + cur.timePoint;
            priorityQueue.add(new CofferMaker(drink[i],cur.workTime));
        }
        return drink;
    }

    /**
     *  根据获得每个人得到的咖啡时间,从左到右,是用咖啡机洗还是自然干,获得最小的时间
     * @param drink  每个人拿到咖啡的时间
     * @param a  洗咖啡杯洗一杯的时间
     * @param b  咖啡杯自然干的时间
     * @param index  当前的咖啡杯是要洗还是要自然干
     * @param washLine  洗咖啡杯能够洗的时间(若正在洗,则不能工作)
     * @return
     */
    public int process(int[] drink,int a,int b,int index,int washLine){
        if(index == drink.length - 1){
            // Math.max(washLine,drink[index])
            // 洗咖啡杯机器可以洗杯子的时间需要已经获得咖啡 ; 而获得咖啡需要判断洗咖啡杯的机器能不能给洗
            return Math.min(Math.max(washLine,drink[index]) + a,drink[index] + b);
        }
        //当前咖啡杯选择洗咖啡杯机器洗,洗咖啡杯的洗完时间
        int wash = Math.max(washLine,drink[index]) + a;
        //洗完剩下的被子的时间
        int next1 = process(drink,a,b,index + 1,wash);
        //当前杯子洗完的时间和剩下杯子洗碗的时间取最大
        int p1 = Math.max(wash,next1);
        //当前咖啡杯选择自然干
        int dry = drink[index] + b;
        int next2 = process(drink,a,b,index + 1,washLine);
        int p2 = Math.max(dry,next2);
        return Math.min(p1,p2);  //返回用机器洗和自然干的最小时间
    }

    //获得这些人从冲咖啡到最后一个咖啡杯干净,至少需要多少时间?
    public int minCofferTimes(int[] arr,int N,int a,int b){
        int[] drink = getPeopleDrinkTime(arr,N);
        return process(drink,a,b,0,0);
    }
}

题目二十五:数组任意相邻两个数乘积为4的倍数

给定一个数组arr,如果通过调整可以做到arr中任意两个相邻的数字相乘是4的倍数,返回true;如果不能返回false。

package class05;

import org.junit.Test;

import javax.swing.*;

/**
 * 给定一个数组arr,如果通过调整可以做到arr中任意两个相邻的数字相乘是4的倍数,返回true;如果不能返回false。
 */
public class Code01_Multi4 {

    //先遍历一遍数组,找到奇数个数,偶数中只含有一个2因子的个数,和包含4因子的个数
    public boolean Can4(int[] arr){
        int oddNum = 0;
        int num_2 = 0;
        int num_4 = 0;
        for (int i = 0;i < arr.length;i++){
            if (isOddNum(arr[i])){
                oddNum++;
            }else {
                if (arr[i] % 4 == 0){
                    num_4++;
                }else {
                    num_2++;
                }
            }
        }
        //判断只有一个2因子的个数
        //没有2因子,说明只有奇数和4的倍数,那么 奇4奇4奇4.....  这样排列即可
        //有2的因子,两个个2排列在一起可以成为4的倍数 22222,最后一个2后面需要加4的倍数
        // 即 222224...  那么排列方式 2222...4奇4奇4.....这样
        if(num_2 == 0){
            return oddNum == 0 ? (num_4 >= 1 ? true : false) : (num_4 >= oddNum - 1 ? true : false);
        }else {
            return num_4 >= oddNum ? true : false;
        }
    }

    //判断一个数是不是奇数
    public boolean isOddNum(int n){
        return n % 2 == 0 ? false : true;
    }
}

题目二十六:斐波那契公式推演

首先像斐波那契数列1,1,2,3,5,8...除了初始项,其他位置都有严格的推演公式,F(N)=F(N-1)+F(N-2),由于最小项为N-2,由N问题变成N-2,那么该公式可以变成一个二阶矩阵的问题,即有

|F(3) F(2)| = |F(2) F(1)| *\begin{vmatrix} a &b \\ c& d \end{vmatrix},|F(4) F(3)| = |F(3) F(2)| *\begin{vmatrix} a &b \\ c& d \end{vmatrix},

...... |F(N) F(N-1)| = |F(N-1) F(N-2)| *\begin{vmatrix} a &b \\ c& d \end{vmatrix},

可以得到 |F(N) F(N-1)| = |F(2) F(1)| *\begin{vmatrix} a & b\\ c& d \end{vmatrix}^{n-2}

而a,b,c,d可以根据有限几个方程得到,a=1,b=1,c=1,d=0

所以斐波那契问题就变成了求 |F(N) F(N-1)| = |F(2) F(1)| *\begin{vmatrix} 1 & 1\\ 1& 0 \end{vmatrix}^{n-2}

而一个数的N次方求解最快就是通过把N转换成二进制进行运算

eg: 10^{75}=10^{1001011}=1*10^{1}*10^{2}*10^{8}*10^{64}

所以时间复杂度可有O(N) -> O(logN)

因此,任意一个F(N)=aF(N-1)+bF(N-2)+cF(N-3)+dF(N-4)+eF(N-5)+...(最低到N-a可以转换和斐波那契一样的推演问题,因此求出该表达式的矩阵并建立F(N)的表达式

|F(N) F(N-1) F(N-2) F(N-3) .. F(N-a)|

=|F(a+1) .. F(3) F(2) F(1)| * |a阶矩阵|n-a

根据前n个表达式值求出a阶矩阵即可

//斐波那契数列 O(logN) 求解
public int fi(int n){
    if(n < 1){
        return 0;
    }
    if(n == 1 || n == 2){
        return 1;
    }
    int[][] base = {{1,1},
                       {1,0}};
    int[][] res = matrixPower(base,n-2);
    return res[0][0] + res[1][0];
}

//求解一个矩阵的p次方
public int[][] matrixPower(int[][] m,int p){
    int[][] res = new int[m.length][m[0].length];
    //先让res为单位矩阵,即对角线为1,其余都是0
    for (int i = 0;i < res.length;i++){
        res[i][i] = 1;
    }
    int[][] t = m;
    for (;p != 0;p >>= 1){  //每次二进制右移以为,看是不是等于1
        if((p & 1) != 0){
            res = multiMatrix(res,t);
        }
        t = multiMatrix(t,t);
    }
    return res;
}

//两个矩阵相乘
public int[][] multiMatrix(int[][] m,int[][] t){
    int[][] res = new int[m.length][m[0].length];
    for(int i = 0;i < res.length;i++){
        for (int j = 0;j < t[0].length;j++){
            for (int k = 0;k < t.length;k++){
                res[i][j] += m[i][k] * t[k][j];
            }
        }
    }
    return res;
}

斐波那契引申1:达标字符串

字符串只由’0’和’1’两种字符构成

当字符串长度为1时,所有可能的字符串为”0”,”1”

当字符串长度为2时,所有可能的字符串为”00”、 ”01”、 ”10”、 ”11”

当字符串长度为3时,所有可能的字符串为”000”、 ”001”、 ”010”、 ”011”、 ”100”、

”101”、 ”110”、 ”111”

.......

如果某一个字符串中,只要是出现’0’的位置,左边就靠着’1’,这样的字符串叫作达标字符串。

给定一个正数N,返回所有长度为N的字符串中,达标字符串的数量。

eg:N=3,返回3,因为只有”101”、”110”、”111”达标

对于N长度的字符串,要求有0,左边必须有1,那么可以直到首位必须是1

那么对于i长度的字符串,其左边是1的话,则有F(i)=F(i-1)+F(i-2),因此其就满足斐波那契数列,可直接根据斐波那契进行求解。

public int qualifiedString(int N){
    return fi(N+1);
}

斐波那契引申2:三角形问题

在迷糊的大草原上,小红捡到了n根木棍,第i根木棍的长度为i,小红现在很开心。想选出其中的三根木棍组成美丽的三角形。但是小明想捉弄小红,想去掉一些木棍,使得小红任意选三根木棍都不能组成三角形。

请问小明最少去掉多少根木棍呢?给定N,请返回至少去掉多少根?

斐波那契额数列:1 1 2 3 5 8 13 21 ...刚好无法构成三角形的情况,这也是无法构成三角形的最多情况,就是题意去掉最少的木棍形成。

//去掉最少的木棍,当前有N个数,从中去掉最少的
public int minSticks(int N){
    //获得斐波那契额数列中小于N最大的一个数
    // 1 1 2 3 5 8 13 21 .....
    int num = 1;
    while (fi(num) < N){
        num++;
    }
    return N - (num - 1);//num - 1是其中保留的无法拼成三角形的数
}

题目二十七:背包问题变形

牛牛准备参加学校组织的春游,出发前牛牛准备往背包中装入一些零食,牛牛的背包容量为w。

牛牛家里一共有n袋零食。第i袋零食体积为v[i]

牛牛想知道在总体积不超过背包容量的情况下,他一共有多少种零食放法(总体积为0也算一种)

package class05;

import org.junit.Test;

import java.util.Arrays;

/**
 * 牛牛准备参加学校组织的春游,出发前牛牛准备往背包中装入一些零食,牛牛的背包容量为w。
 * 牛牛家里一共有n袋零食。第i袋零食体积为v[i]
 * 牛牛想知道在总体积不超过背包容量的情况下,他一共有多少种零食放法(总体积为0也算一种)
 */
public class Code03_PackageSnacks {

    //v 零食体积数组 一共n长度
    //w 背包容量
    //返回背包可以装零食的方法总数
    public int getAll(int[] v,int w){
        if(w == 0){
            return 0;
        }
        int sum = 0;
        for(int i : v){
            sum += i;
        }
        if(sum <= w){ //背包容量比所有零食总和都要大,直接是2^n次
            return (int) Math.pow(2,v.length);
        }
        int res = 0;
        for (int i = 1;i <= w;i++){
            res += process(v,0,i,0);
        }
        return res;
    }

    //v 零食体积数组
    //index 当前在第index位置的零食,是否要装下
    //capacity 背包容量
    //total  当前已经装的容量
    public int process(int[] v,int index,int capacity,int total){
        if(index == v.length){
            return total == capacity ? 1 : 0;
        }
        int res = 0;
        //每个index都有放和不放的可能,放进背包的话 要满足当前背包容量加index物品的重量小于背包容量 才能放入
        if(v[index] + total <= capacity){
            res += process(v,index + 1,capacity,v[index] + total);
        }
        res += process(v,index + 1,capacity,total);
        return res;
    }

    @Test
    public void test(){
        int[] v = {4,1,5,2,1};
        int all = getAll(v, 8);
        System.out.println(all);
    }
}

题目二十八:找工作(有序表)

为了找到自己满意的工作,牛牛收集了每种工作的难度和报酬。牛牛选工作的标准是在难度不超过自身能力值的情况下,牛牛选择报酬最高的工作。在牛牛选定了自己的工作后,牛牛的小伙伴们来找牛牛帮忙选工作,牛牛依然使用自己的标准帮助小伙伴们。牛牛的小伙伴太多了,于是他只好把这个任务交给了你。(工作无限制)

class Job{

    public int money;

    public int hard;

    public Job(int money,int hard){

this.money = money;

       this.hard = hard;

    }

}

给定一个Job类型的数组jobarr,表示所有的工作。给定一个int类型的数组arr,表示所有小伙伴的能力。返回int类型的数组,表示每一个小伙伴按照牛牛的标准选择工作所能获得的报酬。

先对Job类型的数组进行分类,通过有序表存储下面筛选的。

把所有工作按难度由小到大,先删掉同样难度的低报酬,只保留同样难度的最高报酬;然后根据难度上升,报酬也要上升的原则进行删除难度大报酬低的职位。

package class05;

import org.junit.Test;

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

/**
 * 给定一个Job类型的数组jobarr,表示所有的工作。
 * 给定一个int类型的数组arr,表示所有小伙伴的能力。
 * 返回int类型的数组,表示每一个小伙伴按照牛牛的标准选择工作所能获得的报酬。
 */
public class Code04_JobChoose {

    class Job{
        public int money;
        public int hard;

        public Job(int hard,int money){
            this.hard = hard;
            this.money = money;
        }
    }

    class JobComparator implements Comparator<Job>{
        @Override
        public int compare(Job o1, Job o2) {
            return o1.hard == o2.hard ? (o2.money - o1.money) : (o1.hard - o2.hard);
        }
    }

    //把所有工作按难度由小到大,先删掉同样难度的低报酬,只保留同样难度的最高报酬;
    // 然后根据难度上升,报酬也要上升的原则进行删除难度大报酬低的职位。
    public int[] getJobMoney(Job[] jobarr,int[] peopleAbility){
        Arrays.sort(jobarr,new JobComparator());  //先对工作排序,难度由小到大,同难度的钱由大到小
        System.out.print("jobMoney=> ");
        for(Job j : jobarr){
            System.out.print(j.hard + ":" + j.money + "\t");
        }
        System.out.println();
        TreeMap<Integer,Integer> treeMap = new TreeMap<>();
        treeMap.put(jobarr[0].hard,jobarr[0].money);
        int preMoney = jobarr[0].money;
        for (int i = 1;i < jobarr.length;i++){
            //jobArr已经按照要求排好序,只保留相同工作难度,获得工作薪资最多的
            // 并且随难度升高,工作薪资也升高的工作
            if(!treeMap.containsKey(jobarr[i].hard) && jobarr[i].money > preMoney){
                treeMap.put(jobarr[i].hard,jobarr[i].money);
                preMoney = jobarr[i].money;
            }
        }
        System.out.print("treeMap=> ");
        for (Object o : treeMap.keySet()) {
            System.out.print(o + ":" + treeMap.get(o) + "\t");
        }
        System.out.println();
        int[] salary = new int[jobarr.length];
        for (int i = 0;i < salary.length;i++){
            Integer maxhard = treeMap.floorKey(peopleAbility[i]); //返回小于等于key的最大Key的key,没有返回null
            salary[i] = maxhard == null ? 0 : treeMap.get(maxhard);
        }
        return salary;
    }

    public Job[] jobRandom(){
        int N = (int)(Math.random() * 20 + 1);
        Job[] jobArr = new Job[N];
        int hard = 0;
        int money;
        for (int i = 0;i < N;i++){ //工作难度在1~10
            hard = (int)(Math.random() * 10) + 1;
            money = (int)(Math.random() * 20) + 1;
            jobArr[i] = new Job(hard,money);
        }
        return jobArr;
    }

    public int[] ability(int n){
        int[] ability = new int[n];
        for (int i = 0;i < n;i++){  //工作能力在0~15
            ability[i] = (int)(Math.random() * 15);
        }
        return ability;
    }

    @Test
    public void test(){
        int N = 100;
        for(int i = 0;i < 100;i++){
            Job[] jobArr = jobRandom();
            int[] ability = ability(jobArr.length);
            int[] pmoney = getJobMoney(jobArr,ability);
            System.out.print("ability=> ");
            System.out.println(Arrays.toString(ability));
            System.out.print("peopleMoney=> ");
            System.out.println(Arrays.toString(pmoney));
            System.out.println();
        }

    }
}

题目二十九:字符串转换(整数边界)

给定一个字符串,如果该字符串符合人们日常书写一个整数的形式,返回int类型的这个数;如果不符合或者越界返回-1或者报错。

分析:1.除了数字之外,只允许由’-’;

2.如果有’-’,开头只出现一次,且后面有数字,且不是0;

3.如果第一个字符是0,后续必须没有数字;

转换过程转换成负数,因为负数绝对值范围比正数的绝对值范围大一个,然后要考虑溢出的问题。

package class05;

/**
 * 给定一个字符串,如果该字符串符合人们日常书写一个整数的形式,返回int类型的这个数;
 * 如果不符合或者越界返回-1或者报错。
 */
public class Code05_DailyWritingOfNumericStrings {

    public int convert(String str){
        if(str == null || "".equals(str)){
            return 0;
        }
        char[] chars = str.toCharArray();
        if(!isVaild(chars)){
            throw new RuntimeException("can not convert");
        }
        boolean neg = chars[0] == '-' ? true : false; //是否有负号
        // 转换用负数表示,因为负数的绝对值范围比正数的绝对值范围大一个
        // 数字转换过程中,可能会存在溢出的情况
        int minq = Integer.MIN_VALUE / 10;
        int minr = Integer.MIN_VALUE % 10;
        int res = 0;
        int cur = 0;
        for(int i = neg ? 1 : 0;i < chars.length;i++){ //负数的有效下标从1开始,正数从0开始
            cur = '0' - chars[i];  //以负数的形式接着
            //判断是否会溢出
            if(res < minq || (res == minq && cur < minr)){
                throw new RuntimeException("can not convert");
            }
            res = res * 10 + cur;
        }
        // res数转换数字绝对值的负数表达,
        // Integer.MIN_VALUE  -2147483648
        // Integer.MAX_VALUE  2147483647
        // 2147483648越界 -> res=-2147483648不越界
        if(!neg && res == Integer.MIN_VALUE){
            throw new RuntimeException("can not convert");
        }
        return neg ? res : -res;
    }

    //判断是否是有效数字表达式
    public boolean isVaild(char[] chars){
        if(chars[0] != '-' && chars[0] < '0' || chars[0] > '9'){
            return false;
        }
        if(chars[0] == '-' && (chars.length == 1 || chars[1] == '0')){
            return false;
        }
        for (int i = 1;i < chars.length;i++){
            if(chars[i] < '0' || chars[i] > '9'){
                return false;
            }
        }
        return true;
    }
}

题目三十:前缀树

给你一个字符串类型的数组arr,譬如:

String[] arr = {“b\\cst”,”d\\”,”a\\d\\e”,”a\\b\\c”};

你把这些路径中蕴含的目标结构给画出来,于目录直接列在父目录下面,并比父目录向右前进两格,就像这样:

a

    b

       c

    d

       e

b

    cst

d

同一级的需要按照字母顺序排列,不能乱

分析:

注意Java”a\\d\\e”打印出来的是”a\d\e”,是因为转义字符的存在

在调用split(),需要用四个\\\\,其有转义和正则匹配(\\\\->\\->\)

package class06;

import org.junit.Test;

import java.util.TreeMap;

/**
 * 给你一个字符串类型的数组arr,譬如:
 * String[] arr = {“b\\cst”,”d\\”,”a\\d\\e”,”a\\b\\c”};
 * 你把这些路径中蕴含的目标结构给画出来,于目录直接列在父目录下面,并比父目录向右前进两格,就像这样:
 */
public class DirectoryListPrint {

    class Node{  //把字符串中每一级目录用节点表示,节点之间的路径表示目录名称
        String name;
        TreeMap<String,Node> nextMap; //有序表,保证按顺序打印

        public Node(String name){
            this.name = name;
            nextMap = new TreeMap<>();
        }
    }

    //打印目录
    public void printDirectoryString(String[] strings){
        if (strings == null){
            System.out.println();
        }
        Node head = generateFolderTree(strings);
        printFolderTree(head,0);
    }

    //根据所给的字符目录数组,生成一个前缀树
    private Node generateFolderTree(String[] folderPath){
        //先生成一个空节点,表示前缀树的头节点
        Node head = new Node("");
        for(String str : folderPath){
            String[] paths = str.split("\\\\"); //因为有转义和正则匹配,所以要用四个\
            Node cur = head;
            for (int i = 0;i < paths.length;i++){
                if (!cur.nextMap.containsKey(paths[i])){
                    cur.nextMap.put(paths[i],new Node(paths[i]));
                }
                cur = cur.nextMap.get(paths[i]);
            }
        }
        return head;
    }

    //打印一个前缀数  深度优先遍历
    private void printFolderTree(Node head,int level){
        if(level != 0){
            System.out.println(get2nSpace(level) + head.name);
        }
        for(Node next : head.nextMap.values()){
            printFolderTree(next,level + 1);
        }
    }

    //打印每级目录的前面空格
    private String get2nSpace(int level){
        String res = "";
        for (int i = 1;i < level;i++){
            res += "  ";
        }
        return res;
    }

    @Test
    public void test(){
        String[] strings = {"b\\cst","d\\","a\\d\\e","a\\b\\c"};
        printDirectoryString(strings);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值