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

题目一:绳索占据最大点数(滑动窗口/二分查找)

给定一个有序数组arr,代表数轴上个从左到右有n个点arr[0]、arr[1]...arr[n-1],给定一个正数L,代表一根长度为L的绳子,求绳子最多能覆盖其中的几个点。

package class01;

import org.junit.Test;

/**
 * 给定一个有序数组arr,代表数轴上个从左到右有n个点arr[0]、arr[1]...arr[n-1],
 * 给定一个正数L,代表一根长度为L的绳子,求绳子最多能覆盖其中的几个点。
 */
public class Code01_MaxPointAboutRope {

    //方法一:绳子每占一个点,查找大于等于绳子最左边的点,计算个数
    public static int getMaxPointWithRope(int[] arr,int L){
        if(arr == null || arr.length == 0 || L <= 0){
            return -1;
        }
        int maxPointNum = 0;
        int leftIndex = -1;
        for(int i = 0;i < arr.length;i++){
            leftIndex = getLeftIndex(arr,arr[i]-L);
            maxPointNum = Math.max(maxPointNum,i - leftIndex + 1);
        }
        return maxPointNum;
    }

    //对于有序数组arr,找到 大于等于num 的最左一个数
    public static int getLeftIndex(int[] arr,int num){
        if(arr == null || arr.length == 0){
            return -1;
        }
        int left = 0;
        int right = arr.length - 1;
        while (left <= right){
            int middle = (left + right) / 2;
            if(arr[middle] > num){
                right = middle - 1;
            }else if(arr[middle] < num){
                left = middle + 1;
            }else if(arr[middle] == num){
                return middle;
            }
        }
        return left; //如果二分没有找到那个数,那么现在left指针肯定指在大于等于这个数的下标上
    }
    
    //方法二:滑动窗口的办法,绳子左端先固定,查看右边距离是否大于数组元素,大于可以,小于则长度不够,结束
    public static int getMaxPointNum(int[] arr,int L){
        if(arr == null || arr.length == 0 || L <= 0){
            return -1;
        }
        int maxPointNum = 0;  //记录最大点数
        int index = -1;
        for(int i = 0;i < arr.length;i++){
            index = i;
            int num = 0;
            while (index < arr.length && arr[i] + L >= arr[index]){
                num++;
                index++;
            }
            maxPointNum = Math.max(maxPointNum,num);
        }
        return maxPointNum;
    }

    @Test
    public void test(){
        int[] arr = {1,4,7,9,11,22,44};
        int L = 5;
        int num = getMaxPointNum(arr, L);
        System.out.println("num = " + num);
    }
}

题目二:整型输入输出规律问题

1.小虎去附近的商店买水果,奸诈的商贩使用了捆绑交易,只提供6个每袋和8个每袋的包装包袋不可拆分。可是现在小虎只想购买恰好n个苹果,小虎想购买尽量少的袋数方便携带。如果不能购买恰好n个苹果,小虎将不会购买。输入一个整数n,表示小虎想购买的苹果,返回最小使用多少袋子。如果无论如何都不能正好装下,返回-1。

package class01;

import org.junit.Test;

/**
 * 小虎区附近的商店买水果,奸诈的商贩使用了捆绑交易,只提供6个每袋和8个每袋的包装包袋不可拆分。
 * 可是现在小虎只想购买恰好n个苹果,小虎想购买尽量少的袋数方便携带。
 * 如果不能购买恰好n个苹果,小虎将不会购买。
 * 输入一个整数n,表示小虎想购买的苹果,返回最小使用多少袋子。如果无论如何都不能正好装下,返回-1。
 */
public class Code02_ApplePackingBags {

    //方法一:尽量使用8类型的袋子,然后减
    public static int getPackingBags(int apple){
        if(apple < 0){
            return -1;
        }
        int bag_6 = -1;
        int bag_8 = apple / 8;   //全用8类型的袋子,最多用多少个
        int rest = apple - 8 * bag_8;
        //6和8 的最小公倍数为 24,因此大于24过后,数字可变成 24 + x ,则会重复之前的操作
        while (bag_8 >= 0 && rest < 24){
            if(rest % 6 == 0){
                bag_6 = rest / 6;
                break;
            }
            rest = apple - 8 * (--bag_8);
        }
        return bag_6 == -1 ? -1 : bag_6 + bag_8;
    }

