leetcode刷面试题(面试题04合集)

面试题 04.01. 节点间通路
节点间通路。给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。

示例1:
输入:n = 3, graph = [[0, 1], [0, 2], [1, 2], [1, 2]], start = 0, target = 2
输出:true

示例2:
输入:n = 5, graph = [[0, 1], [0, 2], [0, 4], [0, 4], [0, 1], [1, 3], [1, 4], [1, 3], [2, 3], [3, 4]], start = 0, target = 4
输出 true

提示:
节点数量n在[0, 1e5]范围内。
节点编号大于等于 0 小于 n。
图中可能存在自环和平行边。

public boolean findWhetherExistsPath(int n, int[][] graph, int start, int target) {
    Node[] nodes = new Node[n];
    for (int i = 0; i < graph.length; i++) {
        if (nodes[graph[i][0]] == null) {
            nodes[graph[i][0]] = new Node();
        }
        if (nodes[graph[i][1]] == null) {
            nodes[graph[i][1]] = new Node();
        }
        if (nodes[graph[i][0]].children == null) {
            nodes[graph[i][0]].children = new HashSet<Integer>();
        }
        nodes[graph[i][0]].children.add(graph[i][1]);
    }
    if (nodes[start] == null || nodes[target] == null) {
        return false;
    }
    boolean[] ans = new boolean[n];
    doTrue(nodes, start, ans);
    return ans[target];
}

private void doTrue(Node[] nodes, int start, boolean[] ans) {
    if (ans[start]) {
        return;
    }
    ans[start] = true;
    if (nodes[start].children == null) {
        return;
    }
    for (Integer child : nodes[start].children) {
        doTrue(nodes, child, ans);
    }
}

class Node {
    Set<Integer> children;
}

面试题 04.02. 最小高度树

给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。
示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0 
     / \ 
   -3   9 
   /   / 
 -10  5 
public TreeNode sortedArrayToBST(int[] nums) {
    return getTreeNode(nums, 0, nums.length - 1);
}

private TreeNode getTreeNode(int[] nums, int start, int end) {
    if (start > end) {
        return null;
    }
    int middle = (start + end) / 2;
    TreeNode treeNode = new TreeNode(nums[middle]);
    treeNode.left = getTreeNode(nums, start, middle - 1);
    treeNode.right = getTreeNode(nums, middle + 1, end);
    return treeNode;
}

面试题 04.03. 特定深度节点链表
给定一棵二叉树,设计一个算法,创建含有某一深度上所有节点的链表(比如,若一棵树的深度为 D,则会创建出 D 个链表)。返回一个包含所有深度的链表的数组。

示例:

输入:[1,2,3,4,5,null,7,8]

        1
       /  \ 
      2    3
     / \    \ 
    4   5    7
   /
  8

输出:[[1],[2,3],[4,5,7],[8]]

public ListNode[] listOfDepth(TreeNode tree) {
    if (tree == null) {
        return new ListNode[0];
    }
    int deep = getDeep(tree);
    ListNode[] ans = new ListNode[deep];
    ListNode[] list = new ListNode[deep];
    addAns(tree, ans, list, 0);
    return ans;
}

private void addAns(TreeNode tree, ListNode[] ans, ListNode[] list, int k) {
    if (tree == null) {
        return;
    }
    ListNode node = new ListNode(tree.val);
    if (list[k] == null) {
        ans[k] = node;
        list[k] = node;
    } else {
        list[k].next = node;
        list[k] = node;
    }
    addAns(tree.left, ans, list, k + 1);
    addAns(tree.right, ans, list, k + 1);
}

private int getDeep(TreeNode tree) {
    if (tree == null) {
        return 0;
    }
    return Math.max(getDeep(tree.left), getDeep(tree.right))+1;
}

面试题 04.04. 检查平衡性
实现一个函数,检查二叉树是否平衡。在这个问题中,平衡树的定义如下:任意一个节点,其两棵子树的高度差不超过 1。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

      1
     / \
    2   2
   / \
  3   3
 / \
