题目二十一:接雨水问题(看当前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)| *,|F(4) F(3)| = |F(3) F(2)| *
,
...... |F(N) F(N-1)| = |F(N-1) F(N-2)| *,
可以得到 |F(N) F(N-1)| = |F(2) F(1)| *
而a,b,c,d可以根据有限几个方程得到,a=1,b=1,c=1,d=0
所以斐波那契问题就变成了求 |F(N) F(N-1)| = |F(2) F(1)| *
而一个数的N次方求解最快就是通过把N转换成二进制进行运算
eg: =
=1*
*
*
*
所以时间复杂度可有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);
}
}