    //方法二:对于输入是整型,输出是整型,根据已有的方法,找出输出整型的规律
    //找到他们的规律,不需要关心为什么产生
    public static int getPackingBags2(int apple){
        if( apple < 0 || (apple & 1) != 0){  //奇数就输出-1
            return -1;
        }

        if(apple < 18){
            return apple == 0 ? 0 : (apple == 6 || apple == 8) ? 1 :
                    (apple == 12 || apple == 14 || apple == 16) ? 2 : -1;
        }
        return (apple - 18) / 8 + 3;
    }

    @Test
    public void test(){
        for(int i = 0;i <= 100;i++){
            int res = getPackingBags(i);
            System.out.println(i + " : " + res);
        }
//        int N = 20;
//        int packingBags = getPackingBags(N);
//        System.out.println(packingBags);
    }
}

2.牛吃草的问题

先手和后手均可以以4的倍数进行吃草,给定一份n的草,问谁赢?

package class01;

/**
 * 牛羊吃草问题
 */
public class Code03_Eat {

    // n份青草放在一起
    // 先手后手都决定聪明
    // string "先手" "后手"   都可以选择吃 1、4、16 、4^n 份
    public static String winner1(int n){
        // 0  1  2  3  4
        // 后 先  后 先 先
        if(n < 5){  //base case
            return (n == 0 || n == 2) ? "后手" : "先手";
        }
        // n >= 5 时
        int base = 1;
        while(base <= n){
            // 当前一共n份草,先手吃掉的是base份,n-base 是留给后手的草
            // 母过程 先手   在子过程里面是  后手
            if(winner1(n - base).equals("后手")){
                return "先手";
            }
            if(base > n * 4){  //防止base*4溢出
                break;
            }
            base *= 4;    //以4的倍数吃草
        }
        return "后手";
    }

    //  根据winner1() 方法的输出规律得到以下的方法
    public static String winner2(int n){
        if(n % 5 == 0 || n % 5 == 2){
            return "后手";
        }else {
            return "先手";
        }
    }
}

题目三:预处理(查询代价频繁)

1.牛牛有一些排成一行的正方形。每个正方形已经被染成红色或者绿色。牛牛现在可以选择任意一个正方形然后用这两种颜色的任意一种进行染色,这个正方形 的颜色将会被覆盖。牛牛的目标是在完成染色之后,每个红色R都比每个绿色G距离最左侧近。牛牛想知道他最少需要涂染几个正方形。

eg:s=RGRGR

涂染之后变成 RRRGG就满足要求,涂染个数为2,没有比这更好的涂染方案。

package class01;

import org.junit.Test;

import java.util.Arrays;

public class Code04_ColorRedGreen {

    //方法一:直接处理,时间复杂度大  
    public static int getMinPaint1(String s){   //时间复杂度 O(n^2)
        if(s == null || s.length() < 2){
            return 0;
        }
        char[] chars = s.toCharArray();
        int N = chars.length;
        int minPoint = Integer.MAX_VALUE;
        for(int i = 0;i <= N;i++){  //0个R,1个R,...
            int count = 0;
            if (i == 0){  //全是G   R变成G
                for(char c : chars){
                    if(c == 'R'){
                        count++;
                    }
                }
            }else if (i == N){ //全是R   G变成R
                for(char c : chars){
                    if(c == 'G'){
                        count++;
                    }
                }
            }else {
                //一般情况下,左侧看看有多少个G变成R,右侧有多少个R变成G
                for (int j = 0; j < i; j++) {
                    if (chars[j] == 'G') {
                        count++;
                    }
                }
                for (int j = i; j < N; j++) {
                    if (chars[j] == 'R') {
                        count++;
                    }
                }
            }
            minPoint = Math.min(minPoint,count);
        }
        return minPoint;
    }

    //方法二:先申请数组记录 R 和 G 的个数,这样到达指定位置直接拿即可,不需要再查询
    public static int getMinPaint2(String s) {
        if(s == null || s.length() < 2){
            return 0;
        }
        char[] chars = s.toCharArray();
        int[] arr_R = getNumi_N(chars,'R');  //i~N-1位置R的个数
        int[] arr_G = getNum0_i(chars,'G');  //0~i位置G个数
        int N = chars.length;
        int minPoint = Integer.MAX_VALUE;
        for(int i = 0;i <= N;i++){ //0个R,1个R,... 左侧的G变成R  右侧的R变成G  
            if(i == 0){  //都变为G   查询R的个数
                minPoint = Math.min(minPoint,arr_R[0]);
            }else if(i == N){   //都变为R   查询G的个数
                minPoint = Math.min(minPoint,arr_G[N-1]);
            }else {  //一般情况  i位置左侧查询G的个数 + i位置以及i右侧查询R的个数
                minPoint = Math.min(minPoint, arr_G[i-1] + arr_R[i]);
            }
        }
        return minPoint;
    }

