一些技巧2

231 阅读3分钟

题目一

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

代码

    public static int minAddToMakeValid(String s) {
        int ans = 0;
        int count = 0;
        for (int i = 0; i < s.length(); i++){
            if (s.charAt(i) == '('){
                count++;
            }else {//为)括号
                if (count == 0){
                    ans++;
                }else {
                    count--;
                }
            }
        }
        retur n ans + count;
    }

题目二

给定一个数组arr,求差值为k的去重数字对。 例如:[1,3,0,5] k = 2 则有(1,3)、(3,5)

代码

    public static List<List<Integer>> allPair (int[] arr, int k) {
        HashSet<Integer> set = new HashSet<>();
        for (int i = 0; i < arr.length; i++){
            set.add(arr[i]);
        }
        List<List<Integer>> res = new ArrayList<>();
        for (Integer cur : set){
            if (set.contains(cur + k)){
                //Arrays.asList的作用是将数组转化为list
                res.add(Arrays.asList(cur, cur + k));
            }
        }
        return res;
    }

    public static void main(String[] args) {
        int[] arr = new int[]{1,5,4,8,7,4,3,2,9};
        System.out.println(allPair(arr, 4));
    }

题目三

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

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

注意以下两点:

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

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

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

代码

    public static int maxOps(int[] arr1, int[] arr2){
        double sum1 = 0;
        //记录arr1数组的全部和
        for (int i= 0; i < arr1.length; i++){
            sum1 += (double) arr1[i];
        }
        double sum2 = 0;
        //记录arr2数组的全部和
        for (int i= 0; i < arr2.length; i++){
            sum2 += (double) arr2[i];
        }
        //如果两个的平均值相同,则不论什么操作,都没办法达到题目的要求
        if (avg(sum1, arr1.length) == avg(sum2, arr2.length)){
            return 0;
        }
        //平均值不相等
        int[] arrMore = null;
        int[] arrLess = null;
        double sumMore = 0;
        double sumLess = 0;
        //重定位平均值大的集合和平均值小的集合,因为移动的时候,只可以从平均值大的集合移动到平均值小的集合
        if (avg(sum1, arr1.length) > avg(sum2, arr2.length)){
            arrMore = arr1;
            sumMore = sum1;
            arrLess = arr2;
            sumLess = sum2;
        }else {
            arrMore = arr2;
            sumMore = sum2;
            arrLess = arr1;
            sumLess = sum1;
        }
        //先将较大的排序是为了方便选择,因为元素从平均值大的集合移到平均值小的集合中,先移符合条件的最小的数,
        //这样可以使大集合的平均值升最大,小集合升最慢,就可以找到原来不满足而现在满足的数了,增加了
        //移动的数量
        Arrays.sort(arrMore);
        HashSet<Integer> setless = new HashSet<>();
        //较小的集合需要用哈希表登记一下,因为我们为了保证移到小集合中的数据在小集合中没有
        for (int num : arrLess){
            setless.add(num);
        }
        int moreSize = arrMore.length;//平均值大的集合还剩几个数
        int lessSize = arrLess.length;//平均值小的集合还剩几个数
        int ops = 0;//操作了多少次
        for (int i = 0; i < arrMore.length; i++){//从小到大依次选择平均值大的集合中的数
            double cur = (double) arrMore[i];
            if (cur < avg(sumMore, moreSize) && cur > avg(sumLess, lessSize)
                    && !setless.contains(arrMore[i])){
                sumMore -= cur;
                moreSize--;
                sumLess += cur;
                lessSize++;
                setless.add(arrMore[i]);
                ops++;
            }
        }
        return ops;
    }
    //算出平均值
    public static double avg(double sum, int size){
        return sum / (double) (size);
    }

题目四

找到最长的有效括号子串
例如:())[()(())()]))(()) 最长即为8

代码

对于这种必须连续的子串或者子数组的问题,你就求每个位置结尾的情况下答案是多少。

dp[i]表示子串如果必须以i位置字符结尾,最长的长度是多大。如果遇到左括号结尾,直接填0;

