算法面试笔记

剑指 Offer 07. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

解题思路
前序 [1] [2,4,7] [3,5,6,8]
中序 [4,7,2] [1] [5,3,8,6]

  1. 前序遍历可以确认树的根节点
  2. 中序遍历确认树的左右子树
  3. 递归还原所有子树
  4. 递归的终止条件是子树没有子节点了,即left > right
class Solution {
    HashMap<Integer, Integer> map = new HashMap();
    int[] preArr;

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for(int i = 0; i < inorder.length; i++){
            map.put(inorder[i], i);
        }
        preArr = preorder;
       return recursive(0, 0, inorder.length - 1);
    }

    public TreeNode recursive(int preRoot, int inLeft, int inRight){
        if(inLeft > inRight){
            return null;
        }
        int inRootIndex = map.get(preArr[preRoot]);
        TreeNode node = new TreeNode(preArr[preRoot]);
        node.left = recursive(preRoot + 1, inLeft, inRootIndex - 1);
        node.right = recursive(preRoot + (inRootIndex - inLeft) + 1,
                        inRootIndex + 1,  inRight);  
        return node;                     
    }
}

https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/
https://www.bilibili.com/video/av78678634/

三角形最小路径和

在这里插入图片描述
在这里插入图片描述
解法一:递归


class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        return  dfs(triangle, 0, 0);
    }

    private int dfs(List<List<Integer>> triangle, int i, int j) {
        if (i == triangle.size()) {
            return 0;
        }
        return Math.min(dfs(triangle, i + 1, j), dfs(triangle, i + 1, j + 1)) + triangle.get(i).get(j);
    }
}

暴力搜索会有大量的重复计算,导致 超时,因此在 解法二 中结合记忆化数组进行优化。

解法二:递归 + 记忆化
在解法一的基础上,定义了二维数组进行记忆化。

class Solution {
    Integer[][] memo;
    public int minimumTotal(List<List<Integer>> triangle) {
        memo = new Integer[triangle.size()][triangle.size()];
        return  dfs(triangle, 0, 0);
    }

    private int dfs(List<List<Integer>> triangle, int i, int j) {
        if (i == triangle.size()) {
            return 0;
        }
        if (memo[i][j] != null) {
            return memo[i][j];
        }
        return memo[i][j] = Math.min(dfs(triangle, i + 1, j), dfs(triangle, i + 1, j + 1)) + triangle.get(i).get(j);
    }
}

解法三:动态规划
定义二维 dp 数组,将解法二中「自顶向下的递归」改为「自底向上的递推」。

1、状态定义:
dp[i][j]dp[i][j] 表示从点 (i, j)(i,j) 到底边的最小路径和。

2、状态转移:
dp[i][j] = min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j]dp[i][j]=min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j]

3、代码实现:

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        // dp[i][j] 表示从点 (i, j) 到底边的最小路径和。
        int[][] dp = new int[n + 1][n + 1];
        // 从三角形的最后一行开始递推。
        for (int i = n - 1; i >= 0; i--) {
            for (int j = 0; j <= i; j++) {
                dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j);
            }
        }
        return dp[0][0];
    }
}

https://leetcode-cn.com/problems/triangle/

对非常大的两个数字求和——数字字符串求和

对于大到会溢出任何数据类型的整数进行求和:
思路:将数字一字符串的形式保存;逐位进行求和;
细节:注意进位;

public class BigIntegerTest {

    public static int[] add(String a, String b) {
        String[] strs = prepare(a, b);
        a = strs[0];
        b = strs[1];
        System.out.println("\t" + a);
        System.out.println("+\t" + b);
        System.out.println("------------------------");

        int n = a.length();
        int[] res = new int[n + 1];//可能产生进位
        int num1 = 0, num2 = 0, num = 0;

        //从最低位开始逐位相加
        for (int i = n - 1; i >= 0; i--) {
            num1 = Integer.parseInt(a.substring(i, i + 1));
            num2 = Integer.parseInt(b.substring(i, i + 1));
            //该位数值的和,可能会产生进位; 该和有两部分组成
            num = num1 + num2;//部分一,本位数字求和
            num += res[i + 1];//部分二,本位数字求和在加上进位值。

            res[i + 1] = (num % 10);//本位
            res[i] = num / 10;//上一位

        }

        return res;
    }