    //返回chars上 0~每个位置之间(包含该位置) 的 c 的对应个数 数组
    public static int[] getNum0_i(char[] chars,char c){
        if(chars == null){
            return null;
        }
        int[] arr = new int[chars.length];
        arr[0] = chars[0] == c ? 1 : 0;
        for (int i = 1;i < chars.length;i++){
            arr[i] = arr[i - 1];
            arr[i] += chars[i] == c ? 1 : 0;
        }
        return arr;
    }

    //返回chars上 每个位置之间(包含该位置)~N-1 的 c 的对应个数 数组
    public static int[] getNumi_N(char[] chars,char c){
        if(chars == null){
            return null;
        }
        int N = chars.length;
        int[] arr = new int[N];
        arr[N - 1] = chars[N - 1] == c ? 1 : 0;
        for (int i = N - 2;i >= 0;i--){
            arr[i] = arr[i+1];
            arr[i] += chars[i] == c ? 1 : 0;

        }
        return arr;
    }

    //生成一个随机长度的RG数组
    public static String getRandom(){
        int N = (int)(Math.random() * 100 + 1);
        StringBuffer s = new StringBuffer(N);
        for(int i = 0;i < N;i++){
            if((int)(Math.random() * 2) == 0){
                s.append('R');
            }else {
                s.append('G');
            }
        }
        return s.toString();
    }

    @Test
    public void test(){
        for(int i = 1;i <= 100;i++){
            String s = getRandom();
            int minPoint1 = getMinPaint1(s);
            int minPoint2 = getMinPaint2(s);
            System.out.print(s + " : " + minPoint1 + " : " + minPoint2);
            if(minPoint1 != minPoint2){
                System.out.print("\t" + "false");
                break;
            }
            System.out.println();
        }
    }
}

2.给定一个N*N的矩阵matrix,只有0和1两种值,返回边框全是1的最大正方形的边长长度。

eg:

01111

01001

01001

01111

01011

其中边框全为1的最大正方形的大小为4*4,所以返回4

package class01;

import org.junit.Test;

import java.util.Arrays;

public class Code05_MaxBorderSize {

    public static int getMaxOneBorder(int[][] arr){  //时间复杂度O(N^3)
        if(arr == null){
            return 0;
        }
        int N = arr.length;
        int M = arr[0].length;
        int[][] rightArr = getRightOne(arr);  //右边连续为1的数量(包含该位置)
        int[][] downArr = getDownOne(arr);   //下边连续为1的数量(包含该位置)
//        print(rightArr);
//        System.out.println();
//        print(downArr);
        int maxBorderSize = Integer.MIN_VALUE;
        for(int row = 0;row < N;row++){
            for(int col = 0;col < M;col++){
                //确定正方形的左上角顶点 (row,col)
                // 正方形宽度为 border ,依次枚举
                for (int border = 1;border <= Math.min(N - row,M - col);border++){
                    //判断其四条边的值是不是 1   四个边都需要判断
                    //这里使用预处理的数组信息,可以直接得到右侧或者下面连续1的数量
                    //如果不使用预处理信息,在这里需要对四条边进行遍历,判断每条边的值是不是为1,则整体时间复杂度会上升为O(N^4)
                    if(rightArr[row][col] < border && downArr[row][col + border - 1] < border
                            && rightArr[row + border - 1][col] < border){
                        break;
                    }
                    maxBorderSize = Math.max(maxBorderSize,border);
                }

            }
        }
        return maxBorderSize;
    }

    //获得该位置以及该位置右侧连续1的数量
    public static int[][] getRightOne(int[][] arr){
        if(arr == null){
            return null;
        }
        int N = arr.length;     //N行
        int M = arr[0].length;  //M列
        int[][] rightArr = new int[N][M];
        for(int row = 0;row <= N - 1;row++){
            rightArr[row][M - 1] = arr[row][M - 1] == 0 ? 0 : 1;
            for(int j = M - 2;j >= 0;j--){
                if(arr[row][j] == 0){
                    rightArr[row][j] = 0;
                }
                if(arr[row][j] == 1){
                    rightArr[row][j] = rightArr[row][j + 1] + 1;
                }
            }
        }
        return rightArr;
    }

