10.经典面试题

659--分割数组为连续子序列

 Map<Integer, Integer> countNum;
    Map<Integer, Integer> tail;
    public boolean isPossible(int[] nums) {
        //用一个哈希表统计每个元素出现的次数
        countNum = new HashMap<Integer, Integer>();
        for (int num : nums) {
            countNum.put(num, countNum.getOrDefault(num, 0) + 1);
        }
        //定义一个哈希表记录所有连续的子序列,但是只记录子序列中最大的元素
        tail = new HashMap<Integer, Integer>();
        for (int num : nums) {
            int count = countNum.getOrDefault(num, 0);
            if (count <= 0) {//当前元素已经用完,直接跳过
                continue;
                //判断是不是可以构成尾部
            } else if (tail.getOrDefault(num - 1, 0) > 0) {//前面还有数字,可以构成以num结尾的子序列
                countNum.put(num, count - 1);
                tail.put(num - 1, tail.get(num - 1) - 1);//覆盖当前最长的子序列,以他结尾的子序列可能为多个
                tail.put(num, tail.getOrDefault(num, 0) + 1);//当前以num结尾的子序列+1
                //判断是不是可以构成头部
            } else if (countNum.getOrDefault(num + 1, 0) > 0 && countNum.getOrDefault(num + 2, 0) > 0) {
                //组成新的队列需要后面的两个元素
                countNum.put(num, count - 1);
                countNum.put(num + 1, countNum.get(num + 1) - 1);
                countNum.put(num + 2, countNum.get(num + 2) - 1);
                tail.put(num + 2, tail.getOrDefault(num + 2, 0) + 1);//当前以num+2结尾的子序列+1
            } else
                return false;//前后不能构成子序列直接返回false
        }
        return true;
    }

 969--煎饼排序

List<Integer> list = new LinkedList<>();

    public List<Integer> pancakeSort(int[] arr) {
        sort(arr, arr.length);
        return list;
    }

    private void sort(int[] arr, int length) {
        //base
        if (length == 1) return;
        int max = 0;
        int maxIndex = 0;
        for (int i = 0; i < length; i++) {
            if (arr[i] > max) {
                maxIndex = i;
                max = arr[i];
            }
        }
        //第一次翻转将最大的搞到最上面去
        reverse(arr, 0, maxIndex);
        list.add(maxIndex+1);
        //第二次将最大的翻转到最下面
        reverse(arr,0,length-1);
        list.add(length);
        //递归的调用直到只剩下一个
        sort(arr,length-1);
    }

    void reverse(int[] arr, int i, int j) {
        while (i < j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
            i++;
            j--;
        }
    }

43--字符串相乘

 

public String multiply(String num1, String num2) {
        if (num1.equals("0") || num2.equals("0")) {
            return "0";
        }
        int[] res = new int[num1.length() + num2.length()];
        for (int i = num1.length() - 1; i >= 0; i--) {
            int n1 = num1.charAt(i) - '0';
            for (int j = num2.length() - 1; j >= 0; j--) {
                int n2 = num2.charAt(j) - '0';
                int sum = (res[i + j + 1] + n1 * n2);
                res[i + j + 1] = sum % 10;//余数(第二位数)
                res[i + j] += sum / 10;//进位(第一位数)
            }
        }
        //  删除零位
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < res.length; i++) {
            if (i == 0 && res[i] == 0) continue;
            result.append(res[i]);
        }
        return result.toString();
    }

 224-- 基本计算器--括号和加减

 public int calculate(String s) {
        //栈顶元素表示了当前位置所有括号所形成的符号,这个栈只储存符号{+1或者-1}
        Deque<Integer> ops = new LinkedList<Integer>();
        ops.push(1);
        //+1为加号 -1为减号
        int sign = 1;

        int ret = 0;
        int n = s.length();
        int i = 0;
        while (i < n) {
            if (s.charAt(i) == ' ') {
                i++;
                //遇到+和-只更改栈顶的符号
            } else if (s.charAt(i) == '+') {
                sign = ops.peek();
                i++;
            } else if (s.charAt(i) == '-') {
                sign = -ops.peek();
                i++;
                //遇到前括号,符号入栈,将影响遇到)前的所有数据
            } else if (s.charAt(i) == '(') {
                ops.push(sign);
                i++;
                //遇到后括号,符号出栈,因为此符号已经影响不了后面的数据了
            } else if (s.charAt(i) == ')') {
                ops.pop();
                i++;
            } else {
                //累加数字123=1*10*10+2*10+3
                long num = 0;
                while (i < n && Character.isDigit(s.charAt(i))) {
                    num = num * 10 + s.charAt(i) - '0';
                    i++;
                }
                ret += sign * num;
            }
        }
        return ret;
    }

