给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数
说明: 叶子节点是指没有子节点的节点
class Solution{
public int maxDepth(TreeNode root){
if(null==root) return 0;
return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
}
}
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
**说明:**叶子节点是指没有子节点的节点。
class Solution{
public int minDepth(TreeNode root){
if(null==root) return 0;
int leftH = minDepth(root.left);
int rightH = minDepth(root.right);
if(0==rightH||0==leftH){
return rightH+leftH+1;
}else{
return Math.min(leftH,rightH)+1;
}
}
}
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(null==root) return false;
if(null==root.left && null==root.right){
return targetSum==root.val;
}
return hasPathSum(root.left, targetSum-root.val) || hasPathSum(root.right, targetSum-root.val);
}
}
输入一个二叉树,该函数输出它的镜像。
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root==null) return null;
TreeNode left = root.left;
TreeNode right = root.right;
if(left==null && right==null){
return root;
}
root.left = mirrorTree(right);
root.right = mirrorTree(left);
return root;
}
}
给你两棵二叉树的根节点 p
和 q
,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p==null && q==null) return true;
if(p==null || q==null) return false;
if(p.val != q.val) return false;
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
}
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的.
class Solution {
private boolean dfs(TreeNode left,TreeNode right){
if(left==null && right==null) return true;
if(left==null || right==null) return false;
if(left.val!=right.val) return false;
return dfs(left.right,right.left) && dfs(left.left,right.right);
}
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
return dfs(root.left,root.right);
}
}
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
class Solution {
public int sumNumbers(TreeNode root) {
return dfs(root,0);
}
private int dfs(TreeNode root,int fromNums){
if(root==null) return 0;
fromNums = fromNums*10 + root.val;
if(root.left==null &&root.right==null) return fromNums;
return dfs(root.right,fromNums)+ dfs(root.left,fromNums);
}
}
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null || root==p ||root ==q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left==null) return right;
if(right==null) return left;
return root;
}
}
根据一棵树的前序遍历与中序遍历构造二叉树
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder.length>0&&inorder.length>0){
TreeNode root = new TreeNode(preorder[0]);
int num=0;
//从中序遍历序列中查找根节点位置
for(int i =0; i<inorder.length; i++){
if(inorder[i]==root.val){
num=i;
}
}
int[] preLeft = Arrays.copyOfRange(preorder,1,num+1);
int[] preRight = Arrays.copyOfRange(preorder,num+1,preorder.length);
int[] inoLeft = Arrays.copyOfRange(inorder,0,num);
int[] inoRight = Arrays.copyOfRange(inorder,num+1,inorder.length);
root.left=buildTree(preLeft,inoLeft);
root.right=buildTree(preRight,inoRight);
return root;
}else{
return null;
}
}
}
#由于python数组索引切片跟数组的index非常方便,感受一下python的魅力
class Solution(object):
def buildTree(self, inorder, postorder):
if postorder:
root=TreeNode(postorder[-1])
index = inorder.index(postorder[-1])
root.left = self.buildTree(inorder[:index],postorder[:index])
root.right = self.buildTree(inorder[index+1:],postorder[index:-1])
return root
返回与给定前序遍历 preorder 相匹配的二叉搜索树(binary search tree)的根结点。
(回想一下,二叉搜索树是二叉树的一种,其每个节点都满足以下规则,对于 node.left 的任何后代,值总 < node.val,而 node.right 的任何后代,值总 > node.val。此外,前序遍历首先显示节点 node 的值,然后遍历 node.left,接着遍历 node.right。)
题目保证,对于给定的测试用例,总能找到满足要求的二叉搜索树。
class Solution(object):
def bstFromPreorder(self, preorder):
if preorder:
root = TreeNode(preorder[0])
root.left = self.bstFromPreorder([x for x in preorder[1:] if x<preorder[0] ])
root.right = self.bstFromPreorder([x for x in preorder[1:] if x>preorder[0]])
return root
class Solution {
public int countComponents(int n, int[][] edges) {
// 所有节点都没被访问
boolean[] visited = new boolean[n];
//构建邻接表
List<Integer> [] adj = new ArrayList[n];
for (int i = 0; i < n; i++) {
adj[i] = new ArrayList<>();
}
for(int[] edge: edges){
adj[edge[0]].add(edge[1]);
adj[edge[1]].add(edge[0]);
}
//深度优先遍历
int count=0;
for(int i=0;i<n;i++){
if(!visited[i]){
dfs(adj,i,visited);
count++;
}
}
return count;
}
private void dfs(List<Integer> [] adj,int u,boolean[] visited){
visited[u] = true;
List<Integer> path = adj[u];
for(int p:path){
if(!visited[p]){
dfs(adj,p,visited);
}
}
}
}
[冗余连接][https://leetcode-cn.com/problems/redundant-connection/]
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。
返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。
class Solution {
private Map<Integer, List<Integer>> adj; //保存每个顶点能到达的所有顶点
private Set<Integer> visited; //为了减少dfs次数,没有出现过的节点直接add这条边
public int[] findRedundantConnection(int[][] edges) {
this.adj = new HashMap<>();
this.visited = new HashSet<>();
for (int[] edge : edges) {
int u = edge[0];
int v = edge[1];
if (adj.containsKey(u) && adj.containsKey(v)) {
visited.clear();
if (dfs(u, v)) {
return edge;
}
}
addEdg(v, u);
addEdg(u, v);
}
return null;
}
private void addEdg(int u, int v) {
if (adj.containsKey(u)) {
adj.get(u).add(v);
return;
}
List<Integer> successors = new ArrayList<>();
successors.add(v);
adj.put(u, successors);
}
private boolean dfs(int from, int to) {
if (to == from) return true;
visited.add(from);
for (Integer point : adj.get(from)) {
if (!visited.contains(point)) {
if (dfs(point, to)) return true;
}
}
return false;
}
}
[找到最终安全状态][https://leetcode-cn.com/problems/find-eventual-safe-states/]
public class Solution {
private Boolean[] visited;
public List<Integer> eventualSafeNodes(int[][] graph) {
int len = graph.length;
visited = new Boolean[len];
List<Integer> res = new ArrayList<>();
for (int i = 0; i < len; ++i) {
if (dfs(i, graph)) {
continue;
}
res.add(i);
}
return res;
}
/**
* @param u
* @param graph
* @return 从顶点 u 出发的所有路径是不是有一条能够回到 u,有回路就返回 true
*/
private boolean dfs(int u, int[][] graph) {
if (visited[u] != null) {
return visited[u];
}
// 先默认从 u 出发的所有路径有回路
visited[u] = true;
// 结点 u 的所有后继结点都不能回到自己,才能认为结点 u 是安全的
for (int successor : graph[u]) {
if (dfs(successor, graph)) {
return true;
}
}
visited[u] = false;
return false;
}
}
栈这种后进先出的数据结构,对于迭代法写深度优先搜索有很重要的作用,同样,栈也被用于回溯算法,先看一看,怎么样使用栈来迭代深度优先算法吧:
前序遍历二叉树的迭代写法
//前序遍历的迭代写法
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
while (!stack.isEmpty() || null != root) {
while (null != root) {
stack.push(root);
//由于前序遍历是先遍历根节点,所以在压入栈的时候就可以输出该节点的值
res.add(root.val);
// 根->左节点
root = root.left;
}
//因为是从根节点过来的 pop出栈之后即可获得上一层节点
root = stack.pop();
root = root.right;
}
return res;
}
中序遍历二叉树的迭代写法
//中序遍历的迭代写法
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
while (!stack.isEmpty() || null != root) {
while (null != root) {
stack.push(root);
root = root.left;
}
root = stack.pop();
//最下面的左节点先输出
res.add(root.val);
root = root.right;
}
return res;
}
后序遍历二叉树的迭代写法
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Deque<TreeNode> stack = new LinkedList<>();
TreeNode prev = null;
while (!stack.isEmpty() || null != root) {
while (null != root) {
stack.push(root);
root = root.left;
}
root = stack.pop();
if(root.right == null || root.right==prev){
//先输出左节点
res.add(root.val);
prev = root;
root = null;
}else{
stack.push(root);
//再输出右节点
root = root.right;
}
}
return res;
}