    //获得该位置以及该位置下面连续1的数量
    public static int[][] getDownOne(int[][] arr){
        if(arr == null){
            return null;
        }
        int N = arr.length;
        int M = arr[0].length;
        int[][] downArr = new int[N][M];
        for(int col = 0;col <= M - 1;col++){
            downArr[N - 1][col] = arr[N - 1][col] == 0 ? 0 : 1;
            for(int j = N - 2;j >= 0;j--){
                if(arr[j][col] == 0){
                    downArr[j][col] = 0;
                }
                if(arr[j][col] == 1){
                    downArr[j][col] = downArr[j + 1][col] + 1;
                }
            }
        }
        return downArr;
    }

    public static void print(int[][] arr){
        for(int[] a : arr){
            System.out.println(Arrays.toString(a));
        }
    }

    @Test
    public void test(){

        int[][] arr = {
                {0,1,1,1,1,1},
                {0,1,0,0,1,1},
                {0,1,0,0,1,1},
                {0,1,1,1,1,1},
                {0,1,0,1,1,1}
        };
        int maxOneBorder = getMaxOneBorder(arr);
        System.out.println(maxOneBorder);
    }
}

题目四:概率返回问题

给定一个函数f,可以1~5的数字等概率返回一个。请加工出1~7的数字等概率返回一个的函数g。

public class Code06_Random5To7 {
    
    public static int f(){
        return (int)(Math.random() * 5) + 1;
    }
    
    //等概率返回0,1
    public static int r01(){
        int res = 0;
        do{
            res = f();
        }while (res == 3);
        return res < 3 ? 0 : 1;
    }
    
    //通过二进制位拼
    //返回1~7 相当于就是等概率返回0~6 + 1
    public static int g(){
        int res = 0;
        do{
            res = (r01() << 2) + (r01() << 1) + r01();   //0~7
        }while (res == 7);
        return res + 1;
    }
}

给定一个函数f,可以a~b的数字等概率返回一个。请加工出c~d的数字等概率返回一个的函数g。

f()-> r01()->g()

现根据f函数,设计出等概率返回01的函数r01(),然后根据c~d之间数字个数,得到需要多少位n,然后根据r01()得到0~2^n之间信息,在舍弃不需要的信息,最后返回的数加上c即可。

给定一个函数f,以p概率返回0,以1-p概率返回1。请加工出等概率返回0和1的函数g。

f函数->  0的概率是p1的概率是1-p

    对该函数进行两次返回,得到00,重新做,得到11,重新做;得到 01,返回0,得到10,返回1。因为01的概率是p*(1-p)  10的概率是(1-p)*p

题目五:简单的动态规划

给定一个非负整数,代表二叉树的节点个数。返回能形成多少中不同的二叉树结构

package class01;

/**
 * 给定一个非负整数,代表二叉树的节点个数。返回能形成多少中不同的二叉树结构
 */
public class Code07_BSTNum {

    public static int process(int n){
        if(n < 0){
            return 0;
        }
        if(n == 0){  //空树
            return 1;
        }
        if(n == 1){
            return 1;
        }
        if(n == 2){
            return 2;
        }
        int res = 0;
        for(int leftTreeNodeNum = 0;leftTreeNodeNum <= n - 1;leftTreeNodeNum++){
            int leftWays = process(leftTreeNodeNum);
            int rightWays = process(n - 1 - leftWays);
            res += leftWays * rightWays;
        }
        return res;
    }

    public static int numTrees(int n){
        if(n < 2){
            return 1;
        }
        int dp[] = new int[n + 1];
        dp[0] = 1;
        for (int i = 1;i < n + 1;i++){ //节点个数为 i时
            for (int j = 0;j <= i - 1;j++){  // 左侧节点个数为 j ,右侧节点个数为 i - j - 1
                dp[i] += dp[j] * dp[i - j - 1];
            }
        }
        return dp[n];
    }
}

题目六:括号字符串

1.添加括号问题

一个完整的括号字符串定义规则如下:

①空字符串是完整的

②如果s是完整的字符串,那么(s)也是完整的

③如果s和t是完整的字符串,将它们连接起来形成的字符串st也是完整的

