树
树是一种非线性数据结构,根据子节点数量可分为 「二叉树」 和 「多叉树」,最顶层的节点称为「根节点 root」。以二叉树为例,每个节点包含三个成员变量:「值 val」、「左子节点 left」、「右子节点 right」 。
class TreeNode {
int val; // 节点值
TreeNode left; // 左子节点
TreeNode right; // 右子节点
TreeNode(int x) { val = x; }
}
//建立此二叉树需要实例化每个节点,并构建各节点的引用指向。
// 初始化节点
TreeNode n1 = new TreeNode(3); // 根节点 root
TreeNode n2 = new TreeNode(4);
TreeNode n3 = new TreeNode(5);
TreeNode n4 = new TreeNode(1);
TreeNode n5 = new TreeNode(2);
// 构建引用指向
n1.left = n2;
n1.right = n3;
n2.left = n4;
n2.right = n5;
一、二叉树的前序遍历
//递归
class Solution {
public List<Integer> preOrderRecur(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
preOrder(root, res);
return res;
}
public void preOrder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
res.add(root.val);
preOrder(root.left, res);
preOrder(root.right, res);
}
}
//栈
//1、初始化栈,并将根节点入栈;
//2、当栈不为空时:
// 弹出栈顶元素 node,并将值添加到结果中;
// 如果 node 的右子树非空,将右子树入栈;
// 如果 node 的左子树非空,将左子树入栈;
//由于栈是“先进后出”的顺序,所以入栈时先将右子树入栈,这样使得前序遍历结果为 “根->左->右”的顺序。
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if (root == null) {
return res;
}
Deque<TreeNode> stack = new LinkedList<TreeNode>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
res.add(node.val);
if (node.right != null) {
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
return res;
}
}
二、二叉树的中序遍历
//递归
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
inorder(root, res);
return res;
}
public void inorder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
inorder(root.left, res);
res.add(root.val);
inorder(root.right, res);
}
}
//迭代
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
Deque<TreeNode> stk = new LinkedList<TreeNode>();
while (root != null || !stk.isEmpty()) {
while (root != null) {
stk.push(root);
root = root.left;
}
root = stk.pop();
res.add(root.val);
root = root.right;
}
return res;
}
}
//给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
public TreeNode helper(int[] nums, int left, int right) {
if (left > right) {
return null;
}
// 总是选择中间位置左边的数字作为根节点
int mid = (left + right) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, left, mid - 1);
root.right = helper(nums, mid + 1, right);
return root;
}
}
验证二叉搜索树
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
class Solution {
public boolean isValidBST(TreeNode root) {
if(root == null){
return false;
}
Deque<TreeNode> stack =new LinkedList<>();
TreeNode pre =null;
while(root != null || !stack.isEmpty()){
while(root != null){
stack.push(root);
root=root.left;
}
root = stack.pop();
//如果当前遍历到的节点比前一个小,说明不是二叉搜索树
if(pre != null && root.val <= pre.val){
return false;
}
pre = root;
root=root.right;
}
return true;
}
}
三、二叉树的后序遍历
//递归
class Solution {
public List<Integer> postOrderRecur(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
postOrder(root, res);
return res;
}
public void postOrder(TreeNode root, List<Integer> res) {
if (root == null) {
return;
}
postOrder(root.left, res);
postOrder(root.right, res);
res.add(root.val);
}
}
//栈
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> res = new LinkedList<>();
if(root == null){
return res;
}
Deque<TreeNode> stack1 = new LinkedList<TreeNode>();
stack1.push(root);
while(!stack1.isEmpty()){
TreeNode node = stack1.pop();
res.addFirst(node.val);
if(node.left!= null){
stack1.push(node.left);
}
if(node.right != null){
stack1.push(node.right);
}
}
return res;
}
}
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。
1、数组的最后一个元素即为根节点
2、划分左右子树
- 遍历后序遍历的 [i,j] 区间元素,寻找第一个大于根节点的节点,索引记为m。此时[i,m-1]为左子树区间,[m,j-1]为右子树区间,根节点索引为j。
3、判断是否为二叉搜索树
- 左子树区间所有节点都小于根节点(pstorder[j])
- 右子树区间内的所有节点都大于根节点(pstorder[j])
- 当遇到≤postorder[j] 的节点则跳出;则可通过p=j 判断是否为二叉搜索树。
class Solution {
public boolean verifyPostorder(int[] postorder) {
return recure(postorder,0,postorder.length-1);
}
boolean recure(int[] postorder,int i, int j){
if(i>=j){
return true;
}
int p=i;
//寻找第一个大于根节点的值,postorder[j]为根节点
//左子树全部小于根节点
while(postorder[p] < postorder[j]){
p++;
}
//此时p指向右子树的第一个节点
int m=p;
//[i,m-1],[m,j]分别为左右子树区间
//验证右子树的正确性,右子树的值全部大于根节点
while(postorder[p] > postorder[j]){
p++;
}
//此时p指向根节点
return p==j && recure(postorder,i,m-1) && recure(postorder,m,j-1);
}
}
四、二叉树的层序遍历(广度优先搜索)
// 二叉树的层序遍历
//1、首先根元素入队
//2、当队列不为空的时候
// 求当前队列的长度si
// 依次从队列中取si个元素进行拓展,然后进入下一次迭代
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<List<Integer>>();
if (root == null) {
return ret;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
List<Integer> level = new ArrayList<Integer>();
int currentLevelSize = queue.size();
while(currentLevelSize >0){
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
currentLevelSize--;
}
ret.add(level);
}
return ret;
}
}
//给你一个二叉树,请你返回其按 层序遍历 得到的节点值。(即逐层地,从左到右访问所有节点)。
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
Queue<TreeNode> queue = new ArrayDeque<>();
if (root != null) {
queue.add(root);
}
while (!queue.isEmpty()) {
int n = queue.size();
List<Integer> level = new ArrayList<>();
for (int i = 0; i < n; i++) {
TreeNode node = queue.poll();
level.add(node.val);
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
res.add(level);
}
return res;
}
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> ans = new ArrayList<>();
if(root == null) return res;
Deque<TreeNode> queue = new LinkedList<>();
TreeNode cur = root;
queue.add(cur) ;
TreeNode curEnd = root;
TreeNode nextEnd = null;
while(!queue.isEmpty()){
cur = queue.poll();
ans.add(cur.val);
if(cur.left != null){
queue.add(cur.left);
nextEnd = cur.left;
}
if(cur.right != null){
queue.add(cur.right);
nextEnd = cur.right;
}
if(cur == curEnd){
res.add(new ArrayList<>(ans));
ans.clear();
curEnd = nextEnd;
}
}
return res;
}
}
二叉树的最大深度(广度优先搜索)
//广度优先搜索
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int count = 0;
while(!queue.isEmpty()){
int size =queue .size();
while(size > 0){
TreeNode node = queue.poll();
if(node.left!= null){
queue.offer(node.left);
}
if(node.right!= null){
queue.offer(node.right);
}
size--;
}
count++;
}
return count;
}
}
//递归
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}else{
int leftDepth =maxDepth(root.left);
int rightDepth=maxDepth(root.right);
return Math.max(leftDepth,rightDepth)+1;
}
}
}
剑指Offer55-II.平衡二叉树
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
class Solution {
public boolean isBalanced(TreeNode root) {
if (root == null) return true;
return Math.abs(depth(root.left) - depth(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
}
private int depth(TreeNode root) {
if (root == null) return 0;
return Math.max(depth(root.left), depth(root.right)) + 1;
}
}
剑指Offer32-III.从上到下打印二叉树III
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
双端队列
LinkedList<Integer> tmp = new LinkedList<>();
tmp.addLast(node.val)
tmp.addFirst(node.val)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new LinkedList<>();
if(root == null){
return res;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
LinkedList<Integer> list = new LinkedList<>();
int size =queue.size();
while(size > 0){
TreeNode node = queue.poll();
if(res.size() % 2 ==0){
list.addLast(node.val);
}else{
list.addFirst(node.val);
}
if(node.left!= null){
queue.add(node.left);
}
if(node.right!= null){
queue.add(node.right);
}
size--;
}
res.add(list);
}
return res;
}
}
在这里插入代码片
五、 重建二叉树
后续遍历,重建二叉树
给定一个搜索二叉树的后续遍历数组,重建二叉树,返回新建的头节点
public static TreeNode process(int[] posArr, int L, int R) {
//头节点为后序遍历的最后一个节点
TreeNode head = new TreeNode(posArr[R]);
//[L,R-1]
int M = -1;
//搜索二叉树的左子树<根节点<右子树
for (int i = L; i < R; i++) {
if (posArr[i] < posArr[R]) {//搜索二叉树的左子树小于根节点,M指向左子树的最后一个节点
M = i;
}
}
if (M == -1) {//左边的数全部大于头节点,只要右子树
head.right = process(posArr, L, R - 1);
} else if (M == R - 1) {//左边的数全部小于头节点,只有左子树
head.left = process(posArr, L, R - 1);
} else {//否则,左右子树都有
head.left = process(posArr, L, M);
head.right = process(posArr, M + 1, R - 1);
}
return head;
}
剑指Offer07.重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
只要我们在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目
对于哈希映射中的每个键值对,键表示一个元素(节点的值),值表示其在中序遍历中的出现位置。在构造二叉树的过程之前,我们可以对中序遍历的列表进行一遍扫描,就可以构造出这个哈希映射。在此后构造二叉树的过程中,我们就只需要O(1) 的时间对根节点进行定位了。
class Solution {
Map<Integer,Integer> indexMap = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n =inorder.length;
for(int i=0; i<n; i++){
indexMap.put(inorder[i],i);
}
return myBuildTree(preorder,inorder,0,n-1,0,n-1);
}
public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return null;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = indexMap.get(preorder[preorder_root]);
// 先把根节点建立出来
TreeNode root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree,inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
}
六、other
剑指Offer68-I.二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
1、找到从根节点到目标节点的路径
- 我们从根节点开始遍历;
- 如果当前节点就是 p,那么成功地找到了节点;
- 如果当前节点的值大于 p 的值,说明 p 应该在当前节点的左子树,因此将当前节点移动到它的左子节点;
- 如果当前节点的值小于 pp 的值,说明 pp 应该在当前节点的右子树,因此将当前节点移动到它的右子节点。
2、当我们分别得到了从根节点到 p 和 q的路径之后,我们就可以很方便地找到它们的最近公共祖先了。显然,p和q的最近公共祖先就是从根节点到它们路径上的「分岔点」,也就是最后一个相同的节点。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
List<TreeNode> p_path=getPath(root,p);
List<TreeNode> q_path=getPath(root,q);
TreeNode ans = null;
for(int i=0; i<p_path.size() && i<q_path.size(); i++){
if(p_path.get(i) == q_path.get(i)){
ans=p_path.get(i);
}else{
break;
}
}
return ans;
}
public List<TreeNode> getPath(TreeNode root, TreeNode target){
List<TreeNode> list = new ArrayList<>();
while(root != target){
list.add(root);
if(target.val<root.val){
root=root.left;
}else{
root=root.right;
}
}
list.add(root);
return list;
}
}
一次遍历
- 我们从根节点开始遍历;
- 如果当前节点的值大于 p和 q 的值,说明 p 和 q应该在当前节点的左子树,因此将当前节点移动到它的左子节点;
- 如果当前节点的值小于 p 和 q的值,说明 p和 q应该在当前节点的右子树,因此将当前节点移动到它的右子节点;
- 如果当前节点的值不满足上述两条要求,那么说明当前节点就是「分岔点」。此时,p和 q要么在当前节点的不同的子树中,要么其中一个就是当前节点。
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
TreeNode ancestor = root;
while (true) {
if (p.val < ancestor.val && q.val < ancestor.val) {
ancestor = ancestor.left;
} else if (p.val > ancestor.val && q.val > ancestor.val) {
ancestor = ancestor.right;
} else {
break;
}
}
return ancestor;
}
}
剑指Offer27.二叉树的镜像
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
递归
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null){
return null;
}
TreeNode leftRoot = mirrorTree(root.right);
TreeNode rightRoot = mirrorTree(root.left);
root.left = leftRoot;
root.right = rightRoot;
return root;
}
}
辅助栈
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null){
return null;
}
Deque<TreeNode> stack =new LinkedList<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node =stack.pop();
TreeNode temp=node.left;
node.left=node.right;
node.right=temp;
if(node.left != null){
stack.push(node.left);
}
if(node.right != null){
stack.push(node.right);
}
}
return root;
}
}
剑指Offer28.对称的二叉树
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
终止条件:
- 当 L和 R 同时越过叶节点: 此树从顶至底的节点都对称,因此返回 true ;
- 当 L或 R 中只有一个越过叶节点: 此树不对称,因此返回 false ;
- 当节点 LL值 != 节点 R 值: 此树不对称,因此返回 false;
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return recur(root.left,root.right);
}
boolean recur(TreeNode L,TreeNode R){
if(L==null && R==null){
return true;
}
if(L == null || R == null || L.val != R.val){
return false;
}
return recur(L.left,R.right) && recur(L.right,R.left);
}
}
199.二叉树的右视图
给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
/**
* Definition for a binary tree node.
* 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 Solution {
public List<Integer> rightSideView(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Deque<TreeNode> queue = new LinkedList<>();
TreeNode cur = root;
queue.add(cur) ;
TreeNode curEnd = root;
TreeNode nextEnd = null;
while(!queue.isEmpty()){
cur = queue.poll();
if(cur.left != null){
queue.add(cur.left);
nextEnd = cur.left;
}
if(cur.right != null){
queue.add(cur.right);
nextEnd = cur.right;
}
if(cur == curEnd){
res.add(cur.val);
curEnd = nextEnd;
}
}
return res;
}
}
前中后遍历
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* }
*/
public class Solution {
/**
*
* @param root TreeNode类 the root of binary tree
* @return int整型二维数组
*/
private ArrayList<Integer> list = new ArrayList<>();
public int[][] threeOrders (TreeNode root) {
// write code here
if(root==null) {
return new int[3][0];
}
preOrder(root);
inOrder(root);
afterOrder(root);
int[][] res = new int[3][list.size()/3];
int index = 0;
for(int i=0;i<3;i++) {
for(int j=0;j<list.size()/3;j++) {
res[i][j] = list.get(index++);
}
}
return res;
}
public void preOrder(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode curr = stack.pop();
list.add(curr.val);
if (curr.right != null) {
stack.push(curr.right);
}
if (curr.left != null) {
stack.push(curr.left);
}
}
}
public void inOrder(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
while(root != null || !stack.isEmpty()){
while(root != null){
stack.push(root);
root=root.left;
}
root = stack.pop();
list.add(root.val);
root=root.right;
}
}
public void afterOrder(TreeNode root) {
Deque<TreeNode> stack1 = new LinkedList<>();
Deque<TreeNode> stack2 = new LinkedList<>();
stack1.push(root);
while(!stack1.isEmpty()){
TreeNode node = stack1.pop();
stack2.push(node);
if(node.left != null){
stack1.push(node.left);
}
if(node.right!= null){
stack1.push(node.right);
}
}
while(!stack2.isEmpty()){
list.add(stack2.pop().val);
}
}
}
剑指Offer54.二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第k大的节点。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int res, k;
public int kthLargest(TreeNode root, int k) {
this.k=k;
dfs(root);
return res;
}
void dfs(TreeNode root){
if(root == null){
return;
}
dfs(root.right);
if(k == 0) return;
k--;
if(k==0){
res=(root.val);
}
dfs(root.left);
}
}