4   4

返回 false 。

boolean balanced = true;

public boolean isBalanced(TreeNode root) {
    int k = getDeep(root);
    return balanced;
}

private int getDeep(TreeNode node) {
    if (node == null) {
        return 0;
    }
    int left = getDeep(node.left);
    int right = getDeep(node.right);
    if (left - right > 1 || right - left > 1) {
        balanced = false;
    }
    return Math.max(left, right) + 1;
}

面试题 04.05. 合法二叉搜索树

实现一个函数,检查一棵二叉树是否为二叉搜索树。
示例 1:

输入:

    2
   / \
  1   3

输出: true

示例 2:

输入:

    5
   / \
  1   4
     / \
    3   6

输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。

public boolean isValidBST(TreeNode root) {
    if (root == null) {
        return true;
    }
    int[] ans = getValid(root);
    return ans[2] == 1;
}

private int[] getValid(TreeNode node) {
    if (node.left == null && node.right == null) {
        return new int[]{node.val, node.val, 1};
    }
    if (node.left == null) {
        int[] ans = getValid(node.right);
        if (ans[2] == 0) {
            return new int[]{0, 0, 0};
        }
        return new int[]{node.val, ans[1], node.val < ans[0] ? 1 : 0};
    }
    if (node.right == null) {
        int[] ans = getValid(node.left);
        if (ans[2] == 0) {
            return new int[]{0, 0, 0};
        }
        return new int[]{ans[0], node.val, node.val > ans[1] ? 1 : 0};
    }
    int[] left = getValid(node.left);
    int[] right = getValid(node.right);
    if (left[2] == 0 || right[2] == 0) {
        return new int[]{0, 0, 0};
    }
    return new int[]{left[0], right[1], (node.val > left[1]) && (node.val < right[0]) ? 1 : 0};
}

面试题 04.06. 后继者
设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。

如果指定节点没有对应的“下一个”节点,则返回null。

示例 1:

输入: root = [2,1,3], p = 1

  2
 / \
1   3

输出: 2

示例 2:

输入: root = [5,3,6,2,4,null,null,1], p = 6

       5
      / \
     3   6
    / \
  2   4
 /   
1

输出: null

//标记前者
TreeNode node = null;
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
    if (root == null) {
        return null;
    }
    if (root.left != null) {
        TreeNode node = inorderSuccessor(root.left, p);
        if (node != null) {
			//找到答案直接返回
            return node;
        }
    }
    if (node == p) {
		//前者和p一样,直接返回本答案
        return root;
    } else {
		//把root标记着前者
        node = root;
    }
    if (root.right == null) {
        return null;
    }
    return inorderSuccessor(root.right, p);
}

面试题 04.08. 首个共同祖先

设计并实现一个算法,找出二叉树中某两个节点的第一个共同祖先。不得将其他的节点存储在另外的数据结构中。注意:这不一定是二叉搜索树。
例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

	3
   / \
  5   1
 / \ / \
6  2 0  8
  / \
 7   4

示例 1:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输入: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

示例 2:
输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。

TreeNode ans = null;

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null) {
        return null;
    }
    //查询左节点
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    if (ans != null) {
        //如果有答案,直接返回
        return ans;
    }
    if (left != null) {
        //左节点有1个结果
        if (root == p || root == q) {
            //如果root也是,那成功了
            ans = root;
            return root;
        }
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if (right != null) {
            //右节点也有结果了
            //那root就是公共节点
            ans = root;
            return root;
        } else {
            return left;
        }
    }
    //下面判断左节点查询无果
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    if (ans != null) {
        //找到答案,直接返回
        return ans;
    }
    if (right != null) {
        if (root == p || root == q) {
            //右结果是结果之一,就判断root了
            ans = root;
            return root;
        } else {
            return right;
        }
    }
    if (root == p || root == q) {
        return root;
    }
    return null;
}