eg:”(()())”,和”(())()”是完整的括号字符串,”())(”,”()(”和”)”是不完整的括号字符串。

牛牛有一个括号字符串s,现在需要在其中任意位置尽量少地添加括号,将其转化为一个完整的括号字符串请问牛牛至少需要添加多少个括号。

package class01;

/**
 * 牛牛有一个括号字符串s,现在需要在其中任意位置尽量少地添加括号,将其转化为一个完整的括号字符串请问牛牛至少需要添加多少个括号。
 */
public class Code08_NeedParentheses {

    public static int needParentheses(String str){
        int count = 0;
        int needSolveRight = 0;
        for(int i = 0;i < str.length();i++){
            if(str.charAt(i) == '('){
                count++;
            }else {  //遇到的是')'
                if(count == 0){ //左右括号已经匹配了,又碰到了右括号')'
                    needSolveRight++;
                }else {
                    count--;
                }
            }
        }
        return count + needSolveRight;
    }
}

2.括号序列深度问题(嵌套括号大小)

 一个合法的括号匹配序列有以下定义:

①空串””是一个合法的括号匹配序列

②如果”X”和”Y”都是合法的括号匹配序列,”XY”也是一个合法的括号匹配序列

③如果”X”是一个合法的括号匹配序列,那么”(X)”也是一个合法的括号匹配序列

④每个合法的括号序列都可以由以上规则生成。

eg:””,”()”,”()()”,”((()))”都是合法的括号序列

对于一个合法的括号序列我们又有以下定义它的深度:

①空串””的深度是0

②如果字符串”X”的深度是x,字符串”Y”的深度是y,那么字符串”XY”的深度为max(x,y)

③如果”X”的深度是x,那么字符串”(X)”的深度是x+1

eg:”()()()”的深度是1,”((()))”的深度是3。

牛牛现在给你一个合法的括号序列,需要你计算出其深度。

package class02;

/**
 * 牛牛现在给你一个合法的括号序列,需要你计算出其深度。
 * ①空串””的深度是0
 * ②如果字符串”X”的深度是x,字符串”Y”的深度是y,那么字符串”XY”的深度为max(x,y)
 * ③如果”X”的深度是x,那么字符串”(X)”的深度是x+1
 */
public class Code03_ParenthesesDepth {

    //str 是给定的一个合法的括号序列
    // 返回该括号序列的深度  相当于就是得到该序列的最大括号嵌套
    public static int parenthesesDepth(String str) {
        if (str == null) {
            return -1;
        }
        if (str == "") {
            return 0;
        }
        char[] chars = str.toCharArray();
        int maxDepth = Integer.MIN_VALUE;
        int count = 0;
        //遇到左括号就加,右括号就减,最大嵌套深度每次都要和count比较
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '(') {
                count++;
                maxDepth = Math.max(maxDepth, count);
            } else {
                count--;
            }
        }
        return maxDepth;
    }
}

3.最大合法子括号字符串长度

给定一个的括号序列,求出其最大的合法子括号字符串长度

package class02;

import org.junit.Test;

import java.util.Arrays;

/**
 * 最大的子括号字符串的长度
 */
public class Code04_MaxSubParenthesesLength {


    public static int getMaxSubParentesesLength(String str){
        if (str == null || str == ""){
            return 0;
        }
        int N = str.length();
        char[] chars = str.toCharArray();
        int[] maxLengthArr = new int[N];    // 记录以每个位置结尾的最大合法子括号字符串长度
        maxLengthArr[0] = 0;  //第一个位置肯定是0,该位置左侧什么都没有,无法构成
        // 以( 结尾肯定是 0
        int maxLength = Integer.MIN_VALUE;
        for(int i = 1;i < N;i++){
            if(chars[i] == '('){
                maxLengthArr[i] = 0;
            }else {
                //     ( ( ) ( ) ) ( ) ( )
                //     0 1 2 3 4 5 6 7 8 9
                //dp[] 0 0 2 0 4 6 0 8 0 10
                //   当前在i位置,其为 ) ,要知道i-1位置的括号长度x之前的一位是不是 (,
                //  即 i-1-x 的位置是不是 ),
                //  是的话至少长度位i-1位置的长度加2,并且要加(i-1-x)-1的长度(因为前面可能也是连续的并结束的)
                int cur = maxLengthArr[i - 1]; //获得前一个位置的合法括号长度
                if (i - 1 - cur - 1 >= 0 && chars[i - 1 - cur] == '('){
                    maxLengthArr[i] = maxLengthArr[i - 1] + 2 + maxLengthArr[i - 1 - cur - 1] ;
                }else{
                    maxLengthArr[i] = 0;
                }
            }
            maxLength = Math.max(maxLength,maxLengthArr[i]);
        }
        System.out.println(Arrays.toString(maxLengthArr));
        return maxLength;
    }

