1.二叉树基础理论
二叉搜索树
二叉搜索树是有数值的了,二叉搜索树是一个有序树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
平衡二叉搜索树
平衡二叉搜索树:又被称为AVL(Adelson-Velsky and Landis)树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二叉树存储方式
二叉树可以链式存储,也可以顺序存储。
那么链式存储方式就用指针, 顺序存储的方式就是用数组。顺序存储的元素在内存是连续分布的,而链式存储则是通过指针把分布在散落在各个地址的节点串联一起。
用数组来存储二叉树如何遍历的呢?
如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。
2.二叉树递归遍历
144.二叉树的前序遍历
根据递归序,根左右
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 {
/**
* 二叉树的前序遍历---->根左右(遍历)
*
* @param root
* @return
*/
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
process(root, ans);
return ans;
}
/**
* 递归---->递归序
*
* @param root
* @param ans
*/
public void process(TreeNode root, List<Integer> ans) {
if (root == null) {
return;
}
ans.add(root.val);
process(root.left, ans);
process(root.right, ans);
}
}
94.二叉树的中序遍历
class Solution {
/**
* 二叉树的中序遍历---->左根右(遍历)
*
* @param root
* @return
*/
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
process(root, ans);
return ans;
}
/**
* 递归---->递归序
*
* @param root
* @param ans
*/
public void process(TreeNode root, List<Integer> ans) {
if (root == null) {
return;
}
process(root.left, ans);
ans.add(root.val);
process(root.right, ans);
}
}
145.二叉树的后序遍历
class Solution {
/**
* 二叉树的后序遍历---->左右根(遍历)
*
* @param root
* @return
*/
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
process(root, ans);
return ans;
}
/**
* 递归---->递归序
*
* @param root
* @param ans
*/
public void process(TreeNode root, List<Integer> ans) {
if (root == null) {
return;
}
process(root.left, ans);
process(root.right, ans);
ans.add(root.val);
}
}
3.二叉树的迭代遍历
144.二叉树的前序遍历
前序用栈!!!
前序的顺序根---->左---->右。一直把根节点的左孩子压栈并且在结果集里面添加这些左孩子的值,直到压到空的时候,node变为现在栈顶弹出来的元素(他自己本身作为根的时候已经压进去过了),然后指向其右孩子。(右孩子是否为空无所谓反正会进循环)
class Solution {
/**
* 二叉树前序遍历---->根左右,迭代法
*
* @param root
* @return
*/
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if (root == null) {
return ans;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (!stack.isEmpty() || node != null) {
while (node != null) {
stack.push(node);
ans.add(node.val);
node = node.left;
}
node = stack.pop();
node = node.right;
}
return ans;
}
}
94.二叉树的中序遍历
中序遍历:左------>根-------->右
跟前序一样的模拟的是一个进栈和出栈,区别就是什么时候把栈里面的元素加入结果集呢?
前序是压栈的时候就加入,所以是个根先的状态,而中序,左孩子先遍历,所以弹出的时候加入结果集,因为他们都是左。
class Solution {
/**
* 二叉树的中序遍历----->左 根 右
*
* @param root
* @return
*/
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ans = new ArrayList<>();
if (root == null) {
return ans;
}
TreeNode node = root;
Stack<TreeNode> stack = new Stack<>();
while (!stack.isEmpty() || node != null) {
while (node != null) {
stack.push(node);
node = node.left;
}
node = stack.pop();
ans.add(node.val);
node = node.right;
}
return ans;
}
}
*145.二叉树的后序遍历
class Solution{
public List<Integer> method1(TreeNode root) {
List<Integer> ans=new LinkedList<>();
Stack<TreeNode> stack=new Stack<>();
TreeNode prev=null;
//主要思想:
//由于在某颗子树访问完成以后,接着就要回溯到其父节点去
//因此可以用prev来记录访问历史,在回溯到父节点时,可以由此来判断,上一个访问的节点是否为右子树
while(root!=null||!stack.isEmpty()){
while(root!=null){
stack.push(root);
root=root.left;
}
//从栈中弹出的元素,左子树一定是访问完了的
root=stack.pop();
//现在需要确定的是是否有右子树,或者右子树是否访问过
//如果没有右子树,或者右子树访问完了,也就是上一个访问的节点是右子节点时
//说明可以访问当前节点
if(root.right==null||prev==root.right){
ans.add(root.val);
//更新历史访问记录,这样回溯的时候父节点可以由此判断右子树是否访问完成
prev=root;
root=null;
}else{
//如果右子树没有被访问,那么将当前节点压栈,访问右子树
stack.push(root);
root=root.right;
}
}
return ans;
}
}
2022.10.22补