新的篇章,二叉树!!!
二叉树的种类
满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节点的二叉树。
完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。
意思就是,对于完全二叉树来说,每一层都是填满的,如果有没有填满的一层,结点顺序也必须从左向右填充。就像这样。
二叉搜索树:对于二叉搜索树,他是一种有序树。其中他的结点必须满足这些条件。
如果左子节点不为空,那么左子节点的值小于结点值。
如果右子节点不为空,那么右子节点的值大于节点值。
左右子树同样满足上面的条件。
简而言之,左节点值<结点值<右节点值。
平衡二叉搜索树:又被称为AVL。它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二叉树的遍历
二叉树的遍历分为前中后三种遍历方式,三者的去边就在于结点和其左右子节点的遍历顺序不同。以根结点的遍历顺序命名。比如:
前序遍历,遍历顺序为中左右,先遍历根节点,在遍历左节点,最后右节点。
中序遍历,遍历顺序为左中右,先遍历左节点,在遍历根节点,最后右节点。
后序遍历,遍历顺序为左右中,先遍历左节点,在遍历右节点,最后中节点。
递归遍历:
题目:二叉树的前序遍历
给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
题目分析 :
cs
public class Solution {
public IList<int> PreorderTraversal(TreeNode root) {
var res=new List<int>();
if(root==null) return res;
Traversal(root,res);
return res;
}
public void Traversal(TreeNode root,List<int> res){
if(root==null) return;
res.Add(root.val); //当前节点,记录,相当于中
Traversal(root.left,res);//将左节点传入,遍历,相当于左
Traversal(root.right,res);//将右节点传入,遍历,相当于右
//遍历顺序为中左右。
//对于递归法的中序和后序,也只是改变这三行的顺序而已
//res.Add(root.val);
//Traversal(root.left,res);
//Traversal(root.right,res);
}
}
他的运行情况大概是这样的
从根节点一路遍历到叶子节点,然后返回到上一级点,在去遍历上一节点的右边。重复这个过程,知道所有节点都被遍历完成。
迭代遍历:
同样的题目,来看看迭代法。
cs
public class Solution {
public IList<int> PreorderTraversal(TreeNode root) {
var res=new List<int>();
if(root==null) return res;
Stack<TreeNode> temp=new Stack<TreeNode>();
temp.Push(root);//放入初始节点
while(temp.Count!=0)//保证所有节点都被放入过栈中
{
TreeNode node=temp.Pop();//取出栈顶元素
res.Add(node.val);
if(node.right!=null)//前序为中左右,这里使用了栈,因为栈是先进后出的,所以先将右节点放入
temp.Push(node.right);
if(node.left!=null)
temp.Push(node.left);
}
return res;
}
}
来看看他的执行过程吧。
对于递归法来说,前序的中左右和后序的左右中其实写法也差不多。
可以看到,在递归是改变入栈的顺序的到的结果是不一样的,而将这个结果反转就成了另外一种遍历。但是对于中序不行。中序的左右中,如果用上面的代码是不行的。
cs
public class Solution {
public IList<int> InorderTraversal(TreeNode root) {
var res=new List<int>();
Stack<TreeNode> temp=new Stack<TreeNode>();//辅助栈
TreeNode cur=root;//指向当前访问的节点
while(temp.Count!=0||cur!=null)
{
if(cur!=null)
{
temp.Push(cur);
cur=cur.left;//向左遍历,确定没有左孩子
}
else//没有左节点的节点
{
cur= temp.Pop();
res.Add(cur.val);
cur=cur.right;//指向右节点,开始迭代
}
}
return res;
}
}
来看看运行过程:
那么,迭代法有没有一种写法能和递归一样,只需要改变顺序就可以改变遍历顺序的呢?
迭代法的统一写法:
cs
// 中序遍历
public IList<int> InorderTraversal(TreeNode root)
{
var res = new List<int>();
var st = new Stack<TreeNode>();
if (root == null) return res;
st.Push(root);
while (st.Count != 0)
{
var node = st.Peek();
if (node == null)
{
st.Pop();
node = st.Peek();
st.Pop();
res.Add(node.val);
}
else
{
st.Pop();
if (node.right != null) st.Push(node.right);//右
st.Push(node);//中
st.Push(null);//中
if (node.left != null) st.Push(node.left);//左
}
}
return res;
}
来看看他的运行过程:
层序遍历:
题目:二叉树的层序遍历
给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
题目分析:
看张图吧》
这就是层序遍历的运行过程。
cs
public class Solution {
public IList<IList<int>> LevelOrder(TreeNode root) {
IList<IList<int>> res=new List<IList<int>>();
Queue tmp = new Queue();//辅助队列
if(root==null) return res;
tmp.Enqueue(root);
while(tmp.Count!=0)
{
int size=tmp.Count;//记录每一层的数量,方便出队时区分层数
IList<int> vec=new List<int>();
for(int i=0;i<size;i++)
{
TreeNode node=(TreeNode)tmp.Dequeue();
vec.Add(node.val);//取出
if(node.left!=null) tmp.Enqueue(node.left);//加入左节点
if(node.right!=null) tmp.Enqueue(node.right);
}
res.Add(vec);
}
return res;
}
}
对于层序遍历的题,有很多的变体,其本质上都是层序。
对于更详细的解析与其他语言的代码块,可以去代码随想录上查看。