    @Test
    public void test(){
        String str = "()())(()())()))";
        System.out.println(getMaxSubParentesesLength(str));

    }
}

题目七:去重差值数字对

给定一个数组arr,求差值为k的去重数字对。

package class02;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

/**
 * 给定一个数组arr,求差值为k的去重数字对。
 */
public class Code01_DifferentValueEqualsK {
    
    public static List<List<Integer>> allPair(int[] arr,int k){
        HashSet<Integer> hashSet = new HashSet<>();  //哈希表不包含重复值
        for(int i = 0;i < arr.length;i++){
            hashSet.add(arr[i]);
        }
        List<List<Integer>> list = new ArrayList<>();
        for(Integer i : hashSet){
            if(hashSet.contains(i + k)){
                list.add(Arrays.asList(i,i + k));
            }
        }
        return list;
    }
}

题目八:平均值大的向平均值小的数组放数据的次数

给一个包含n个整数元素的集合a,一个包含m个元素的集合b。

定义一个magic操作位,从一个集合中取出一个元素,放到另一个集合里,且操作过后每个集合的平均值都大于操作前。

注意以下两点:

1>不可以把一个集合的元素取空,这样就没有平均值了

2>值位x的元素从集合b取出放入集合a,但集合a中已经有值位x的元素,则a的平均值不变(因为集合元素不会重复),b的平均值可能会被改变(因为x被取出了)

问最多可以进行多少次magic操作?

package class02;

import java.util.Arrays;
import java.util.HashSet;

/**
 *   给一个包含n个整数元素的集合a,一个包含m个元素的集合b。
 *   定义一个magic操作位,从一个集合中取出一个元素,放到另一个集合里,且操作过后每个集合的平均值都大于操作前。
 */
public class MagicOp {
    
    //请保证arr1无重复值,arr2无重复值,且arr1和arr2肯定有数字
    public static int maxOps(int[] arr1,int[] arr2){
        double sum1 = 0;
        for(int i = 0;i < arr1.length;i++){
            sum1 += (double)arr1[i];
        }
        double sum2 = 0;
        for(int i = 0;i < arr2.length;i++){
            sum2 += (double)arr2[i];
        }
        //如果两个数组的平均值相等,则没有办法继续进行magic操作
        if(avg(sum1,arr1.length) == avg(sum2,arr2.length)){
            return 0;
        }
        //两个数组的平均值不相等,平均值小的数组不可以拿数出来
        //只有平均值大的数组 才能拿数据 放入小的平均值数组  并且不能拿小数组中已有的值
        //拿的数据范围     小的平均值  < x <  大的平均值  (拿其中较小的)
        int[] arrBig = null;
        int[] arrSmall = null;
        double sumBig = 0;
        double sumSmall = 0;
        // 把平均值大的数组给arrBig,平均值小的给arrSmall
        if(avg(sum1,arr1.length) > avg(sum2,arr2.length)){
            arrBig = arr1;
            arrSmall = arr2;
            sumBig = sum1;
            sumSmall = sum2;
        }else {
            arrBig = arr2;
            arrSmall = arr1;
            sumBig = sum2;
            sumSmall = sum1;
        }
        //把   小的平均值 < x < 大的平均值  中最小的取出,且在小的数组中没出现过的数
        Arrays.sort(arrBig);  //大平均值数组排序,方便取数
        HashSet<Integer> setSmall = new HashSet<>(); //保存小平均值的数据,保证后序加入的每个数不重复
        for(Integer i : arrSmall){
            setSmall.add(i);
        }
        int magicOps = 0;  //记录一共操作多少次
        // 注意操作过后两个数组的平均值都是要变大的
        int bigSize = arrBig.length;
        int smallSize = arrSmall.length;
        //大平均值的数据已经从小到大排序,而平均值是逐渐增大的,因此可以直接从左向右挑即可
        for (int i = 0;i < arrBig.length;i++){ 
            double cur = (double)arrBig[i];
            if(cur < avg(sumBig,bigSize) 
                    && cur > avg(sumSmall,smallSize) 
                    && !setSmall.contains(cur)){
                bigSize--; //大数组拿出当前数据,总和减少
                sumBig -= cur; 
                smallSize++;  //小数组得到当前数据,总和增加
                sumSmall += cur;
                setSmall.add(arrBig[i]);  //将数据记录在小数组中
                magicOps++;  //当前操作加一
            }
        }
        return magicOps;
    }
    
