算法中级学习1

一、观察表法

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

/**
 * @Author: 郜宇博
 * @Date: 2021/11/18 14:19
 */
public class Bag {
    public static void main(String[] args) {
        for (int i = 1 ; i < 100; i ++){
            if (minBags(i) != minBag1(i)){
                System.out.println(i+" no");
            }
        }
    }

    /**
     * 袋子分为8,6两种,想让袋子数量最少,优先选择8
     * 所以先m/8得出用几个8袋子,然后计算剩余的苹果数
     * 查看剩余的苹果树是否可以被6整除,如果可以总袋数就等于bag8 + bag6
     * 不可以就将8袋子所用数量-1,再计算剩余苹果树然后判断bag6,
     * 当bag8减到0或者剩余苹果>24时,都不用继续了(因为>24肯定优先考虑bag8)
     */
    public static int minBags(int m){
        if ((m & 1)!= 0){
            return -1;
        }
        if (m == 6 || m == 8){
            return 1;
        }
        int bag6 = -1;
        int bag8 = m / 8;
        int rest = m - 8 * bag8;
        while (rest < 24 && bag8 >= 0){
            bag6 = rest % 6 == 0 ? rest/6: -1;
            if (bag6 != -1){
                return bag8 + bag6;
            }
            bag8--;
            rest = m - (bag8 * 8);
        }
        return -1;
    }
    /**
     * 观察表法
     */
    public static int minBag1(int m){
        if ( (m & 1) != 0){
            return -1;
        }
        if (m < 18){
            switch (m){
                case 6:
                case 8:
                    return 1;
                case 12:
                case 14:
                case 16:
                    return 2;
                default:
                    return -1;
            }
        }
        return ((m - 18) / 8) + 3;
    }
}

二、滑动窗口

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

/**
 * 滑动窗口,让left一直向右走
 * 先让绳子起始点处于ar[0],记为L,然后伸长绳子看能否到下一点arr[1],一直到不可以伸长为止
 * 当不可以伸长的时候,更新max,L++,一直到arr末尾
 */
public static int maxCount(int []arr,int L){
    int left = 0;
    int count = 0;
    int max = Integer.MIN_VALUE;
    //int count = 0;
    for (left = 0; left < arr.length; left++){
        count = 0;
        while (left+count < arr.length && arr[left+count] - arr[left] <= L){
            count++;
        }
        //更新max
        max = Math.max(count,max);
    }
    return max;
}

三、预处理法

牛牛有一些排成一行的正方形。每个正方形已经被染成红色或者绿色。牛牛现在可 以选择任意一个正方形然后用这两种颜色的任意一种进行染色,这个正方形的颜色将 会被覆盖。

牛牛的目标是在完成染色之后,每个红色R都比每个绿色G距离最左侧近。

牛牛想知道他最少需要涂染几个正方形。

如样例所示: s = RGRGR 我们涂染之后变成RRRGG满足要求了,涂染的个数为2,没有比这个更好的涂染方案

/**
 * @Author: 郜宇博
 * @Date: 2021/11/18 16:38
 */
public class Color {
    public static void main(String[] args) {
        System.out.println(minColorCount("RGRGR"));

    }

    /**
     * 预处理法
     * 将左侧都变为R,右侧都变为G
     * 因此要逐个位置从左到右尝试,如将0的左边变为R,和[0,n-1]变为G
     *                           1的左边变为R, [1,n-1]变为G
     * 而要想求最小染色数,就需要知道左边范围有多少个G,右边范围有多少个R
     * 也就是[0,i]多少个G,[i,n-1]有多少个R
     * 有这两个结果,就可以遍历,不断更新min = [0,i]+[i+1,n-1];
     */
    public static int minColorCount(String str){
        int i = 0;
        char[] chars = str.toCharArray();
        int [] leftG = new int[chars.length];
        int [] rightR = new int[chars.length];
        int min = Integer.MAX_VALUE;
        leftG[0] = chars[0] == 'G'?1:0;
        rightR[chars.length-1] = chars[chars.length-1] == 'R'?1:0;
        //预处理
        for ( i= 1; i < leftG.length; i++){
            leftG[i] = leftG[i-1] + ((chars[i] == 'G')?1:0);
        }
        for (i = rightR.length-2; i >= 0; i--){
            rightR[i] = rightR[i+1] + ((chars[i] == 'R')?1:0);
        }
        for (i = 0; i < chars.length; i++){
            if (i == 0){
                min = Math.min(min,leftG[chars.length-1]);
            }
            else if (i == chars.length-1){
                min = Math.min(min,rightR[0]);
            }
            else {
                min = Math.min(min,leftG[i]+rightR[i+1]);
            }
        }
        return min;
    }
}