227--基本计算器--加减乘除

public int calculate(String s) {
        Deque<Integer> stack=new LinkedList<>();
        char preSign='+';
        int num=0;
        int n=s.length();
        for (int i = 0; i < n; i++) {
            if(Character.isDigit(s.charAt(i))){
                num+=num*10+s.charAt(i)-'0';
            }
            //由于乘除优先与加减就干脆全部都计算完了再加起来
            if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n - 1) {
                switch (preSign){
                    case '+':
                        stack.push(num);
                        break;
                    case '-':
                        stack.push(-num);
                        break;
                    case '*':
                        stack.push(stack.pop()*num);
                        break;
                    case '/':
                        stack.push(stack.pop()/num);
                        break;
                }
                //前一个符号
                preSign=s.charAt(i);
                //计算完成后置零
                num=0;
            }
        }
        int ans=0;
        while (!stack.isEmpty()){
            ans+=stack.pop();
        }
        return ans;
    }

772--加减乘除括号

    public int calculate(String s) {
        List<Character> list = new ArrayList<>();
        for (int i = 0; i < s.length(); i++) {
            list.add(s.charAt(i));
        }
        return helper(list);
    }

    private int helper(List<Character> s) {
        Stack<Integer> stack = new Stack<>();
        // 记录 num 前的符号,初始化为 +
        char sign = '+';
        int num = 0, res = 0;
        while (s.size() > 0) {
            char c = s.remove(0);
            //数字累加
            if (Character.isDigit(c)) {
                num = 10 * num + (c - '0');
            }
            //遇到前括号递归,括号包含的算式我们认为是一个数字就行了
            if (c == '(') num = helper(s);
            if ((!Character.isDigit(c) && c != ' ') || s.size() == 0) {
                switch (sign) {
                    case '+':
                        stack.push(num);
                        break;
                    case '-':
                        stack.push(-num);
                        break;
                    case '*':
                        stack.push(stack.pop()*num);
                        break;
                    case '/':
                        stack.push(stack.peek()/num);
                        break;
                }
                //更新符号为当前符号,数字清零
                sign=c;
                num=0;
            }
            if(c==')')break;//遇到右括号递归结束
        }
        while (!stack.isEmpty()){
            res+=stack.pop();
        }
        return res;
    }

42--接雨水

 对于第i个位置,它可以装的水为min(l_max,r_max)-height[i],暴力解法

  public int trap(int[] height) {
        int n=height.length;
        int[] dp = new int[height.length];
        dp[0]=0;
        dp[n-1]=0;
        for (int i = 1; i <n-1 ; i++) {
            dp[i]=getMax(height,i)-height[i];
            if(dp[i]<0)dp[i]=0;
        }
        int maxTrap=0;
        for (int i = 0; i < n - 1; i++) {
            maxTrap+=dp[i];
        }
        return maxTrap;
    }
    public int getMax(int[] height,int i){
        int l_max=0;
        int r_max=0;
        for (int j = i+1; j <height.length ; j++) {
            r_max=Math.max(r_max,height[j]);
        }
        for (int j = i-1; j >=0 ; j--) {
            l_max=Math.max(l_max,height[j]);
        }
        return Math.min(l_max,r_max);
    }

备忘录解法:

 public int trap(int[] height) {
        if (height == null || height.length == 0) return 0;
        int n = height.length;
        int res = 0;
        //充当备忘录的数组
        int[] left = new int[n];
        int[] right = new int[n];
        //base
        left[0] = height[0];
        right[n - 1] = height[n - 1];
        //从左向右计算l_max
        for (int i = 1; i < n; i++) {
            left[i] = Math.max(left[i - 1], height[i]);
        }
        //从右到左计算r_max
        for (int i = n - 2; i >= 0; i--) {
            right[i] = Math.max(right[i + 1], height[i]);
        }
        //计算答案
        for (int i = 0; i < n - 1; i++) {
            res += Math.min(left[i], right[i]) - height[i];
        }
        return res;

    }

双指针解法:

    public int trap(int[] height) {
        if (height == null || height.length == 0) return 0;
        int n = height.length;
        int res = 0;
        //左指针
        int leftIndex=0;
        //右指针
        int rightIndex=n-1;
        int rightMax=height[n-1];
        int leftMax=height[0];
        while (leftIndex<=rightIndex){
            leftMax=Math.max(height[leftIndex],leftMax);
            rightMax=Math.max(height[rightIndex],rightMax);
            if(leftMax<rightMax){
                res+=leftMax-height[leftIndex];
                leftIndex++;//左边已经计算完了
            }else{
                res+=rightMax-height[rightIndex];
                rightIndex--;//右边已经计算完了
            }
        }
        return res;
    }