面试题 04.09. 二叉搜索树序列
从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。给定一个由不同节点组成的二叉树,输出所有可能生成此树的数组。

示例:
给定如下二叉树

    2
   / \
  1   3

返回:
[
[2,1,3],
[2,3,1]
]

public List<List<Integer>> BSTSequences(TreeNode root) {
    if (root == null) {
        List<List<Integer>> ans = new ArrayList<>();
        ans.add(new ArrayList<>());
        return ans;
    }
    //查询倒序的
    List<List<Integer>> left = getAns(root);
    List<List<Integer>> ans = new ArrayList<>();
    //倒置
    for (List<Integer> list : left) {
        List<Integer> res = new ArrayList<>(list.size());
        for (int i = list.size() - 1; i >= 0; i--) {
            res.add(list.get(i));
        }
        ans.add(res);
    }
    return ans;
}

private List<List<Integer>> getAns(TreeNode node) {
    if (node.left == null && node.right == null) {
        //如果没有子节点,那就是自己
        List<List<Integer>> ans = new ArrayList<>();
        List<Integer> list = new ArrayList<>();
        list.add(node.val);
        ans.add(list);
        return ans;
    }
    if (node.right == null) {
        //右节点为空时,所有的之前的可能性,最后加上本val即可
        List<List<Integer>> anss = BSTSequences(node.left);
        for (List<Integer> list : anss) {
            list.add(node.val);
        }
        return anss;
    }
    if (node.left == null) {
        //左节点为空时,所有的之前的可能性,最后加上本val即可
        List<List<Integer>> anss = BSTSequences(node.right);
        for (List<Integer> list : anss) {
            list.add(node.val);
        }
        return anss;
    }
    List<List<Integer>> left = BSTSequences(node.left);
    List<List<Integer>> right = BSTSequences(node.right);
    List<List<Integer>> ans = new ArrayList<>();
    for (List<Integer> lis1 : left) {
        for (List<Integer> lis2 : right) {
            //左节点的可能性与右节点的可能性,进行混合
            addHH(ans, new ArrayList<>(), lis1, lis2, 0, 0, node.val);
        }
    }
    return ans;
}

//混合规矩很简单,原来在前面的还在前面,最后加上本val即可
private void addHH(List<List<Integer>> ans, List<Integer> res, List<Integer> lis1, List<Integer> lis2,
                   int sta1, int sta2, int val) {
    if (sta1 == lis1.size() && sta2 == lis2.size()) {
        res.add(val);
        ans.add(res);
        //遍历结束
        return;
    }
    if (sta1 == lis1.size()) {
        res.add(lis2.get(sta2));
        addHH(ans, res, lis1, lis2, sta1, sta2 + 1, val);
        return;
    }
    if (sta2 == lis2.size()) {
        res.add(lis1.get(sta1));
        addHH(ans, res, lis1, lis2, sta1 + 1, sta2, val);
        return;
    }
    List<Integer> res2 = new ArrayList<>(res);
    //两个可能性分别考虑
    res.add(lis1.get(sta1));
    addHH(ans, res, lis1, lis2, sta1 + 1, sta2, val);
    res2.add(lis2.get(sta2));
    addHH(ans, res2, lis1, lis2, sta1, sta2 + 1, val);
}

面试题 04.10. 检查子树

检查子树。你有两棵非常大的二叉树:T1,有几万个节点;T2,有几万个节点。设计一个算法,判断 T2 是否为 T1 的子树。
如果 T1 有这么一个节点 n,其子树与 T2 一模一样,则 T2 为 T1 的子树,也就是说,从节点 n 处把树砍断,得到的树与 T2 完全相同。

示例1:
输入:t1 = [1, 2, 3], t2 = [2]
输出:true

示例2:
输入:t1 = [1, null, 2, 4], t2 = [3, 2]
输出:false

提示:
树的节点数目范围为[0, 20000]。

答:每个节点都去判断,如果所有节点数值都一样,判断会很多次。我的想法是,找到同一深度的,再去判断,能减少判断次数。