四、等概率返回

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

/**
 * @Author: 郜宇博
 * @Date: 2021/11/18 18:24
 */
public class RandomNum {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++){
            System.out.println(getRandom7());
        }
    }
    public static int getRandom5(){
        return (int) (Math.random() * 5) + 1;
    }
    /**
        1-7等概率相当于0-6等概率+1
        二进制表示的话,需要3位二进制可以表示到0-7,
        所以需要一个 方法可以等概率返回0和1,
        使用三次该方法,然后拼接到一起形成十进制的数,如果最后为7,那么重新使用三次。
        这样可以得到0-6等概率,再+1就是1-7
     */
    public static int getRandom7(){
        int random7 = 0;
        do {
            int random1 = getRandom01();
            int random2 = getRandom01();
            int random3 = getRandom01();
            random7 = random1 + (random2 << 1)+(random3<<2);
        }while (random7 == 7);
        return random7 + 1;
    }

    /**
     * 等概率返回0 和 1
     * 使用等概率返回1-5 :12345
     * 大于3返回1 ,小于3返回0等于三重新使用
     */
    public static int getRandom01(){
        int random01 = 0;
        do {
            random01 = getRandom5();
        }while (random01 == 3);

        return random01 > 3?1:0;
    }
    
}

五、括号匹配

题1

需要多少额外的括号,才能将字符串的括号保持正确匹配

/**
 * @Author: 郜宇博
 * @Date: 2021/11/19 16:13
 */
public class NeedParentheses {
    public static void main(String[] args) {

        for (int i =0 ; i < 20; i++){
            String s = randomParentheses(10);
            if (needParentheses(s) != needParentheses1(s)){
                System.out.println("no");
            }
        }
    }
    public static String randomParentheses(int m) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < m; i++) {
            int i1 = new Random().nextInt(10);
            //奇
            if ((i1 & 1) != 0){
                stringBuilder.append("(");
            }
            else {
                stringBuilder.append(")");
            }
        }
        return stringBuilder.toString();

    }
    public static int needParentheses1(String str) {
        int leftRest = 0;
        int needSolveRight = 0;
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == '(') {
                leftRest++;
            } else {
                if (leftRest == 0) {
                    needSolveRight++;
                } else {
                    leftRest--;
                }
            }
        }
        return leftRest + needSolveRight;
    }
    /**
     * 设置一个count变量
     * 从左向右遍历。遇到左括号++,右括号--
     * 在过程中如果count <0 ,那么说明单独出现了右括号,此时需要一个左括号
     * 遍历结束后,如果count >0,说明左括号多了,需要count这么多的右括号
     */
    public static int needParentheses(String str){
        char[] chars = str.toCharArray();
        //当前状态,<0代表缺'('
        int count = 0;
        //需要多少额外的括号字符
        int res = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == '('){
                count++;
            }
            //遇到右括号
            else {
                //count--;
                //if (count < 0){
                //    //加一个左括号
                //    res++;
                //    count = 0;
                //}
                if (count == 0){
                    res++;
                }else {
                    count--;
                }
            }
        }
        return res+ count;
    }
}

题2

括号字符串的最大深度

public static int deep(String s) {
    char[] str = s.toCharArray();
    int count = 0;
    int max = 0;
    for (int i = 0; i < str.length; i++) {
        if (str[i] == '(') {
            max = Math.max(max, ++count);
        } else {
            count=0;
        }
    }
    return max;
}

六、magic集合移动

给一个包含n个整数元素的集合a,一个包含m个整数元素的集合b。 定义magic操作为,从一个集合中取出一个元素,放到另一个集合里,且操作过 后每个集合的平均值都大于操作前。 注意以下两点:

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

2)值为x的元素从集合b取出放入集合a,但集合a中已经有值为x的元素,则a的 平均值不变(因为集合元素不会重复),b的平均值可能会改变(因为x被取出 了)
问最多可以进行多少次magic操作?

/**
 * @Author: 郜宇博
 * @Date: 2021/11/21 20:57
 */
public class MagicOp {
    public static void main(String[] args) {
        int[] arr1 = { 1, 2, 5 };
        int[] arr2 = { 2, 3, 4, 5, 6 };
        System.out.println(magic(arr1, arr2));
    }