20--有效的括号

利用栈来解决括号的问题

public boolean isValid(String s) {
        Stack<Character> stackLeft = new Stack<>();
        int n = s.length();
        for (int i = 0; i < n; i++) {
            if (s.charAt(i) == '(' || s.charAt(i) == '[' || s.charAt(i) == '{') {
                stackLeft.push(s.charAt(i));
            } else {
                if (!stackLeft.isEmpty() && left(s.charAt(i)) == stackLeft.peek()) {
                    stackLeft.pop();
                } else return false;
            }
        }
        return stackLeft.isEmpty();
    }

    public char left(char c) {
        if (c == ')') return '(';
        if (c == ']') return '[';
        return '{';
    }

 921--使括号有效的最少添加

 在不断的往里添加左括号,在扫描到右括号的时候弹出左括号,如果没有左括号了说明要补一个操作数,同样如果说整个扫描完毕以后,栈里依然还有左括号那么也需要补一个操作数

 public int minAddToMakeValid(String s) {
        Stack<Character> stack = new Stack<Character>();
        int n = s.length();
        int res = 0;
        for (int i = 0; i < n; i++) {
            char c=s.charAt(i);
            if(c=='('){
                stack.push(c);
            }else {
                if(stack.isEmpty()){
                    res++;
                }else {
                    stack.pop();
                }
            }
        }
        return res+stack.size();
    }

1541--平衡括号的最少添加

public int minInsertions(String s) {
        //need表示为对右括号的需求
        int res=0,need=0;
        for (int i = 0; i < s.length(); i++) {
            if(s.charAt(i)=='('){
                need+=2;
                if(need%2==1){
                    //插入一个右括号
                    res++;
                    //对右括号的需求减1
                    need--;
                }
            }else{
                need--;
                if(need==-1){
                    //添加一个左括号
                    res++;
                    //对右括号的需求变为1
                    need=1;
                }
            }
        }
        return res+need;

    }

855--考场就坐

    // 将端点 p 映射到以 p 为左端点的线段
    private Map<Integer, int[]> startMap;
    // 将端点 p 映射到以 p 为右端点的线段
    private Map<Integer, int[]> endMap;
    // 根据线段长度从小到大存放所有线段
    private TreeSet<int[]> pq;//平衡二叉树
    private int N;

    public likou855(int N) {
        this.N = N;
        startMap = new HashMap<>();
        endMap = new HashMap<>();
        pq = new TreeSet<>((a, b) -> {
            int distA = distance(a);
            int distB = distance(b);
            // 如果长度相同,就比较索引
            if (distA == distB)
                return b[0] - a[0];
            return distA - distB;
        });
        // 在有序集合中先放一个虚拟线段
        addInterval(new int[] {-1, N});
    }

    /* 去除一个线段 */
    private void removeInterval(int[] intv) {
        pq.remove(intv);
        startMap.remove(intv[0]);
        endMap.remove(intv[1]);
    }

    /* 增加一个线段 */
    private void addInterval(int[] intv) {
        pq.add(intv);
        startMap.put(intv[0], intv);
        endMap.put(intv[1], intv);
    }

    /* 计算一个线段的长度 */
    private int distance(int[] intv) {
        int x = intv[0];
        int y = intv[1];
        if (x == -1) return y;
        if (y == N) return N - 1 - x;
        // 中点和端点之间的长度
        return (y - x) / 2;
    }
    public int seat() {
        // 从有序集合拿出最长的线段
        int[] longest = pq.last();
        int x = longest[0];
        int y = longest[1];
        int seat;
        if (x == -1) { // 情况一
            seat = 0;
        } else if (y == N) { // 情况二
            seat = N - 1;
        } else { // 情况三
            seat = (y - x) / 2 + x;
        }
        // 将最长的线段分成两段
        int[] left = new int[] {x, seat};
        int[] right = new int[] {seat, y};
        removeInterval(longest);
        addInterval(left);
        addInterval(right);
        return seat;
    }

    public void leave(int p) {
        // 将 p 左右的线段找出来
        int[] right = startMap.get(p);
        int[] left = endMap.get(p);
        // 合并两个线段成为一个线段
        int[] merged = new int[] {left[0], right[1]};
        removeInterval(left);
        removeInterval(right);
        addInterval(merged);
    }

 392--判断子序列

 双指针秒杀

 public boolean isSubsequence(String s, String t) {
        int i = 0, j = 0;
        while (j < t.length() && i < s.length()) {
            if (t.charAt(j) == s.charAt(i)) {
                i++;
            }
            j++;
        }
        return i == s.length();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值