演算法中級學習1

橡皮筋兒發表於2021-11-30

一、觀察表法

小虎去附近的商店買蘋果,奸詐的商販使用了捆綁交易,只提供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;
    }
}

相關文章