个人LeetCode刷题总结。
1.深度优先遍历
2..分层遍历二叉树或广度优先遍历
3.求二叉树中节点的个数
4.求二叉树的深度
5.将二叉查找树变为有序的双向链表
6.求二叉树第K层的节点个数
7.求二叉树叶子节点的个数
8.判断两颗二叉树是否同构
9.判断二叉树是不是平衡二叉树(AVL树)
10.求二叉树的镜像
11.求二叉树中节点的最大距离
12.由中序遍历和后序遍历重建二叉树
13.判断一棵树是不是完全二叉树
1.深度优先遍历
深度优先遍历分为前序遍历,中序遍历,后序遍历。
1.前序遍历(先访问根节点,再访问左子树,最后访问右子树)
1.前序遍历递归解法:
1.先判断根节点是否是否为空,为空直接返回
2.如果不为空,先访问根节点的值,再访问左子树,最后访问右子树
public void preOrderedTraverse(Node<T> node){
if(node == null){
return;
}
System.out.println(node.element);//这里只是简单地打印一下值
preOrderedTraverse(node.left);
preOrderedTraverse(node.right);
}
2.前序遍历非递归解法:
/**
* 先序遍历的非递归解法:
* 总的就是利用栈结构先进后出特点
* 1.判断根节点是否为空,为空直接返回
* 2.初始化之后将根节点加入栈
* 3.只要栈不为空就进入循环,先poll出栈顶节点并访问,然后先push进右节点(如果不为空),
* 在push左节点(如果不为空的话)
* @param node
*/
public void preOrderTraverseByStack(Node<T> root){
if(root == null){
return;
}
Stack<Node<T>> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
Node<T> cur = stack.pop();
System.out.println(cur.element);//这里访问就是简单的打印一下
if(cur.right != null){
stack.push(cur.right);
}
if(cur.left != null){
stack.push(cur.left);
}
}
}
2.中序遍历(先访问左子树,再访问根节点,最后访问右子树)
1.中序遍历递归解法
1.先判断根节点是否是否为空,为空直接返回
2.如果不为空,先访问左子树,再访问根节点的值,最后访问右子树
public void midOrderedTraverse(Node<T> node){
if(node == null){
return;
}
midOrderedTraverse(node.left);
System.out.println(node.element);
midOrderedTraverse(node.right);
}
2.中序遍历非递归解法
/**
* 中序遍历非递归解法
* 使用栈结构先进后出,后进先出的特点
* 1.如果根节点为空,就返回
* 2.创建一个指针节点cur,一开始指向root
*
* 3.只要栈不为空或者指针节点cur就进入栈
* 4.寻找当前节点cur做为根节点的最左节点,并把一路上遇到的左节点都push入栈
* 5.通过步骤4,将最左节点放到了栈顶,poll并访问这个节点
* 6.访问最左节点的右子树,即将cur节点重新指向为步骤5返回节点的右节点,同样的也要将右子树上的根节点和左节点们加入栈,如果他们存在的话
*
* 总的来说,就是将从根节点开始陆续把左节点们放到队列中,然后访问第一个左节点,然后访问该节点的右子树,并把该节点及左节点们加入栈
* @param root
*/
public void midOrderTraveseByStack(Node<T> root){
if(root == null){
return;
}
Node<T> cur = root;
Stack<Node<T>> stack = new Stack<>();
while(cur!= null || !stack.isEmpty()){
while(cur != null){//把当前节点和一路上的左节点们加入队列
stack.push(cur);
cur = cur.left;
}
Node<T> node = stack.pop();//返回当前最左节点
System.out.println(node.element);//这里就用打印表示访问的逻辑了。。
cur = node.right; //访问当前最左节点的右子树
}
}
3.后序遍历(先访问左子树,再访问右子树,最后访问根节点)
1.后序遍历递归解法
1.先判断根节点是否是否为空,为空直接返回
2.如果不为空,先访问左子树,再访问右子树,最后访问根节点的值,
public void afterOrderedTraverse(Node<T> node){
if(node == null){
return;
}
afterOrderedTraverse(node.left);
afterOrderedTraverse(node.right);
System.out.println(node.element);
}
3.后序遍历非递归版
/**
* 非递归后序遍历难点在于,需要使用一个节点记录上一次访问的节点是位于左子树还是右子树
* 1.如果上一次访问的节点是左子树,则需先跳过根节点,先访问右子树
* 2.如果上一次访问的节点是右子树,则可以访问根节点
*
* 其他逻辑与中序遍历的非递归基本一致
*
* @param root
*/
public void afterOrderTraverseByStack(Node<T> root){
if(root == null){
return ;
}
Stack<Node<T>> stack = new Stack<>();
Node<T> cur = root;
Node<T> lastVisitNode = null;
while(cur != null || !stack.isEmpty()){
while(cur != null){ //将根节点和左节点们加入栈中
stack.push(cur);
cur = cur.left;
}
Node<T> node = stack.pop();//返回当前的最左节点
//如果不存在右子树,或者已经访问过右子树,就可以直接访问当前节点;否则要先访问右子树
if(node.right == null || node.right == lastVisitNode){
System.out.println(node.element);
lastVisitNode = node; //更新上一次访问的节点
cur = null;
}
else{
//再把该节点返回栈中
stack.push(node);
//继续访问右子树
cur = node.right;
}
}
}
2.分层遍历二叉树也叫广度优先遍历(从左到右,从上到下访问树中的节点)
解法:使用队列来解决
使用队列来保存树的节点,首先将根节点(如果不为空的话,为空就直接返回了)加入队列来初始化队列。
然后只有队列不为空就进入循环,在每一次循环中,取出并访问队首节点,
然后将该节点的左,右子节点依次加入队列尾部(如果存在的话)
public void traverseTreeInLevel(Node<T> root){
if(root != null){
return;
}
Queue<Node<T>> queue = new LinkedList<>();
queue.add(root);
while(!queue.isEmpty()){
Node<T> node = queue.poll();
System.out.println(node.element);
if(node.left != null){
queue.add(node.left);
}
if(node.right != null){
queue.add(node.right);
}
}
}
3.求二叉树中节点的个数
递归求法:
1.先判断根节点是否为空,为空返回0;
2.如果不为空,当前树中节点的个数 = 1+左子树个数+右子树个数
public int getAllNodesNumbers(Node<T> root){
if(root == null){
return 0;
}
int leftChildTreeNumbers = getAllNodesNumbers(root.left);
int rightChildTreeNumbers = getAllNodesNumbers(root.right);
return leftChildTreeNumbers + rightChildTreeNumbers+1;
}
4.求二叉树的深度
递归解法:
1.先判断根节点是否为空,如果为空则返回0;
2.如果根节点不为空,则树的深度 = Math.max(左子树的深度,右子树的深度)+1
public int getTreeDepth(Node<T> root){
if(root == null){
return 0;
}
else{
int leftChildTreeDepth = getTreeDepth(root.left);
int rightChildTreeDepth = getTreeDepth(root.right);
return Math.max(leftChildTreeDepth, rightChildTreeDepth)+1;
}
}
5.将二叉查找树变为有序的双向链表
排序二叉树(英语:sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
任意节点的左、右子树也分别为二叉查找树;
没有键值相等的节点。
详见https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%85%83%E6%90%9C%E5%B0%8B%E6%A8%B9
/**
* 使用非递归的中序遍历(借助栈来实现的),原因中序遍历二叉查找树刚好就是有序的
* 把每一次遍历的节点与前一个节点连接起来
*
* @param root
* @return
*/
public Node<T> convertBSTIntoList1(Node<T> root){
if(root == null){
return null;
}
Node<T> cur = root;
Node<T> pre = root;
boolean isFirst = true;
Stack<Node<T>> stack = new Stack<>();
while(cur!= null || !stack.isEmpty()){
while(cur!=null){//将当前节点及其左节点们加入栈
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
if(isFirst){
root = cur;//让root指向中序遍历第一个节点,即最左节点
pre = cur;
isFirst = false;
}
else{
pre.right = cur;//这两步是为了连接当前节点和前一个节点
cur.left = pre;
pre = cur;
}
cur = cur.right;//转移到当前节点的右子树
}
return root;
}
/*
* 递归解法:
* 1.将左子树构造成双链表,并返回链表头节点。
* 2.定位至左子树双链表最后一个节点。
* 3.如果左子树链表不为空的话,将当前root追加到左子树链表。
* 4.将右子树构造成双链表,并返回链表头节点。
* 5.如果右子树链表不为空的话,将该链表追加到root节点之后。
* 6.根据左子树链表是否为空确定返回的节点。
*/
public Node<T> convertBSTIntoList2(Node<T> root){
if(root == null){
return null;
}
if(root.left == null && root.right == null){
return root;
}
//将左子树转换成双向链表并返回表头节点
Node<T> left = convertBSTIntoList2(root.left);
Node<T> point =left;
//找到左子树的最后一个节点
while(point !=null && point.right != null){
point = point.right;
}
if(left != null){ //如果左子树不为空,将左子树的最后一个节点与root相连接
point.right = root;
root.left = point;
}
//将右子树转换成双向链表并返回表头节点
Node<T> right = convertBSTIntoList2(root.right);
if(right != null){
//将root与右子树装换成的链表的表头节点相连
root.right = right;
right.left = root;
}
return left != null ? left : root;
}
/*
* 递归对方法二的改进,没了第三步,换成使用内部属性来记录左子树的最后一个节点
* 递归解法:
* 1.将左子树构造成双链表,并返回链表头节点。
* 2.定位至左子树双链表最后一个节点。
*
* 4.将右子树构造成双链表,并返回链表头节点。
* 5.如果右子树链表不为空的话,将该链表追加到root节点之后。
* 6.根据左子树链表是否为空确定返回的节点。
*/
protected Node<T> lastLeft;
public Node<T> convertBSTIntoList3(Node<T> root){
if(root == null){
return null;
}
if(root.left == null && root.right == null){
lastLeft = root;
return root;
}
//将左子树转换成双向链表并返回表头节点
Node<T> left = convertBSTIntoList3(root.left);
if(left != null){ //如果左子树不为空,将左子树的最后一个节点与root相连接
lastLeft.right = root;
root.left = lastLeft;
}
//将右子树转换成双向链表并返回表头节点
Node<T> right = convertBSTIntoList3(root.right);
if(right != null){
//将root与右子树装换成的链表的表头节点相连
root.right = right;
right.left = root;
}
return left != null ? left : root;
}
6.求二叉树第K层的节点个数
递归解法:
首先得明白这个道理,当K>1时,根节点的第K层相当于根节点左子树(右子树)的第K-1层;
也就说,根节点的第K层节点个数=左子树的第k-1层的个数+右子树的第k-1层的个数
解法:
当二叉树根节点为空或者k<1时,直接返回0
当 k=1,直接返回1
如果前两个条件没有返回,则返回左子树的第k-1层的个数+右子树的第k-1层的个数
public int getKthLevelNodesNumber(Node<T> root, int k){
if(root == null || k < 1){
return 0;
}
if(k == 1){
return 1;
}
return getKthLevelNodesNumber(root.left, k-1)+getKthLevelNodesNumber(root.right, k-1);
}
7.求二叉树叶子节点的个数
递归解法:
/**
* 求叶子节点的个数
* 叶子节点定义:左节点和右节点同时为空
* 解法:
* 划归为左子树和右子树的叶子节点之和的问题
* 如果根节点为空则返回0;
* 如果根节点的左右节点同时为null则返回1
* 如果前两个条件没有返回,则返回根节点左子树的叶子数+根节点右子树的叶子数
* @param args
*/
public int getLeafNodeNumber(Node<T> root){
if(root == null){
return 0;
}
if(root.left == null && root.right == null){
return 1;
}
return getLeafNodeNumber(root.left) + getLeafNodeNumber(root.right);
}
8.判断两颗二叉树是否同构
同构就是二叉树结构相同,不需要考虑数值
/*判断两颗二叉树是否同构
同构就是二叉树结构相同,不需要考虑数值
递归解法:
1.如果两颗树都为空,那么就返回真
2.如果一棵树为空,另一颗不为空,那么就返回假
3.如果前两个条件没有返回,则比较两个树的左子树和右子树,对结果取And返回
*/
public boolean isBTStrutureSame(Node<T> tree1, Node<T> tree2){
if( tree1 == null && tree2 == null){
return true;
}
if((tree1 == null && tree2 != null) || (tree2 == null && tree1 != null) ){
return false;
}
return isBTStrutureSame(tree1.left, tree2.left) && isBTStrutureSame(tree1.right, tree2.right);
}
9.判断二叉树是不是平衡二叉树(AVL树)
平衡二叉树(Balanced Binary Tree)又被称为AVL树,
且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
/**
* 递归解法
* 如果根节点为空,返回true
* 如果根节点不为空,则获取左子树的高度,获取右子树的高度,相减去绝对值,如果大于1,返回false
* 如果两个条件没有返回,则判断根节点的左右子树是否为ALV,对判断结果取And
* @param root
* @return
*/
public boolean isALV(Node<T> root){
if(root == null){
return true;
}
int leftChildTreeHeight = getTreeDepth(root.left);
int rightChildTreeHeight = getTreeDepth(root.right);
boolean result = Math.abs(leftChildTreeHeight - rightChildTreeHeight) > 1;
if(result){
return false;
}
return isALV(root.left) && isALV(root.right);
}
10.求二叉树的镜像
/**
* 求二叉树的镜像
* 思路:对二叉树的每一个节点,实现左右节点的交换
* 递归解法:
* 1.如果当前根节点为空,或者左右子节点同时为空则返回
* 2.如果不满足上述条件,则交换左右子节点
* 3.将当前根节点的左右子节点作为新的根节点,继续交换
*
* @param args
*/
public void mirrorBT(Node<T> root){
if(root == null || (root.left == null && root.right == null)){
return;
}
Node<T> leftRef = root.left;
root.left = root.right;
root.right = leftRef;
mirrorBT(root.left);
mirrorBT(root.right);
}
11. 求二叉树中节点的最大距离
1.效率低的方法,但好理解
即二叉树中相距最远的两个节点之间的距离。
/**
*这个两个节点的最大距离分为以下三种情况
*两个节点在当前根节点的左右子树上,即经过根节点,为左子树的深度+右子树的深度
*两个节点都在当前根节点的左子树上,即把当前根节点的左节点作为新的根节点,递归求这个两个节点的最大距离
*两个节点都在当前根节点的右子树上,即把当前根节点的右节点作为新的根节点,递归求这个两个节点的最大距离
*最后取三者的最大值
* @param root
* @return
*/
public int getMaxDistance(Node<T> root){
if(root == null){
return 0;
}
int depthOfleftTree = getTreeDepth(root.left);//默认就加了根节点root.left,所以刚好等于到root的距离
int depthOfRightTree = getTreeDepth(root.right);//同上
int leftTreeMaxDistance = getMaxDistance(root.left);
int rightTreeMaxDistance = getMaxDistance(root.right);
return Math.max(Math.max(leftTreeMaxDistance,rightTreeMaxDistance), depthOfleftTree+depthOfRightTree);
}
2.使用成员变量与递归
public class Solution {
int max = 0;
public int diameterOfBinaryTree(TreeNode root) {
maxDepth(root);
return max;
}
private int maxDepth(TreeNode root) {
if (root == null) return 0;
int left = maxDepth(root.left);
int right = maxDepth(root.right);
max = Math.max(max, left + right); //每次递归都检查是否需要更新max,
return Math.max(left, right) + 1;
}
}
12.由中序遍历和后序遍历重建二叉树
leetcode原题
https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/#/description
解题思路:
1.主要分治法的使用
2.从后序遍历的结果中,取出最后一个作为根节点的值
然后找到该值在中序遍历的位置rootIndex,
如果rootIndex>0,即该节点不是第一个,则表明存在左子树。
取出inOrder和POSTOrder左子树的部分,递归调用
如果rootIndex<inOrder.size()-1,即该节点不是最后一个,则表明存在右子树
取出inOrder和POSTOrder右子树的部分,递归调用
public class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(inorder == null || inorder.length == 0){
return null;
}
TreeNode root = new TreeNode(0);
ArrayList<Integer> inorderArrayList = new ArrayList<>();
ArrayList<Integer> postorderArrayList = new ArrayList<>();
for(int i = 0 ;i<inorder.length;i++){
inorderArrayList.add(inorder[i]);
postorderArrayList.add(postorder[i]);
}
build(root, inorderArrayList,postorderArrayList);
return root;
}
private void build(TreeNode root, List<Integer> inOrder, List<Integer> postOrder){
if(postOrder.size()<1){
return;
}
root.val = postOrder.get(postOrder.size()-1);
int rootIndex = inOrder.indexOf(root.val);
if(rootIndex > 0){//表明存在左子树
root.left = new TreeNode(0);
//取出inOrder和POSTOrder左子树的部分,递归调用
ArrayList<Integer> newInOrder = new ArrayList<>(inOrder.subList(0, rootIndex));
ArrayList<Integer> newPostOrder = new ArrayList<>(postOrder.subList(0, rootIndex));
build(root.left, newInOrder, newPostOrder);
}
if(rootIndex<inOrder.size()-1){//表明存在右子树
root.right = new TreeNode(0);
//取出inOrder和POSTOrder右子树的部分,递归调用
ArrayList<Integer> newInOrder = new ArrayList<>(inOrder.subList(rootIndex+1, postOrder.size()));
ArrayList<Integer> newPostOrder = new ArrayList<>(postOrder.subList(rootIndex, postOrder.size()-1));
build(root.right, newInOrder, newPostOrder);
}
}
}
13.判断一棵树是不是完全二叉树
若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全
二叉树。
算法:按层次遍历二叉树(从左到右,从上到下),遇到某个节点。
1.如果他的左子树为空,那么它的右子树也必须为空,并且排在该节点之后的所有节点
左右节点都为空
2.如过他的左子树不为空,右子树为空,那么排在该节点之后的所有节点
左右节点都为空
/*判断一棵树是不是完全二叉树
定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全
二叉树。
算法:按层次遍历二叉树(从左到右,从上到下),遇到某个节点。
1.如果他的左子树为空,那么它的右子树也必须为空,并且排在该节点之后的所有节点左右节点都为空
2.如过他的左子树不为空,右子树为空,那么排在该节点之后的所有节点左右节点都为空
*/
public boolean isComopleteBT(Node<T> root){
Queue<Node<T>> queue = new LinkedList<>();
queue.add(root);
boolean flag = false; //true表明之后的节点必须都为叶子节点,即左右节点为空
while(!queue.isEmpty()){
Node<T> cur = queue.poll();
if(!flag){
if(cur.left!= null && cur.right !=null){
queue.add(cur.left);
queue.add(cur.right);
}
if(cur.left == null && cur.right!=null){
return false;
}
if(cur.left == null && cur.right == null){
flag = true;
}
if(cur.left != null && cur.right == null){
flag = true;
queue.add(cur.left);
}
}
else{//必须都是叶子节点
if(cur.left != null || cur.right!=null){
return false;
}
}
}
return true;
}