代码随想录 刷题攻略
电信保温杯笔记——代码随想录 刷题攻略
讲义地址
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode() {
}
TreeNode(int val) {
this.val = val;
}
TreeNode(int val, TreeNode left, TreeNode right) {
this.val = val;
this.left = left;
this.right = right;
}
}
class Test{
public static void main(String[] args) {
TreeNode root = createTree("[2,1,3,null,4,null,7]");
Solution solution = new Solution();
}
// string的输入格式为:"[2,1,3,null,4,null,7]"
public static TreeNode createTree(String string){
if (string.equals("[]")){
return null;
}
ArrayList<Integer> list = new ArrayList<>();
String temp = "";
for (int i = 1; i < string.length(); i++) {
char ch = string.charAt(i);
if (ch == ',' || ch == ']') {
if (temp.equals("null")) {
// null点先赋值为无穷小,后面再剪枝
list.add(Integer.MIN_VALUE);
}else {
list.add(Integer.valueOf(temp));
}
temp = "";
}else {
temp += ch;
}
}
if (list.get(0) == Integer.MIN_VALUE){
return null;
}
TreeNode root = new TreeNode();
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
TreeNode cur;
for (int i = 0; i < list.size(); i++) {
cur = queue.poll();
cur.val = list.get(i);
// null点先赋值为无穷小,后面再剪枝
cur.left = new TreeNode(Integer.MIN_VALUE);
cur.right = new TreeNode(Integer.MIN_VALUE);
queue.offer(cur.left);
queue.offer(cur.right);
}
// 剪枝,将值为无穷小的子节点删除
queue.clear();
queue.offer(root);
while (!queue.isEmpty()) {
cur = queue.poll();
if (cur.left.val == Integer.MIN_VALUE) {
cur.left = null;
}else {
queue.offer(cur.left);
}
if (cur.right.val == Integer.MIN_VALUE) {
cur.right = null;
}else {
queue.offer(cur.right);
}
}
return root;
}
}
二叉树遍历有前序、中序、后序、层次遍历4种,其中前序、中序、后序的区别在于:左右节点的操作相对次序不变,中节点的操作在左右节点的前中后,就代表前序、中序、后序。
一周刷爆LeetCode,算法da神左神(左程云)耗时100天打造算法与数据结构基础到高级全家桶教程,直击BTAJ等一线大厂必问算法面试题真题详解 笔记的P6 5.二叉树中,基础第五课题目一:二叉树节点结构,有讲解先序中序后序遍历的递归与非递归方法。
下面三题的非递归方法在下两章中。
leetcode地址
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
preOrderRecurrent(root, res);
return res;
}
public void preOrderRecurrent(TreeNode node, List<Integer> res){
if (node == null){
return;
}
res.add(node.val);
preOrderRecurrent(node.left, res);
preOrderRecurrent(node.right, res);
}
}
leetcode地址
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
inOrderRecurrent(root, res);
return res;
}
public void inOrderRecurrent(TreeNode node, List<Integer> res){
if (node == null){
return;
}
inOrderRecurrent(node.left, res);
res.add(node.val);
inOrderRecurrent(node.right, res);
}
}
leetcode地址
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
postOrderRecurrent(root, res);
return res;
}
public void postOrderRecurrent(TreeNode node, List<Integer> res){
if (node == null){
return;
}
postOrderRecurrent(node.left, res);
postOrderRecurrent(node.right, res);
res.add(node.val);
}
}
讲义地址
非递归方法又分为统一法和非统一法。
统一法:先序中序后序的代码结构相同,其中几条代码顺序不同;
非统一法:3者代码风格不能做到统一写法,只有前序和后序是相似的,中序的逻辑与另外两种不同。
统一法在下一章中介绍。
leetcode地址
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
stack.add(root);
while (!stack.empty()){
TreeNode temp = stack.pop();
res.add(temp.val);
if (temp.right != null){
stack.push(temp.right);
}
if (temp.left != null){
stack.push(temp.left);
}
}
return res;
}
}
leetcode地址
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.empty()){
if (cur != null){
stack.push(cur);
cur = cur.left;
}else{
cur = stack.pop();
res.add(cur.val);
cur = cur.right;
}
}
return res;
}
}
左节点需要遍历到底,才会开始遍历右节点,右节点再重复遍历自身左节点的操作。
里面的if-else可以改为while
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) {
return res;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.empty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
res.add(cur.val);
cur = cur.right;
}
return res;
}
}
leetcode地址
这是先序遍历的基础上修改,将先序变成中右左,然后再倒序得到的,并不是真正的后序遍历
。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.empty()){
TreeNode temp = stack.pop();
res.add(temp.val);
if (temp.left != null){
// 放入左节点
stack.push(temp.left);
}
if (temp.right != null){
// 放入右节点
stack.push(temp.right);
}
}
Collections.reverse(res);
return res;
}
}
如果不使用reverse操作,可以使用多一个stack来收集val,然后将stack吐出到res即可。
真正的后序遍历
。
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;// 这个指针用于添加节点
TreeNode temp = null;// 这个指针用于对stack顶节点进行判断
TreeNode pre = null;// 这个指针用于判断当前节点右节点是否遍历过
while (cur != null || !stack.empty()){
while (cur != null){
stack.push(cur);
cur = cur.left;
}
temp = stack.peek();
// 查看当前节点的右节点是否遍历过,null相对于遍历了
if (temp.right == null || temp.right == pre){
// 右节点已经遍历过
pre =stack.pop();
res.add(pre.val);
cur = null;
}else {
//没有遍历过
cur = temp.right;
}
}
return res;
}
}
讲义地址
非递归(栈)统一法效率低,因为它加入了大量的null记号作为中节点的标记,虽然记一种当三种用,不过效率低,面试意义不大。
这个方法的核心思想:以先序为例,将栈节点弹出,化栈节点为3个节点,将这三个节点压进去栈,顺序是右左中null,弹出顺序就是null中左右,以null为中节点的标记,意思就是中节点的左右子节点已经遍历完了。
leetcode地址
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode cur = root.left;
while (!stack.empty()){
TreeNode temp = stack.pop();
if (temp != null){
// 由于先序的出栈顺序是中左右,所以入栈顺序是右左中
if (temp.right != null){
// 放入右节点
stack.push(temp.right);
}
if (temp.left != null){
// 放入左节点
stack.push(temp.left);
}
// 放入中节点和null,null作为中节点的标记
stack.push(temp);
stack.push(null);
}else {
temp = stack.pop();
res.add(temp.val);
}
}
return res;
}
}
leetcode地址
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode cur = root.left;
while (!stack.empty()){
TreeNode temp = stack.pop();
if (temp != null){
// 由于中序的出栈顺序是左中右,所以入栈顺序是右中左
if (temp.right != null){
// 放入右节点
stack.push(temp.right);
}
// 放入中节点和null,null作为中节点的标记
stack.push(temp);
stack.push(null);
if (temp.left != null){
// 放入左节点
stack.push(temp.left);
}
}else {
temp = stack.pop();
res.add(temp.val);
}
}
return res;
}
}
leetcode地址
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode cur = root.left;
while (!stack.empty()){
TreeNode temp = stack.pop();
if (temp != null){
// 由于后序的出栈顺序是左右中,所以入栈顺序是中右左
// 放入中节点和null,null作为中节点的标记
stack.push(temp);
stack.push(null);
if (temp.right != null){
// 放入右节点
stack.push(temp.right);
}
if (temp.left != null){
// 放入左节点
stack.push(temp.left);
}
}else {
temp = stack.pop();
res.add(temp.val);
}
}
return res;
}
}
讲义地址
层次遍历,实质就是先序遍历。但并不是输出结果的数组拼接起来就是先序遍历的顺序,因为并不是每一层的数组填满了再填下一层的数组。
leetcode地址
根节点代表第0层。level代表node所在的层。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
ArrayList res = new ArrayList<>();
levelOrderRecurrent(root, 0, res);
return res;
}
public void levelOrderRecurrent(TreeNode node, int level, List<List<Integer>> res) {
if (node == null) {
return;
}
if (res.size() <= level) {
res.add(new ArrayList<>());
}
res.get(level).add(node.val);
levelOrderRecurrent(node.left, level + 1, res);
levelOrderRecurrent(node.right, level + 1, res);
return;
}
}
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
ArrayList res = new ArrayList<>();
levelOrderRecurrent(root, 1, res);
return res;
}
public void levelOrderRecurrent(TreeNode node, int depth, List<List<Integer>> res) {
if (node == null) {
return;
}
if (res.size() < depth) {
res.add(new ArrayList<>());
}
res.get(depth - 1).add(node.val);
levelOrderRecurrent(node.left, depth + 1, res);
levelOrderRecurrent(node.right, depth + 1, res);
return;
}
}
用一个变量len记录该层的长度,也就是当前队列的长度。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
ArrayList res = new ArrayList<>();
if (root == null){
return res;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
ArrayList<Integer> list = null;
TreeNode temp = null;
int len = 0;
while (!queue.isEmpty()){
len = queue.size();
list = new ArrayList<>();
while (len > 0){
temp = queue.pop();
list.add(temp.val);
if (temp.left != null){
queue.add(temp.left);
}
if (temp.right != null){
queue.add(temp.right);
}
len--;
}
res.add(list);
}
return res;
}
}
以null作为该层结尾的标记。
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
ArrayList res = new ArrayList<>();
if (root == null){
return res;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root);
queue.add(null);
ArrayList<Integer> list = new ArrayList<>();
TreeNode temp = null;
while (!queue.isEmpty()){
temp = queue.pop();
if (temp != null){
list.add(temp.val);
if (temp.left != null){
queue.add(temp.left);
}
if (temp.right != null){
queue.add(temp.right);
}
if (queue.peek() == null){
queue.add(null);
}
}else {
if (queue.isEmpty()){
break;
}
res.add(list);
list = new ArrayList<Integer>();
}
}
return res;
}
}
107.二叉树的层次遍历II
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的层序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
104.二叉树的最大深度
111.二叉树的最小深度
讲义地址
leetcode地址
先序后序遍历都可以,也是各有三种方法,无非是先交换还是先遍历,唯独中序不行,不能遍历中穿插交换。
下面是先序的做法。
class Solution {
public TreeNode invertTree(TreeNode root) {
preOrderRecurrentFun(root);
return root;
}
public void preOrderRecurrentFun(TreeNode node){
if (node == null){
return;
}
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
preOrderRecurrentFun(node.left);
preOrderRecurrentFun(node.right);
}
}
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null){
return root;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode cur = null;
TreeNode temp = null;
while (!stack.empty()){
cur = stack.pop();
temp = cur.left;
cur.left = cur.right;
cur.right = temp;
if (cur.right != null){
stack.push(cur.right);
}
if (cur.left != null){
stack.push(cur.left);
}
}
return root;
}
}
讲义地址
讲义地址
leetcode地址
有点像先序遍历。有三种方法。
class Solution {
public boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}
private boolean compare(TreeNode left, TreeNode right) {
if (left == null && right != null) {
return false;
}
if (left != null && right == null) {
return false;
}
if (left == null && right == null) {
return true;
}
if (left.val != right.val) {
return false;
}
// 比较外侧
boolean compareOutside = compare(left.left, right.right);
// 比较内侧
boolean compareInside = compare(left.right, right.left);
return compareOutside && compareInside;
}
}
class Solution {
public boolean isSymmetric(TreeNode root) {
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(root.left);
queue.add(root.right);
TreeNode left = null;
TreeNode right = null;
while (!queue.isEmpty()){
left = queue.pop();
right = queue.pop();
if (left == null && right == null){
continue;
}
if (left == null || right == null || right.val != left.val){
return false;
}
queue.add(left.left);
queue.add(right.right);
queue.add(left.right);
queue.add(right.left);
}
return true;
}
}
100.相同的树
572.另一个树的子树
讲义地址
leetcode地址
根节点为第0层,它的子节点为第1层,如此类推,level代表当前层,maxLevel代表遇到的最大层。采用顺序记录的树结构:
class Solution {
public int maxDepth(TreeNode root) {
return preOrderRecurrentFun(root, 0, -1) + 1;
}
public int preOrderRecurrentFun(TreeNode node, int level, int maxLevel){
if (node == null){
return maxLevel;
}
maxLevel = Integer.max(level, maxLevel);
int leftMaxLevel = preOrderRecurrentFun(node.left, level + 1, maxLevel);
int rightMaxLevel = preOrderRecurrentFun(node.right, level + 1, maxLevel);
maxLevel =Integer.max(leftMaxLevel, rightMaxLevel);
return maxLevel;
}
}
还有逆序记录的树状结构,记录当前节点子树的最大高度height:
class Solution {
public int maxDepth(TreeNode root) {
return preOrderRecurrentFun(root);
}
public int preOrderRecurrentFun(TreeNode node){
if (node == null){
return 0;
}
int leftHeight = preOrderRecurrentFun(node.left);
int rightHeight = preOrderRecurrentFun(node.right);
return Integer.max(leftHeight, rightHeight) + 1;
}
}
层次遍历
class Solution {
public int maxDepth(TreeNode root) {
if (root == null){
return 0;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root);
TreeNode temp = null;
int depth = 0;
int len = 0;
while (!queue.isEmpty()){
len = queue.size();
while (len > 0){
temp = queue.poll();
if (temp.left != null){
queue.offer(temp.left);
}
if (temp.right != null){
queue.offer(temp.right);
}
len--;
}
depth++;
}
return depth;
}
}
559.n叉树的最大深度
讲义地址
leetcode地址
后序遍历。
class Solution {
public int minDepth(TreeNode root) {
return postOrderRecurrentFun(root);
}
public int postOrderRecurrentFun(TreeNode node){
if (node == null){
return 0;
}
int leftMinDepth = postOrderRecurrentFun(node.left);
int rightMinDepth = postOrderRecurrentFun(node.right);
if (leftMinDepth == 0){
return rightMinDepth + 1;
}
if(rightMinDepth == 0){
return leftMinDepth + 1;
}
return Integer.min(leftMinDepth, rightMinDepth) + 1;
}
}
class Solution {
public int minDepth(TreeNode root) {
if (root == null){
return 0;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
TreeNode temp = null;
int len = 0;
while (!queue.isEmpty()){
len = queue.size();
depth++;
while (len > 0){
temp = queue.poll();
// 碰到叶节点返回
if (temp.left == null && temp.right == null){
return depth;
}
if (temp.left != null){
queue.offer(temp.left);
}
if (temp.right != null){
queue.offer(temp.right);
}
len--;
}
}
return depth;
}
}
讲义地址
leetcode地址
class Solution {
public int countNodes(TreeNode root) {
return recurrentFun(root);
}
public int recurrentFun(TreeNode node){
if (node == null){
return 0;
}
return recurrentFun(node.left) + recurrentFun(node.right) + 1;
}
}
层次遍历。
class Solution {
public int countNodes(TreeNode root) {
if (root == null){
return 0;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int res = 0;
TreeNode temp = null;
int len = 0;
while (!queue.isEmpty()){
len = queue.size();
while (len > 0){
temp = queue.poll();
res++;
if (temp.left != null){
queue.offer(temp.left);
}
if (temp.right != null){
queue.offer(temp.right);
}
len--;
}
}
return res;
}
}
讲义地址
leetcode地址
后序遍历。
class Solution {
public boolean isBalanced(TreeNode root) {
return recurrent(root).isBalanced;
}
public Result recurrent(TreeNode node){
Result res = new Result();
if (node == null){
return res;
}
Result leftRes = recurrent(node.left);
Result rightRes = recurrent(node.right);
res.height = Integer.max(leftRes.height, rightRes.height) + 1;
boolean isBalanced = true;
if (Math.abs(leftRes.height - rightRes.height) > 1){
isBalanced = false;
}
res.isBalanced = isBalanced && leftRes.isBalanced && rightRes.isBalanced;
return res;
}
}
class Result{
int height = 0;
boolean isBalanced = true;
public Result() {
}
public Result(int height, boolean isBalanced) {
this.height = height;
this.isBalanced = isBalanced;
}
}
其实这个也是后序遍历,只是可以提前返回而已,只要将这行代码
if (leftHeight == -1) {
return -1;
}
下移一行,其实就是后序遍历。
class Solution {
/**
* 递归法
*/
public boolean isBalanced(TreeNode root) {
return getHeight(root) != -1;
}
private int getHeight(TreeNode root) {
if (root == null) {
return 0;
}
int leftHeight = getHeight(root.left);
if (leftHeight == -1) {
return -1;
}
int rightHeight = getHeight(root.right);
if (rightHeight == -1) {
return -1;
}
// 左右子树高度差大于1,return -1表示已经不是平衡树了
if (Math.abs(leftHeight - rightHeight) > 1) {
return -1;
}
return Math.max(leftHeight, rightHeight) + 1;
}
}
因为要比较左右节点的高度,所以因为使用真正的后序遍历。
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null){
return true;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;// 这个指针用于添加节点
TreeNode temp = null;// 这个指针用于对stack顶节点进行判断
TreeNode pre = null;// 这个指针用于判断当前节点右节点是否遍历过
while (cur != null || !stack.empty()){
while (cur != null){
stack.push(cur);
cur = cur.left;
}
temp = stack.peek();
// 查看当前节点的右节点是否遍历过,null相对于遍历了
if (temp.right == null || temp.right == pre){
// 右节点已经遍历过
if (Math.abs(getHeight(temp.left) - getHeight(temp.right)) > 1){
return false;
}
pre =stack.pop();
cur = null;
}else {
//没有遍历过
cur = temp.right;
}
}
return true;
}
public int getHeight(TreeNode root){
if (root == null){
return 0;
}
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int len;
int depth = 0;
TreeNode temp;
while (!queue.isEmpty()){
len = queue.size();
while (len > 0) {
temp = queue.poll();
if (temp.left != null){
queue.offer(temp.left);
}
if (temp.right != null) {
queue.offer(temp.right);
}
len--;
}
depth++;
}
return depth;
}
}
针对暴力迭代法的getHeight方法做优化,利用TreeNode.val来保存当前结点的高度,但个人认为这样会破坏了树本身的值。
讲义地址
leetcode地址
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
ArrayList<String> res = new ArrayList<>();
recurrent(root, "", res);
return res;
}
public void recurrent(TreeNode node, String path, List<String> res){
if (node == null){
return;
}
path = path + String.valueOf(node.val);
recurrent(node.left, path + "->" , res);
recurrent(node.right, path + "->" , res);
if (node.left == null && node.right == null){
res.add(path);
}
}
}
层次遍历。
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
ArrayList<String> res = new ArrayList<>();
if (root == null){
res.add("");
return res;
}
LinkedList<TreeNode> queue = new LinkedList<>();
ArrayList<String> curPaths;
ArrayList<String> oldPaths = new ArrayList<>();
oldPaths.add("");
queue.offer(root);
int len;
TreeNode temp;
String path;
while (!queue.isEmpty()){
len = queue.size();
curPaths = new ArrayList<>();
for (int i = 0; i < len; i++) {
temp = queue.poll();
path = oldPaths.get(i)+ String.valueOf(temp.val);
if (temp.left == null && temp.right == null) {
res.add(path);
continue;
}
if (temp.left != null){
queue.offer(temp.left);
curPaths.add(path + "->");
}
if (temp.right != null){
queue.offer(temp.right);
curPaths.add(path + "->");
}
}
oldPaths = curPaths;
}
return res;
}
}
讲义地址
讲义地址
讲义地址
leetcode地址
和先序后序中序层次遍历无关,主要是遍历的过程中如何判断是左叶子节点。
判断条件:当前点a的左子节点不为空,并且左子节点的左右子节点都为空,那么a的左子节点为左叶节点。
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
return recurrent(root);
}
public int recurrent(TreeNode node){
// level是node的上一层,根节点为0层,根节点的上一层为-1层
if (node == null) {
return 0;
}
int res = 0;
if (node.left != null) {
if (node.left.left == null && node.left.right == null){
res += node.left.val;
}
}
res += recurrent(node.left);
res += recurrent(node.right);
return res;
}
}
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if (root == null){
return 0;
}
int res = 0;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode temp;
while (!stack.empty()) {
temp = stack.pop();
if (temp.right != null){
stack.push(temp.right);
}
if (temp.left != null){
stack.push(temp.left);
if (temp.left.left == null && temp.left.right == null) {
res += temp.left.val;
}
}
}
return res;
}
}
讲义地址
leetcode地址
层次遍历。
class Solution {
public int findBottomLeftValue(TreeNode root) {
if (root == null){
return 0;
}
ArrayList<List<Integer>> res = new ArrayList<>();
levelOrderRecurrent(root, 1, res);
return res.get(res.size() - 1).get(0);
}
public void levelOrderRecurrent(TreeNode node, int depth, List<List<Integer>> res) {
if (node == null) {
return;
}
if (res.size() < depth) {
res.add(new ArrayList<Integer>());
}
res.get(depth - 1).add(node.val);
levelOrderRecurrent(node.left, depth + 1, res);
levelOrderRecurrent(node.right, depth + 1, res);
return;
}
}
层次遍历。
class Solution {
public int findBottomLeftValue(TreeNode root) {
if (root == null){
return 0;
}
int res = 0;
LinkedList<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int len;
TreeNode temp;
while (!queue.isEmpty()) {
len = queue.size();
res = queue.peek().val;
while (len > 0){
temp = queue.poll();
if (temp.left != null){
queue.offer(temp.left);
}
if (temp.right != null) {
queue.offer(temp.right);
}
len--;
}
}
return res;
}
}
讲义地址
leetcode地址
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null){
return false;
}
return preOrderRecurrent(root, 0, targetSum);
}
public boolean preOrderRecurrent(TreeNode node, int sum, int targetSum){
sum += node.val;
if (sum == targetSum && node.left == null && node.right == null) {
return true;
}
boolean b1 = false;
boolean b2 = false;
if (node.left != null){
b1 = preOrderRecurrent(node.left, sum, targetSum);
// 左子节点已经找到路径了,右节点就不用找了
if (b1 == true){
return true;
}
}
if (node.right != null){
b2 = preOrderRecurrent(node.right, sum, targetSum);
}
return b1 || b2;
}
}
层次遍历。
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null) {
return false;
}
LinkedList<TreeNode> queue = new LinkedList<>();
ArrayList<Integer> curSums;
ArrayList<Integer> oldSums = new ArrayList<>();
oldSums.add(0);
queue.offer(root);
int len;
int curSum;
TreeNode temp;
while (!queue.isEmpty()) {
len = queue.size();
curSums = new ArrayList<>();
for (int i = 0; i < len; i++) {
temp = queue.poll();
curSum = oldSums.get(i) + temp.val;
if (curSum == targetSum && temp.left == null && temp.right == null) {
return true;
}
// oldSum.get(i) < targetSum
if (temp.left != null) {
queue.offer(temp.left);
curSums.add(curSum);
}
if (temp.right != null) {
queue.offer(temp.right);
curSums.add(curSum);
}
}
oldSums = curSums;
}
return false;
}
}
leetcode地址
1
讲义地址
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
return Recurrent(inorder,0, inorder.length,
postorder, 0, postorder.length);
}
// 变量都是左闭右开的,left指向数组起始位置,right指向末尾元素的下一个位置
public TreeNode Recurrent(int[] inorder, int inLeft, int inRight,
int[] postorder, int postLeft, int postRight){
if (inRight - inLeft == 0){
return null;
}
// 1.找出中序数组中,中节点的位置
int inMid = 0;
int postMid = postRight -1;// 后序数组中,中节点的位置
for (int i = inLeft; i < inRight; i++) {
if (inorder[i] == postorder[postMid]){
inMid = i;
break;
}
}
TreeNode node = new TreeNode(postorder[postMid]);
// 2.划分后,左数组信息,
// 如果先序数组只有一个元素,即inLeft + 1 = inRight,
// 那么划分后,左数组的 newInLeft == newInRight,即左数组没有元素。
// 这些变量只是方便阅读,实际中可以直接将新的值放到函数中
int newInLeft = inLeft;
int newInRight = inMid;
int newPostLeft = postLeft;
int newPostRight = postLeft + inMid - inLeft;
node.left = Recurrent(inorder, newInLeft, newInRight,
postorder, newPostLeft, newPostRight);
// 3.划分后,右节点信息
newInLeft = inMid + 1;
newInRight = inRight;
newPostLeft = postLeft + inMid - inLeft;
newPostRight = postMid;
node.right = Recurrent(inorder, newInLeft, newInRight,
postorder, newPostLeft, newPostRight);
return node;
}
}
leetcode地址
1
讲义地址
讲义地址
leetcode地址
跟先序中序后续层次遍历无关。
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
return recurrent(root1, root2);
}
public TreeNode recurrent(TreeNode node1, TreeNode node2){
if (node1 == null && node2 == null){
return null;
}
TreeNode node = new TreeNode();
if (node1 == null && node2 != null){
node.val = node2.val;
node.left = recurrent(null, node2.left);
node.right = recurrent(null, node2.right);
}
if (node1 != null && node2 == null) {
node.val = node1.val;
node.left = recurrent(null, node1.left);
node.right = recurrent(null, node1.right);
}
if (node1 != null && node2 != null) {
node.val = node1.val + node2.val;
node.left = recurrent(node1.left, node2.left);
node.right = recurrent(node1.right, node2.right);
}
return node;
}
}
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
return recurrent(root1, root2);
}
public TreeNode recurrent(TreeNode node1, TreeNode node2) {
if (node1 == null) {
return node2;
}
if (node2 == null) {
return node1;
}
TreeNode node = new TreeNode(node1.val + node2.val);
node.left = recurrent(node1.left, node2.left);
node.right = recurrent(node1.right, node2.right);
return node;
}
}
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null){
return root2;
}
if (root2 == null) {
return root1;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode root = new TreeNode();
stack.push(root2);
stack.push(root1);
stack.push(root);
TreeNode node1;
TreeNode node2;
TreeNode cur;
while (!stack.empty()) {
cur = stack.pop();
node1 = stack.pop();
node2 = stack.pop();
cur.val = node1.val + node2.val;
// 处理当前节点的右节点
if (node1.right == null) {
cur.right = node2.right;
} else if (node2.right == null) {
cur.right = node1.right;
}else {
// node1.right != null && node1.right != null
stack.push(node2.right);
stack.push(node1.right);
cur.right = new TreeNode();
stack.push(cur.right);
}
// 处理当前节点的左节点
if (node1.left == null) {
cur.left = node2.left;
} else if (node2.left == null) {
cur.left = node1.left;
}else {
// node1.left != null && node1.left != null
stack.push(node2.left);
stack.push(node1.left);
cur.left = new TreeNode();
stack.push(cur.left);
}
}
return root;
}
}
讲义地址
leetcode地址
和遍历无关。
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
return preOrderRecurrent(root, val);
}
public TreeNode preOrderRecurrent(TreeNode node, int val) {
if (node == null) {
return null;
}
if (node.val == val) {
return node;
}
TreeNode left = preOrderRecurrent(node.left, val);
if (left != null) {
return left;
}
TreeNode right = preOrderRecurrent(node.right, val);
return right;
}
}
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if (root == null){
return null;
}
Stack<TreeNode> stack = new Stack<>();
stack.add(root);
while (!stack.empty()){
TreeNode temp = stack.pop();
if (temp.val == val){
return temp;
}
if (temp.right != null){
stack.push(temp.right);
}
if (temp.left != null){
stack.push(temp.left);
}
}
return null;
}
}
讲义地址
leetcode地址
中序遍历的节点,如果是升序排列,那么这棵树就是二叉搜索树,这是中序的性质。要在遍历中进行比较,最主要是要记录上一个节点的值。
class Solution {
TreeNode min;
public boolean isValidBST(TreeNode root) {
return recurrent(root);
}
public boolean recurrent(TreeNode node) {
if (node == null) {
return true;
}
boolean leftRes = recurrent(node.left);
// 如果左子树不满足,则提起终止
if (!leftRes) {
return false;
}
if (min != null && min.val >= node.val) {
return false;
}
min = node;
boolean rightRes = recurrent(node.right);
return rightRes;
}
}
class Solution {
public boolean isValidBST(TreeNode root) {
if (root == null) {
return true;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode min = null;
while (cur != null || !stack.empty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
if (min != null && min.val >= cur.val) {
return false;
}
min = cur;
cur = cur.right;
}
return true;
}
}
讲义地址
leetcode地址
中序遍历。
class Solution {
TreeNode last;
int res = Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
inOrderRecurrent(root);
return res;
}
public void inOrderRecurrent(TreeNode node){
if (node == null){
return;
}
inOrderRecurrent(node.left);
if (last != null){
res = Integer.min(res, Math.abs(last.val - node.val));
}
last = node;
inOrderRecurrent(node.right);
}
}
class Solution {
public int getMinimumDifference(TreeNode root) {
if (root == null || (root.left == null && root.right == null)) {
return 0;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode last = null; // 记录上一个节点
int res = Integer.MAX_VALUE;
while (cur != null || !stack.empty()) {
while (cur != null) {
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
if (last != null) {
res = Integer.min(res, Math.abs(last. val - cur.val));
}
last = cur;
cur = cur.right;
}
return res;
}
}
讲义地址
leetcode地址
中序遍历。
class Solution {
ArrayList<Integer> resList = new ArrayList<>();
int maxCount = 0;
int count = 0;
TreeNode pre = null;
public int[] findMode(TreeNode root) {
inOrderRecurrent(root);
int len = resList.size();
int[] res = new int[len];
for (int i = 0; i < len; i++) {
res[i] = resList.get(i);
}
return res;
}
public void inOrderRecurrent(TreeNode node) {
if (node == null) {
return;
}
inOrderRecurrent(node.left);
int rootValue = node.val;
// 计数
if (pre == null || rootValue != pre.val) {
count = 1;
} else {
count++;
}
// 更新结果以及maxCount
if (count > maxCount) {
resList.clear();
resList.add(rootValue);
maxCount = count;
} else if (count == maxCount) {
resList.add(rootValue);
}
pre = node;
inOrderRecurrent(node.right);
}
}
class Solution {
public int[] findMode(TreeNode root) {
ArrayList<Integer> resList = new ArrayList<>();
int maxCount = 0;
int count = 0;
TreeNode pre = null;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else {
cur = stack.pop();
// 计数
if (pre == null || cur.val != pre.val) {
count = 1;
} else {
count++;
}
// 更新结果
if (count > maxCount) {
maxCount = count;
resList.clear();
resList.add(cur.val);
} else if (count == maxCount) {
resList.add(cur.val);
}
pre = cur;
cur = cur.right;
}
}
int len = resList.size();
int[] res = new int[len];
for (int i = 0; i < len; i++) {
res[i] = resList.get(i);
}
return res;
// return result.stream().mapToInt(Integer::intValue).toArray();
}
}
讲义地址
leetcode地址
前提条件:节点不重复。
此题属于回溯,非递归法不适合模拟回溯的过程。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return postOrderRecurrent(root, p, q);
}
public TreeNode postOrderRecurrent(TreeNode node, TreeNode p, TreeNode q) {
if (node == null || node == p || node == q) {// 递归结束条件
return node;
}
TreeNode left = postOrderRecurrent(node.left, p, q);
TreeNode right = postOrderRecurrent(node.right, p, q);
if (left == null && right == null) {// 若未找到节点 p 或 q
return null;
}
if (left != null && right == null) {// 若找到一个节点
return left;
}
if (left == null && right != null) {// 若找到一个节点
return right;
}
// 两个节点都找到
return node;
}
}
讲义地址
讲义地址
leetcode地址
前提条件:节点不重复,节点2个输入节点都是存在于树中。
与遍历顺序无关。
节点q,p的关系无非下面三种:相互不包含,q包含p,p包含q
那么公共节点s一定在区间【p,q】或者【q,p】里面。
当s为父节点的左节点时,那么它一定比右边的那些节点的值要小:
同理,当s为父节点的右节点时,那么它一定比左边的那些节点的值要小:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
return recurrent(root, p, q);
}
public TreeNode recurrent(TreeNode node, TreeNode p, TreeNode q) {
if (node.val > p.val && node.val > q.val) {
return recurrent(node.left, p, q);
}
if (node.val < p.val && node.val < q.val) {
return recurrent(node.right, p, q);
}
return node;
}
}
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
TreeNode temp = root;
while (true) {
if (temp.val > p.val && temp.val > q.val) {
temp = temp.left;
}else if (temp.val < p.val && temp.val < q.val) {
temp = temp.right;
}else {
return temp;
}
}
}
}
讲义地址
leetcode地址
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
return recurrent(root,val);
}
public TreeNode recurrent(TreeNode node, int val){
if (node == null){
return new TreeNode(val);
}
if (node.val > val) {
node.left = recurrent(node.left, val);
}
if (node.val < val) {
node.right = recurrent(node.right,val);
}
return node;
}
}
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) {
return root = new TreeNode(val);
}
TreeNode cur = root;
while (true) {
if (cur.val > val) {
if (cur.left == null) {
cur.left = new TreeNode(val);
return root;
}else {
cur = cur.left;
}
}else {
if (cur.right == null) {
cur.right = new TreeNode(val);
return root;
}else {
cur = cur.right;
}
}
}
}
}
讲义地址
leetcode地址
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
return delete(root, key);
}
private TreeNode delete(TreeNode node, int key) {
if (node == null) {
return null;
}
if (node.val > key) {
node.left = delete(node.left, key);
} else if (node.val < key) {
node.right = delete(node.right, key);
} else {
// node.val == key,找到
if (node.left == null) {
return node.right;
}
if (node.right == null) {
return node.left;
}
// 左右子节点都不为空
TreeNode temp = node.right;
// 找右子树的最小值
while (temp.left != null) {
temp = temp.left;
}
// 最小值移动到删除节点的位置
node.val = temp.val;
// 删除原来的最小值
node.right = delete(node.right, temp.val);
}
return node;
}
}
讲义地址
leetcode地址
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
return recurrent(root, low, high);
}
public TreeNode recurrent(TreeNode node, int low, int high){
if (node == null) {
return null;
}
if (node.val > high) {
return recurrent(node.left, low, high);
}
if (node.val < low){
return recurrent(node.right, low, high);
}
node.left = recurrent(node.left, low, high);
node.right = recurrent(node.right, low, high);
return node;
}
}
30,31,32这三章的思路非常相似。
讲义地址
leetcode地址
左闭右开 [left,right),先序遍历。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return recurrent(nums, 0, nums.length);
}
public TreeNode recurrent(int[] nums, int left, int right) {
if (left >= right) {
return null;
}
if (right - left == 1) {
return new TreeNode(nums[left]);
}
int mid = left + ((right - left) >> 1);
TreeNode node = new TreeNode(nums[mid]);
node.left = recurrent(nums, left, mid);
node.right = recurrent(nums, mid + 1, right);
return node;
}
}
左闭右开 [left,right),层次遍历。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
if (nums.length == 0) return null;
//根节点初始化
TreeNode root = new TreeNode();
Queue<TreeNode> nodeQueue = new LinkedList<>();
Queue<Integer> leftQueue = new LinkedList<>();
Queue<Integer> rightQueue = new LinkedList<>();
// 根节点入队列
nodeQueue.offer(root);
// 0为左区间下标初始位置
leftQueue.offer(0);
// nums.size()为右区间下标初始位置
rightQueue.offer(nums.length);
while (!nodeQueue.isEmpty()) {
TreeNode cur = nodeQueue.poll();
int left = leftQueue.poll();
int right = rightQueue.poll();
int mid = left + ((right - left) >> 1);
// 将mid对应的元素给中间节点
cur.val = nums[mid];
// 处理左区间
if (left < mid) {
cur.left = new TreeNode();
nodeQueue.offer(cur.left);
leftQueue.offer(left);
rightQueue.offer(mid);
}
// 处理右区间
if (right > mid + 1) {
cur.right = new TreeNode();
nodeQueue.offer(cur.right);
leftQueue.offer(mid + 1);
rightQueue.offer(right);
}
}
return root;
}
}
讲义地址
leetcode地址
右中左的中序遍历。
class Solution {
int sum = 0;
public TreeNode convertBST(TreeNode root) {
recurrent(root);
return root;
}
public void recurrent(TreeNode node) {
if (node == null) {
return;
}
recurrent(node.right);
sum += node.val;
node.val = sum;
recurrent(node.left);
}
}
class Solution {
public TreeNode convertBST(TreeNode root) {
int sum = 0;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.empty()) {
while (cur != null) {
stack.push(cur);
cur = cur.right;
}
cur = stack.pop();
sum += cur.val;
cur.val = sum;
cur = cur.left;
}
return root;
}
}
讲义地址
if (与遍历有关) {
先序中序后序层次遍历;
各种遍历的性质:
比如:中序遍历的序列如果是降序排列的,那么它就是搜索二叉树
if (递归) {
1. 选择输入参数
2. 终止条件
3. 本轮递归的操作
4. 返回值
}else {
非递归
}
}else {
与遍历无关
}
if (自顶向下) {
比如计算深度、层数,单路径往下的
}else {
自底向上
比如向上递归返回子树的高度
}
各种结构的判断条件:
比如:叶节点的条件:一个节点的子节点存在,且子节点的左右子节点都为空;