boolean ans = false;
public boolean checkSubTree(TreeNode t1, TreeNode t2) {
    if (t2 == null) {
        return true;
    }
    if (t1 == null) {
        return false;
    }
    //获取t2的深度
    int deep = getDeep(t2);
    getDeepByCheck(t1, deep, t2);
    return ans;
}

private int getDeepByCheck(TreeNode node, int deep, TreeNode t2) {
    if (node == null) {
        return 0;
    }
    //判断子节点
    int thisDeep = Math.max(getDeepByCheck(node.left, deep, t2), getDeepByCheck(node.right, deep, t2)) + 1;
    if (thisDeep == deep) {
        if (ans) {
            //如果已经成功,就不再判断
            return thisDeep;
        }
        if (check(node, t2)) {
            ans = true;
        }
    }
    return thisDeep;
}

//判断是否一样
private boolean check(TreeNode node1, TreeNode node2) {
    if (node1 == null && node2 == null) {
        return true;
    }
    if (node1 == null || node2 == null) {
        return false;
    }
    if (node1.val != node2.val) {
        return false;
    }
    if (!check(node1.left, node2.left)) {
        return false;
    }
    if (!check(node1.right, node2.right)) {
        return false;
    }
    return true;
}

private int getDeep(TreeNode node) {
    if (node == null) {
        return 0;
    }
    return Math.max(getDeep(node.left), getDeep(node.right)) + 1;
}

面试题 04.12. 求和路径
给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,打印节点数值总和等于某个给定值的所有路径的数量。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束,但是其方向必须向下(只能从父节点指向子节点方向)。

示例:
给定如下二叉树,以及目标和 sum = 22,

          5
         / \
        4   8
       /   / \
      11  13  4
     /  \    / \
    7    2  5   1

返回:

3
解释:和为 22 的路径有:[5,4,11,2], [5,8,4,5], [4,11,7]

提示:
节点总数 <= 10000

原答案。

int ans = 0;
public int pathSum(TreeNode root, int sum) {
    check(root, sum);
    return ans;
}

private List<Integer> check(TreeNode node, int sum) {
    if (node == null) {
        return new ArrayList<>();
    }
    List<Integer> set1 = check(node.left, sum);
    List<Integer> set2 = check(node.right, sum);
    List<Integer> res = new ArrayList<>();
    for (Integer v : set1) {
        res.add(v + node.val);
        if (v + node.val == sum) {
            ans++;
        }
    }
    for (Integer v : set2) {
        res.add(v + node.val);
        if (v + node.val == sum) {
            ans++;
        }
    }
    res.add(node.val);
    if (sum == node.val) {
        ans++;
    }
    return res;
}

最快的答案,理解之后。

public int pathSum(TreeNode root, int sum) {
    if (root == null) {
        return 0;
    }
    Map<Integer, Integer> map = new HashMap<>();
    //默认有一个sum为0的
    map.put(0, 1);
    return doPathSum(root, map, sum, 0);
}

//all代表从根节点一直加到本节点一路的值的总和
//map保留这一路的值
//如一个树一直left的结果是:1,2,3,4,5,6
//那all分别是1,3,6,10,15
//如果sum是3,那总共就有2个可能性,3-0,6-3;
//这么一考虑,就可以理解成,每个树从上到下的链表,我们用map来保存前序和,差值就是中间一部分的和的值
private int doPathSum(TreeNode node, Map<Integer, Integer> map, int sum, int all) {
    if (node == null) {
        return 0;
    }
    all += node.val;
    int res = 0;
    res += map.getOrDefault(all - sum, 0);
    //递归下的都是要用到的
    map.put(all, map.getOrDefault(all, 0) + 1);
    res += doPathSum(node.left, map, sum, all);
    res += doPathSum(node.right, map, sum, all);
    //不干扰后面的,进行减除
    map.put(all, map.get(all) - 1);
    return res;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值