    //对数字字符串做格式处理,不足位用0补齐
    public static String[] prepare(String a, String b) {
        String[] strs = new String[2];
        for (int i = 0; i < 2; i++) {
            strs[i] = "";
        }
        if (a.length() > b.length())
            b = doFormat(b, a.length(), 0);
        else if (a.length() < b.length())
            a = doFormat(a, b.length(), 0);
        strs[0] = a;
        strs[1] = b;
        return strs;
    }

    public static String doFormat(String str, int n, int partten) {
        String newStr = "";
        for (int i = 0; i < n - str.length(); i++) {
            newStr += "0";
        }
        return newStr + str;
    }

    public static void main(String[] args) {
        String strA = "123456789";
        String strB = "12345678910111213";
        int[] result = add(strA, strB);
        System.out.print("\t");
        for (int i = 0; i < result.length; i++) {
            if (result[i] == 0 && i == 0)
                continue;
            System.out.print(result[i]);
        }
    }
}

实现Pow(x, n)

在这里插入图片描述
由于每次递归都会使得指数减少一半,因此递归的层数为 O(\log n)O(logn),算法可以在很快的时间内得到结果。

class Solution {
    public double myPow(double x, int n) {
        long N = n;
        return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
    }

    public double quickMul(double x, long N) {
        if (N == 0) {
            return 1.0;
        }
        double y = quickMul(x, N / 2);
        return N % 2 == 0 ? y * y : y * y * x;
    }
}

时间复杂度:O(\log n)O(logn),即为递归的层数。

空间复杂度:O(\log n)O(logn),即为递归的层数。这是由于递归的函数调用会使用栈空间。

class Solution {
    public double myPow(double x, int n) {
        long N = n;
        return N >= 0 ? quickMul(x, N) : 1.0 / quickMul(x, -N);
    }

    public double quickMul(double x, long N) {
        double ans = 1.0;
        // 贡献的初始值为 x
        double x_contribute = x;
        // 在对 N 进行二进制拆分的同时计算答案
        while (N > 0) {
            if (N % 2 == 1) {
                // 如果 N 二进制表示的最低位为 1,那么需要计入贡献
                ans *= x_contribute;
            }
            // 将贡献不断地平方
            x_contribute *= x_contribute;
            // 舍弃 N 二进制表示的最低位,这样我们每次只要判断最低位即可
            N /= 2;
        }
        return ans;
    }
}

时间复杂度:O(\log n)O(logn),即为对 nn 进行二进制拆分的时间复杂度。

空间复杂度:O(1)O(1)。

https://leetcode-cn.com/problems/powx-n/

x 的平方根

在这里插入图片描述
在这里插入图片描述

class Solution {
    public int mySqrt(int x) {
        int l = 0, r = x, ans = -1;
        while (l <= r) {
            int mid = l + (r - l) / 2;
            if ((long) mid * mid <= x) {
                ans = mid;
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        return ans;
    }
}

在这里插入图片描述
https://leetcode-cn.com/problems/sqrtx/

二叉搜索树的后序遍历序列

>>和>>>的区别

  • >> :有符号右移运算,若参与运算的数字为正,则在高位补0,若为负则在高位补1

  • >>>:无符号右移运算,无论参与运算的数字为正数还是负数,都在高位补0

  • <<:左移运算

实现String的endWith()方法

你从之前的工作经历中最大的收获是什么

还有什么想问的吗,前两面感觉怎么样

  1. 该职位的具体职责和未来的设想?
  2. 具体是想找什么样的人来胜任这个岗位?
  3. 怎么评估我的能力,有什么评价或者是建议?
  4. 您觉得XXX(你面试的这个部门业务)对集团价值是怎样的,怎么理解这里面的衡量数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值