    public static double avg(double sum,int length){
        return sum / (double) length;
    }
}

题目九:升序栈

请编写一个程序,对一个栈里的整型数据,按升序进行排序(即排序前,栈里的数据是无序的),排序后最大元素位于栈顶,要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中。

package class02;

import org.junit.Test;

import java.awt.event.ItemEvent;
import java.util.Stack;

/**
 * 请编写一个程序,对一个栈里的整型数据,按升序进行排序(即排序前,栈里的数据是无序的),
 * 排序后最大元素位于栈顶,要求最多只能使用一个额外的栈存放临时数据,但不得将元素复制到别的数据结构中。
 */
public class Code05_UpStackArray {

    public static Stack<Integer> upStackArray(Stack<Integer> stack){
        if(stack.isEmpty()){
            return stack;
        }
        Stack<Integer> temp = new Stack<>();  //辅助栈  要保证其是从大到小的,顶层最小
        temp.push(stack.pop());
        while (!stack.isEmpty()){
           int cur = stack.pop();
           while (!temp.isEmpty() && cur > temp.peek()){  //一致弹出,直到cur小于栈顶,可以进入
               stack.push(temp.pop());
           }
           temp.push(cur);
        }
        while (!temp.isEmpty()){
            stack.push(temp.pop());
        }
        return stack;
    }
}

题目十:从左向右的递归方法

将给定的数转换为字符串,原则如下:1对应a,2对应b,...26对应z,例如12258可以转换为”abben”,”aveh”,”abyh”,”lbeh”和”lyh”,个数对应为5,编写一个函数,给出可以转换的不同字符串的个数。

package class02;

import org.junit.Test;

/**
 * 将给定的数转换为字符串,原则如下:1对应a,2对应b,...26对应z,
 * 例如12258可以转换为”abben”,”aveh”,”abyh”,”lbeh”和”lyh”,个数对应为5,
 * 编写一个函数,给出可以转换的不同字符串的个数。
 */
public class Code06_MaxTimesNumberToString {

    public static  int getMaxChangeTimes(String str){
        return getMaxChangeTimes(str,0);
    }

    //str 给定的数字字符串  index 表示当前在index位置,index之前的字符已经排序好了,不需要管,只需要管index之后的字符转换即可
    // 返回可以转换为字母字符串的个数
    public static int getMaxChangeTimes(String str,int index){
        if(index == str.length()){  //遍历到最后,表示方式的字符转换是可行的,返回方法1
            return 1;
        }
        if(str.charAt(index) == '0'){  //如果以0开头,都表示该方式不行
            return 0;
        }
        //
        int res = getMaxChangeTimes(str,index+1);  //单独作为一个部分
        if(index == str.length() - 1){   //遍历到结尾了,遍历结束,直接返回
            return res;
        }
        if(Integer.valueOf(str.substring(index,index+2)) < 27 ){
            res += getMaxChangeTimes(str,index + 2);
        }
        return res;
    }

    //改为动态规划
    public static int doWays(String str){
        if(str == "" || str == null){
            return -1;
        }
        int N = str.length();
        int[] dp = new int[N + 1];
        dp[N] = 1;  //N=1该方式的字符遍历到最后了,说明该方式的字符可以,返回1
        dp[N - 1] = str.charAt(N - 1) == '0' ? 0 : 1; //判断最后一个字符是 0还是其他,0没有方法,其他有一种
        for (int i = N - 2;i >= 0;i--){
            if(str.charAt(i) == '0'){
                dp[i] = 0;
            }else {
                dp[i] = dp[i + 1] +
                    Integer.valueOf(str.substring(i,i+2)) < 27 ? dp[i+2] : 0;
            }
        }
        return dp[0];
    }
    
    @Test
    public void test(){
        String str = "12258";
        int maxChangeTimes = getMaxChangeTimes(str);
        System.out.println(maxChangeTimes);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值