二叉树
每个元素存在一个节点中 和链表 一样 动态数据结构
和链表一样 节点结构
节点定义
class Node{
E e;
Node left; // 左孩子
Node right; // 右孩子
}
二叉树具有唯一根节点。
二叉树 每个节点 最多有两个孩子
叶子节点的定义 : 这个节点没有左右孩子。
二叉树的每个节点最多有一个父亲节点
二叉树具有天然的递归结构
每个结点的左子树 是 二叉树。
每个节点的右子树也是二叉树。
注意的是:
- 二叉树不一定是满的
- 一个节点也可以是一个二叉树
- Null 空也是二叉树。
二叉树–二分搜索树
性质 (定义): 二叉搜索树 每个节点的值 大于 左子树所有节点的值
小于其 右子树所有节点的值
每一个节点都要满足这样的性质
存储的元素必须要有可比较性
二分搜索树 -------添加元素
复杂递归写法
public void add(Integer e ){
/*
* 如果根节点为空 加到根节点上 size ++
* */
if (root == null){
root = new Node(e);
size ++;
}
else
add(root , e);
}
/*复杂递归写法*/
private void add(Node root, Integer e) {
if (Objects.equals(root.e, e)) {
return;
}
else if (root.e < e && root.right == null) {
root.right = new Node(e);
size ++;
return;
}
else if (root.e > e && root.left == null){
root.left = new Node(e);
size ++;
return;
}
if (e < root.e) {
add(root.left , e);
}
else
add(root.right,e);
}
递归代码结束条件太多,可以试着不做判断 左右孩子是否为空,让递归进行到底,节点为空的是后new 一个新的节点出来 与二茶树的 左节点或者右节点进行链接 简洁代码写法如下
public void add(Integer e ){
/*
* 如果根节点为空 加到根节点上 size ++
* */
root = add (root,e);
}
private Node add(Node root, Integer e) {
if (root == null){
size ++;
return new Node(e);
}
if (e < root.e) {
root.left = add(root.left , e); // 左子树为空的话 会以 e为值 new 一个 新的节点。
}
else
root.right = add(root.right,e);
return root;
}
在二分搜素树中搜节点
public boolean contains(Node root, Integer e) {
if (root == null){
return false;
}
if (root.e.equals(e)) {
return true;
} else if (root.e.compareTo(e) < 0) {
// 去 遍历右子树 遍历到底没找到 节点为空 返回false 左子树相同情况
return contains(root.right, e);
} else
return contains(root.left, e);
// 递归遍历完了 让然没有找到 就返回false.
}
二分搜索树的遍历
对于上面两个操作 搜索节点 和 添加节点 我们只要顾及一个分支的递归遍历, 对于二叉树的 遍历 则要顾及两个分支的遍历。进行两次递归调用。 不加判断条件。
// 前序遍历。 访问顺序 改变一下 就是 中 后 序的遍历
public void preOrder(Node node){
if (node == null){
// 递归终止条件
return;
}
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
**不使用递归实现二叉树的遍历怎么实现 递归是调用系统栈来实现的 栈有一个特点 是 后入先出的数据结构 以前序遍历为例: 根 左 右 **
要实现这样的遍历顺序 像访问根节点 根入就出 但是 左 和 右 子树很大的情况下 不能 直接将其 放在 栈中 因为还要遍历 他的子树
应该逆序放入 才会 按前序遍历的顺序出来 代码如下
public void preOrder(Node node){
Stack<Node> stack = new Stack<>();
stack.push(node);
while (!stack.isEmpty()){
Node cur = stack.pop();
System.out.println(cur.e);
if (cur.right != null)
stack.push(cur.right);
if (cur.left != null)
stack.push(cur.left);
}
}
二叉树的层序遍历(广度优先遍历)
按深度 一层一层 的遍历 BFS
使用队列
这种数据结构实现广度优先遍历
public void bfs(Node node){
Queue<Node> queue = new LinkedList<>();
queue.add(node);
while (!queue.isEmpty()){
// 循环不变性 队列不为空的条件下循环进行
Node remove = queue.remove();
System.out.println(remove.e);
if (null != node.left)
bfs(node.left);
if (null != node.right)
bfs(node.right);
}
}
删除节点的操作
删除最小节点
public Node removeMinNode(Node node){
/*递归终止 传过来的 root 肯定在系统栈顶的最顶端 直接返回node 即可*/
// root = node;
if (null == node.left) {
// 当前节点没有左子树了 就是 最小节点
/*
最小节点课能有右子树 拿到最小节点的右子树*/
Node rightNode = node.right;
node.right = null ; // 让当前节点与树脱离关系 此时右节点 为 改树的最小值
size --;
return rightNode; // 返回一层层调用的的 rightNode
}
// 递归一层一层 最后结束的这层 终止条件 是 已经找到最小的节点 上一层节点 为 node 栈的第二层
node.left = removeMinNode(node.left);
return node;
}
删除最大节点的操作
/* 删除最大 结点的操作*/
public Node removeMaxNode(){
return removeMaxNode(root);
}
private Node removeMaxNode(Node node){
if (null == node.right){
Node scncondMax = node.left;
node.left = null; // 让该节点脱离树
size -- ; // 树的节点减一操作
return scncondMax;
}
node.right = removeMaxNode(node.right);
return node;
}