二叉树理论基础:
满二叉树:
树的结点为2的k次-1个
完全二叉树:
最后一层可以不铺满,但是有右必须有左
二叉搜索树:
左节点都小于根节点,右结点都大于根节点
平衡二叉搜索树:
左子树和右子树的高度差不能超过1
存储方式:线性存储/链式存储
树的遍历(递归/迭代):
递归三部曲:
1.确定递归函数的参数和返回值
2.确定终止条件
一、前中后遍历(递归法)
根据前中后遍历的特点。中左右/左中右/左右中
拿前序遍历来说:
首先我们要输出该结点的值,然后去递归调用左结点和右结点
代码:
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
traversal(root, list);
return list;
}
public void traversal(TreeNode root, List<Integer> list) {
if(root==null)return;
list.add(root.val);
traversal(root.left,list);
traversal(root.right,list);
}
二、前中后遍历(非递归法)
前/后序遍历处理的思路都是一样的(因为前序遍历,经过那一个结点的时候,就会将节点值输出,后序遍历可以由前序遍历通过一系列的转换得到,但是中序遍历,每次都要遍历到最左边,然后菜输出节点值。)
前序遍历:利用栈,将根节点push进去,然后弹出根节点(完成了中),然后将它的右结点、左节点依次入栈。此时在栈顶的元素就是左节点,然后再将左节点pop掉(完成了左),继续将左节点的左右节点入栈。
//定义一个栈存放树节点
Stack<TreeNode> stack=new Stack<>();
//定义一个集合放遍历结果
List<Integer> list=new ArrayList<>();
if(root==null)return list;
stack.push(root);
while(!stack.isEmpty()){
TreeNode node=stack.pop();
list.add(node.val);
if(node.left!=null)stack.push(node.left);
if(node.right!=null)stack.push(node.right);
}
后序遍历:就是在前序遍历的基础上左右节点入栈的顺序改变,然后将结果反转。
中序遍历:
先一路向左判断,如果为空了(左结束了),就弹出栈顶的元素(中结束了),然后看右子节点。
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root==null)return list;
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();
list.add(cur.val);
cur=cur.right;
}
}
return list;
}
三、二叉树的层序遍历
使用Deque<TreeNode>作为数据结构,返回值是List<List<Integer>>;每一个List<Integer>存放的是每一层的数据。
往队列里面放多少个,那么队列的就有多长。输出和输入的顺序是一致的。遵守队列的先进先出特点。在输出上一层元素的循环中,会往队列里面添加(0->2n个元素)。然后等上一层元素都输出完毕之后,重新计算queue的size,然后while(size--)输出第二层的元素
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> lists=new ArrayList<>();
Queue<TreeNode> queue=new ArrayDeque<>();
if(root!=null){
queue.add(root);
}
while(!queue.isEmpty()){
int size=queue.size();
List<Integer> list=new ArrayList<>();
while(size-->0){
TreeNode node=queue.poll();
list.add(node.val);
if(node.left!=null)queue.add(node.left);
if(node.right!=null)queue.add(node.right);
}
lists.add(list);
}
return lists;
}
四、翻转二叉树(曾经有一位应聘者因为不会该问题被淘汰)
使用递归方法:递归三部曲:确立递归函数的返回值和参数、确立终止条件、确立递归逻辑
先将root根节点的左右结点交换,通过swap(TreeNode node)函数交换。然后再递归遍历根节点的左子节点,然后右子结点。这样就是一个先序遍历。后序遍历就是把swap放到最后。
注意:中序遍历(左中右),先调换左子节点的两个结点,然后再调换根节点的左右结点。但是这样左右的顺序就被交换了,之前在左边的子节点跑到了右边。因此第三次还要调换根节点的左子节点。
代码:
public TreeNode mirrorTree(TreeNode root) {
return reverseTree(root);
}
/**
递归三部曲
1.确定递归函数的返回值和参数
2.确定结束条件
3.处理递归逻辑
**/
public TreeNode reverseTree(TreeNode root){
if(root==null)return root;
//递归 翻转左右子节点的结点
reverseTree(root.left);
swap(root);
reverseTree(root.left);
return root;
}
public void swap(TreeNode root){
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
}
五、对称二叉树
我最开始判断是否是对称二叉树是根据:中序遍历 然后从中间向两边走,看两边的元素是否相同,如果相同就说明是对称的。但是没有考虑到有些结点是null的,但是null是无法存在集合中的。
(因此 我想创建一个放字符串的集合 然后把他们的值放进去 如果没有值 就放null 然后比较一下两边的字符串是否相等,还没实现)
因此按照卡尔哥的思想。判断是否是对称树,就要依次对左右结点判断,对相对称的结点判断,会有四种情况:左空 右不空||左不空 右空||左右都空||左右的值不相等。如果左右为空的话就不需要比较剩下的结点了。(这四种情况是起筛选作用的,但是空空的话可以直接返回true)然后剩下最后一种情况,两边值相等,然后就比较子节点。
比较子节点的时候:在按照左左和右右||左右和右左来比较。
代码:
public boolean isSymmetric(TreeNode root) {
return isMirror(root.left,root.right);
}
public boolean isMirror(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 leftFlag=isMirror(left.left,right.right);//左左和右右比较
boolean rightFlag=isMirror(left.right,right.left);//左右和右左比较
return leftFlag&&rightFlag;
}
六、计算二叉树的深度(层序遍历/递归法)
层序遍历(上面有不多做解释了)
public int calculateDepth(TreeNode root) {
//使用层序遍历
//创建一个队列 来存储结点
Queue<TreeNode> queue=new ArrayDeque<>();
//创建lists存放每层的结果
List<List<Integer>> lists=new ArrayList<>();
if(root!=null)queue.add(root);
while(!queue.isEmpty()){
int size=queue.size();
List<Integer> list=new ArrayList<>();
while(size-->0){
TreeNode node=queue.poll();
list.add(node.val);
if(node.left!=null)queue.add(node.left);
if(node.right!=null)queue.add(node.right);
}
lists.add(list);
}
return lists.size();
}
递归法:逻辑:每一个结点的高度就是max(node.left.getHeight(),node.right.getHeight())+1),根据这个逻辑来递归。
递归三部曲:
1.返回值为int(高度) 参数为node
2.终止条件:node==null
3.递归逻辑:
getHeight(TreeNode node){
if(node==null)return 0;
return Math.max(getHeight(node.left),getHeight(node.right))+1;
}
七、计算二叉树的高度(层序遍历/递归)
递归:
注意:计算二叉树的高度时,最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
叶子节点必须是非空的。因此在这里要排除叶子节点是null的情况
递归法:
public int minDepth(TreeNode root) {
return getHeight(root);
}
public int getHeight(TreeNode root) {
if (root == null)
return 0;
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
if (root.left == null) {
return rightHeight + 1;
}
if (root.right == null) {
return leftHeight + 1;
}
return Math.min(leftHeight, rightHeight) + 1;
}
中序遍历:
大致思路:在遍历每一层结点的时候,如果遇到一个结点没有左右子节点,那么从根节点到这个结点的距离就是最小高度。
代码:
public int minDepth(TreeNode root) {
// 层序遍历 如果遇到有结点的左右子节点都为空的话 就返回该层的位置
Queue<TreeNode> queue = new ArrayDeque<>();
if (root == null)
return 0;
List<List<Integer>> lists = new ArrayList<>();
queue.add(root);
int distance = 0;
while (!queue.isEmpty()) {
int size = queue.size();
List<Integer> list = new ArrayList<>();
while (size-- > 0) {
TreeNode node = queue.poll();
list.add(node.val);
boolean flag = isAlone(node);
if (flag) {
return lists.size()+1;
}
if(node.left!=null)queue.add(node.left);
if(node.right!=null)queue.add(node.right);
}
lists.add(list);
}
return 0;
}
public boolean isAlone(TreeNode node) {
if (node.left == null && node.right == null)
return true;
else
return false;
}
八、完全二叉树的节点个数(层序遍历/利用满二叉树的性质)
层序遍历:遍历每一层的元素,然后把元素都加起来,最后返回。
利用满二叉树的性质:
递归三部曲:1.参数为TreeNode node 返回值为int
2.当node==null的时候,返回0;还有一种情况就是当子树为一颗满二叉树的时候,直接返回2<<leftHeight-1;
3.递归逻辑:如果不满足的话,那么就递归函数 return way(root.left)+way(root.right)+1;
然后再在小子树里面找有没有满足满二叉树性质的。
代码:
public int countNodes(TreeNode root) {
if (root == null)
return 0;
TreeNode left = root.left;
TreeNode right = root.right;
int leftHeight=0;
int rightHeight=0;
while(left!=null){
leftHeight++;
left=left.left;
}
while(right!=null){
rightHeight++;
right=right.right;
}
if(leftHeight==rightHeight){
return (2<<leftHeight)-1;
}
return countNodes(root.left)+countNodes(root.right)+1;
}
九、平衡二叉树(递归法/迭代法)
二叉树:任何一个节点的左右节点的高度差值<=1。
递归法:
递归函数的返回值为int,参数为TreeNode root
终止条件:node==null
递归逻辑:
1.当子节点中存在高度插值>1 证明不能是平衡二叉树 直接返回-1。分别对左右子节点讨论
2.当该节点的左右高度差值>1 证明不是平衡二叉树 返回-1
3.都满足,那么直接返回该结点的高度差。return Math.max(leftHeight,rightHeight)+1;