树一些必须知道的简单基础
- 树是一种非线性的数据结构,递归定义
- 节点的度:一棵树含有的子树的个数称为节点的度
如图A节点的度为6,B的度为0. - 树的度:一棵树的最大节点的度称为树的度,如上图:树的度为6.
- 叶子节点:度为0的节点称为叶子节点
- 双亲节点(父节点):若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点
- 根节点:一棵树中,没有双亲结点的结点;如上图:A
- 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
- 树的高度:树中节点的最大层次; 如上图:树的高度为4
树的应用
二叉树的种类
在我们解题过程中二叉树有两种主要的形式:满二叉树和完全二叉树
-
满二叉树
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树 -
完全二叉树
什么是完全二叉树?
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^h -1 个节点。
之前的文章里写过的优先级队列就是一个堆,堆就是一个完全二叉树,同时保证父子节点的顺序关系。
搜索二叉树
前面介绍的树,都没有数值的,而二叉搜索树是有数值的了,二叉搜索树是一个有序树
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
- 它的左、右子树也分别为二叉排序树
平衡二叉搜索树(AVL)
性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
前面提到了二叉搜索树,我们知道,二叉搜索树的特性便于我们进行查找插入删除等一系列操作,其时间复杂度为O(logn),但是,如果遇见最差的情况,比如以下这棵树
这棵树,其实已经退化成链表了,但它仍是一棵二叉搜索树,我们按照逐次增大,如5、6、7、8的顺序构造一棵二叉搜索树,则形如上图。插入的时间复杂度就变成了O(n),导致这种糟糕的情况原因是因为这棵树极其不平衡,右树的重量远大于左树,因此我们提出了叫平衡二叉搜索树的结构,又称之为AVL树
大家使用自己熟悉的编程语言写算法,一定要知道常用的容器底层都是如何实现的,最基本的就是map、set等等,这样有利于我们分析自己代码的性能
二叉树的存储方式
二叉树的存储结构分为:顺序存储和类似于链表的链式存储。
二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉表示方式,具体如下:
// 孩子表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}
顺序存储的方式如图:
如果父节点的数组下表是i,那么它的左孩子就是i * 2 + 1,右孩子就是 i * 2 + 2
二叉树的遍历方式
二叉树主要有两种遍历方式:
1: 深度优先遍历:先往深走,遇到叶子节点再往回走。
2:广度优先遍历:一层一层的去遍历。
关于前中后序遍历我在这里进行了总结:二叉树的前中后序遍历,秒懂
上一篇文章我写了栈和队列的一些相关内容,而栈其实就是递归的一种实现结构,也就说前中后序遍历的逻辑其实都是可以借助栈使用非递归的方式来实现的。
而广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树。
//二叉树节点定义
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;
}
}
二叉树的递归遍历
- 二叉树的前序遍历(leetcode:144.二叉树的前序遍历)
//定义二叉树的头节点
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> preorderTraversal(TreeNode root) {
ArrayList<Integer> result=new ArrayList<>();
preOrder(root,result);
return result;
}
void preOrder(TreeNode root,ArrayList result){
if(root==null){
return;
}
result.add(root.val);
preOrder(root.left,result);
preOrder(root.right,result);
}
}
- 二叉树的中序遍历(leetcode:94.二叉树的中序遍历)
/**
* 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> inorderTraversal(TreeNode root) {
ArrayList<Integer> result=new ArrayList<>();
inOrder(root,result);
return result;
}
void inOrder(TreeNode root,ArrayList result){
if(root==null){
return;
}
inOrder(root.left,result);
result.add(root.val);//注意这一句
inOrder(root.right,result);
}
}
- 二叉树的后序遍历(145.二叉树的后序遍历)
/**
* 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> postorderTraversal(TreeNode root) {
ArrayList<Integer> result=new ArrayList<>();
postOrder(root,result);
return result;
}
void postOrder(TreeNode root,ArrayList result){
if(root==null){
return;
}
postOrder(root.left,result);
postOrder(root.right,result);
result.add(root.val);
}
}