简介
本来想重初中级和企业面试算法开始的,但是最后还是选择从基础的开始,因为我们并不是为了刷题而刷题,而是在刷题过程中锻炼一种算法思维,在大量的训练之后形成一种对算法的独特见解,培养那种对算法的的敏感度,看到题目,大脑中可以浮现一个解题蓝图,而且从初级开始慢慢建立信心,而且这也是在为后边复杂算法的解题思路打基础。
如果你也想训练自己的算法思维,也可以加入我,从初级算法开始,开启你的算法之旅:初级算法。
自己的一些思考:不要在看完题目后直接就看答案,然后去背题,这样行成的算法记忆是不牢固的,一定要有自己的思考;而且不要一开始就在IDEA上边去写,一定试着自己在leetCode提供的白板上边写一遍最后在放到IDEA上边去执行看有什么问题,以便巩固你的基础API的使用和熟练度;还有一点就是大胆些,不是面试我们试错成本低,尽可能把我们的想法融入到代码中
因篇幅问题,博客中只列出示例和自己的解题答案,详细可以直接点击题目查看。
二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
int max = 1;
int leftMax = maxDepth(root.left);
int rightMax = maxDepth(root.right);
return max + Math.max(leftMax,rightMax);
}
}
39 / 39 个通过测试用例
状态:通过
执行用时:0 ms
内存消耗:40 MB
这个用递归就是,当node为null就返回0,不为null默认就是1层,然后继续深入左右两个子节点,哪个大返回哪个,max + (leftMax和rightMax里边最大的),以此类推。
验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:
2
/ \
1 3
输出: true
示例 2:
输入:
5
/ \
1 4
/ \
3 6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
根节点的值为 5 ,但是其右子节点值为 4 。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean help(TreeNode root,Integer min,Integer max){
if(root == null) return true;
int val = root.val;
//保证了root的左子树的最大值小于root的值并且root的右子树的最小值大于root的值;
if(min != null && min >= val) return false;//只要root的左子树中有值大于root,就不是BFT;
if(max != null && max <= val) return false;//只要root的右子树中有值小于root,就不是BFT;
//保证了root的所有左子树和右子树都是BFT;
if(!help(root.left,min,val)) return false;//在遍历左子树的时候只关心最大值,所以将val赋给max;
if(!help(root.right,val,max)) return false;//在遍历右子树的时候只关心最小值,所以将val赋给min;
return true;
}
public boolean isValidBST(TreeNode root) {
if(root == null ) return true;
return help(root,null,null);
}
}
75 / 75 个通过测试用例
状态:通过
执行用时:0 ms
内存消耗:39.7 MB
对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
进阶:
你可以运用递归和迭代两种方法解决这个问题吗?
递归:两个指针分别走左右两侧,左指针和右指针比较,其实就是右子树和左子树的左节点和右节点比较比较。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
return check(root.left,root.right);
}
private boolean check(TreeNode p,TreeNode q) {
if(p == null && q == null) return true;
if(p == null || q == null) return false;
return p.val == q.val && check(p.left,q.right) && check(p.right,q.left);
}
}
195 / 195 个通过测试用例
状态:通过
执行用时:0 ms
内存消耗:37.7 MB
迭代:一对一对不断的加入队列和取出对比。也是左右两个子树的对比。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
return check(root.left,root.right);
}
public boolean check(TreeNode u, TreeNode v) {
Queue<TreeNode> q = new LinkedList<TreeNode>();
q.offer(u);
q.offer(v);
while (!q.isEmpty()) {
u = q.poll();
v = q.poll();
if (u == null && v == null) {
continue;
}
if ((u == null || v == null) || (u.val != v.val)) {
return false;
}
q.offer(u.left);
q.offer(v.right);
q.offer(u.right);
q.offer(v.left);
}
return true;
}
}
195 / 195 个通过测试用例
状态:通过
执行用时:1 ms
内存消耗:39.3 MB
二叉树的层序遍历
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
示例:
二叉树:[3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
深度优先
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<List<Integer>> lists = new ArrayList<>();
public List<List<Integer>> levelOrder(TreeNode root) {
if(root == null) return lists;
help(root,0);
return lists;
}
private void help(TreeNode node,int level){
//只有列表大小为当前level时新创建一个数组塞进去,这样其他的相同层级就避免多创建
if(lists.size() == level) {
lists.add(new ArrayList<Integer>());
}
//把node值添加到当前层级数组中
lists.get(level).add(node.val);
//递归root左侧下一个层级
if(node.left != null){
help(node.left,level + 1);
}
//递归root右侧侧下一个层级
if(node.right != null){
help(node.right,level + 1);
}
}
}
34 / 34 个通过测试用例
状态:通过
执行用时:1 ms
内存消耗:40.3 MB
这种深度优先的策略和广度优先理解起来稍微绕一点。
广度优先
public List<List<Integer>> levelOrder(TreeNode root) {
//思路 二叉树的层序遍历 通用思路为使用 队列,利用队列先进先出特点保存每一行的结点,遍历之后出队
//时间复杂度 空间复杂度 O(n)
List<List<Integer>> res = new ArrayList<>();
if(root == null){
return res;
}
//存储没层结点队列
Queue<TreeNode> queueNodes = new LinkedList<>();
//把当前根节点加入队列
queueNodes.add(root);
//遍历队列节点
while(!queueNodes.isEmpty()){
//获取当前层级有多少个节点
int count = queueNodes.size();
//保存当前遍历节点值得 list
List<Integer> temp = new ArrayList<>();
//根据队列中的数据 遍历获取节点值
for (int i = 0; i < count; i++) {
//当前节点是否有左右子节点
if(queueNodes.peek().left !=null){
queueNodes.offer(queueNodes.peek().left);
}
if(queueNodes.peek().right !=null){
queueNodes.offer(queueNodes.peek().right);
}
//保存当前节点行元素值数组 并从队列中删除
temp.add(queueNodes.poll().val);
}
//保存每一层节点数组
res.add(temp);
}
return res;
}
}
将有序数组转换为二叉搜索树
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5
刚开始思路是对的,但是对于找位置找边界没有找对。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return help(nums,0,nums.length - 1);
}
private TreeNode help(int[] nums,int left,int right){
if(left > right) return null;
int mid = (left + right)/2;
TreeNode root = new TreeNode(nums[mid]);
root.left = help(nums,left,mid - 1);
root.right = help(nums,mid + 1, right);
return root;
}
}
32 / 32 个通过测试用例
状态:通过
执行用时:0 ms
内存消耗:39.6 MB