    /**
     * 移动元素后,保证两个集合的平均值增大
     * 1.保证移动的元素,不在目标集合中
     */
    public static int magic(int []arr1,int[]arr2){
        double sum1=0,sum2 = 0;
        int [] smallList = null;
        int [] largeList = null;
        double largeSum = 0;
        double smallSum = 0;
        for (int value : arr1) {
            sum1 += value;
        }
        for (int value : arr2) {
            sum2 += value;
        }
        //平均值
        double avg1 = getAvg(sum1,arr1.length);
        double avg2 = getAvg(sum2,arr2.length);
        if ( avg1 == avg2){
            return 0;
        }
        if (avg1 > avg2){
            //为集合赋值
            largeList = arr1;
            smallList = arr2;
            largeSum = sum1;
            smallSum = sum2;
        }
        else {
            //为集合赋值
            largeList = arr2;
            smallList = arr1;
            largeSum = sum2;
            smallSum = sum1;
        }
        int largeSize = largeList.length;
        int smallSize = smallList.length;
        int result = 0;
        HashSet<Integer> smallSet = new HashSet();
        for (int num: smallList){
            smallSet.add(num);
        }
        for (int num: largeList){
            double cur = num;
            //移动元素在平均值中间,并且目标集合中不存在该元素
            if (cur > getAvg(smallSum,smallSize) && cur < getAvg(largeSum,largeSize) && !smallSet.contains(cur)){
                smallSet.add(num);
                largeSize--;
                largeSum-=num;
                smallSum+=num;
                smallSize++;
                result++;
            }
        }
        return result;
    }
    private static double getAvg(double sum, int size) {
        if (size > 0 ){
          return sum/ size;
        }
        return -1;
    }
}

七、数字转化

将给定的数转换为字符串,原则如下:1对应 a,2对应b,…..26对应z,

例如12258 可以转换为"abbeh", "aveh", "abyh", "lbeh" and "lyh",个数为5,

编写一个函数,给出可以转换的不同字符串的个数。

/**
 * @Author: 郜宇博
 * @Date: 2021/11/21 21:36
 */
public class NumberConvert {
    public static void main(String[] args) {
        int test = 111143311;
        char[] arr = String.valueOf(test).toCharArray();
        System.out.println(convertDP(arr));

    }
    public static int convert(char []arr,int index){
        if (index == arr.length){
            return 1;
        }
        if (arr[index] == '0'){
            return 0;
        }
        //当前索引位作为一个字母
        int res = convert(arr,index+1);
        //两个索引位组合为字母 规则0-26
        if ( (index +  1) < arr.length
                &&
                ((arr[index]-'0')*10 + (arr[index+1]-'0') < 27 )
        ){
            res += convert(arr,index+2);
        }
        return res;
    }
    public static int convertDP(char[] arr){
        int[] dp = new int[arr.length+1];
        dp[arr.length] = 1;
        dp[arr.length-1] = arr[arr.length-1] == '0'?0:1;
        for (int i = arr.length-2;i >=0; i--){
            if (arr[i] == '0'){
                dp[i] = 0;
            }
            else {
                dp[i] = dp[i+1];
                if ((arr[i] -'0') *10 + (arr[i+1]-'0')<27 ){
                    dp[i] += dp[i+2];
                }
            }
        }
        return dp[0];
    }
}

八、栈内排序

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

public static void sortWithStack(Stack<Integer> stack){
    Stack<Integer> help = new Stack<Integer>();
    help.add(stack.pop());
    //将栈内元素加入辅助栈中
    while (! stack.isEmpty()){
        int pop = stack.pop();
        while (!help.isEmpty() && pop > help.peek()){
            stack.add(help.pop());
        }
        //保证辅助栈的底部是大元素
        help.add(pop);
    }
    //放回stack
    while (!help.isEmpty()){
        stack.add(help.pop());
    }
    while (!stack.isEmpty()){
        System.out.print(stack.pop()+" ");
    }
}

九、二叉树权值

二叉树每个结点都有一个int型权值,给定一棵二叉树,要求计算出从根结点到 叶结点的所有路径中,权值和最大的值为多少。

package day12;
/**
 * @Author: 郜宇博
 * @Date: 2021/11/21 22:17
 */
public class TreeSum {
    public static void main(String[] args) {
        Node head = new Node(4);
        head.left = new Node(1);
        head.left.right = new Node(5);
        head.right = new Node(-7);
        head.right.left = new Node(3);
        System.out.println(getMax(head));
    }
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int val) {
            value = val;
        }
    }
    public static int getMax(Node node){
        if (node == null){
            return 0;
        }
        //叶节点
        if (node.left == node && node.right == null){
            return node.value;
        }
        //左、右节点权值
        int rightSum = getMax(node.right);
        int leftSum = getMax(node.left);
        return Math.max(rightSum,leftSum) + node.value;
    }
}
posted @ 2021-11-30 23:41  橡皮筋儿  阅读(108)  评论(0编辑  收藏  举报