image.png

    public static int maxLength(String s){
        if (s == null || s.equals("")){
            return 0;
        }
        char[] str = s.toCharArray();
        int[] dp = new int[str.length];
        int pre = 0;
        int res = 0;
        for (int  i = 1; i < str.length; i++){
            //只考虑右括号就可以,因为左括号使用默认值0;
            if (str[i] == ')'){
                //看这个位置是不是左括号
                pre = i - dp[i - 1] - 1;
                //如果这个位置不越界并且第左括号
                if (pre >= 0 && str[pre] == '('){
                    //如果是左括号至少+2,如果这个位置的前一个位置存在,就接上这个位置的值
                    dp[i] = dp[i - 1] + 2 + (pre > 0 ? dp[pre - 1] : 0);
                }
            }
        }
        return res;
    }

题目五

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

代码

建立一个辅助栈,这个栈要实现从小到大放数。所以在输出到原stack栈中。

  1. 辅助栈进入了一个数
  2. 新进的数如果比它小,直接进入
  3. 新进的数如果比它大,辅助栈就依次弹出到可以放进去这个数,弹出的这个数放到原stack中
    public static void sortStackByStack(Stack<Integer> stack){
        Stack<Integer> help = new Stack<>();
        while (!stack.isEmpty()){
            int cur = stack.pop();
            while(!help.isEmpty() && help.peek() < cur){
                stack.push(help.pop());
            }
            help.push(cur);
        }
        while(!help.isEmpty()){
            stack.push(help.pop());
        }
    }

    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<Integer>();
        stack.push(3);
        stack.push(4);
        stack.push(1);
        stack.push(6);
        stack.push(7);
        stack.push(5);
        sortStackByStack(stack);
        while(!stack.isEmpty()){
            System.out.println(stack.pop());// 7 6 5 4 3 1
        }
    }

题目六

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

代码

动态规划,dp[i]表示包括它自己从i ~ length - 1的数转化成字符串可以有多少种。

    public static int dpWays(int num){
        if (num < 1){
            return 0;
        }
        char[] str = String.valueOf(num).toCharArray();
        int N = str.length;
        int[] dp = new int[N + 1];
        dp[N] = 1;//什么也没有算一种
        //如果包含开头的是0,则一定为0.没法转换
        dp[N - 1] = str[N - 1] == '0' ? 0 : 1;
        for (int i = N - 2; i >= 0; i--){
            if (str[i] == '0'){
                dp[i] = 0;
            }else {
                //开头不为0,分成两种情况,当前单独算一个+当前和之后两个算在一起
                dp[i] = dp[i + 1] + (((str[i] - '0') * 10 + str[i + 1] - '0') < 27 ? dp[i + 2] : 0);
            }
        }
        return dp[0];
    }

题目七

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

代码

    public static class Node{
        public int value;
        public Node left;
        public Node right;

        public Node(int val){
            value = val;
        }
    }

    public static int maxSumRecursive(Node head){
        return process(head, 0);
    }

    public static int process(Node x, int pre) {
        if (x == null){
            return Integer.MIN_VALUE;
        }
        if (x.left == null && x.right == null){
            return pre + x.value;
        }
        int leftMax = process(x.left, pre + x.value);
        int rightMax = process(x.right, pre + x.value);
        return Math.max(leftMax, rightMax);
        
    public static int maxSumUnrecursive(Node head) {
        int max = 0;
        HashMap<Node, Integer> sumMap = new HashMap<>();
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            stack.add(head);
            sumMap.put(head, head.value);
            while (!stack.isEmpty()) {
                head = stack.pop();
                if (head.left == null && head.right == null) {
                    max = Math.max(max, sumMap.get(head));
                }
                if (head.right != null) {
                    sumMap.put(head.right, sumMap.get(head) + head.right.value);
                    stack.push(head.right);
                }
                if (head.left != null) {
                    sumMap.put(head.left, sumMap.get(head) + head.left.value);
                    stack.push(head.left);
                }
            }
        }
        return max;
    }