逸帅 发表于 2021-3-25 20:21

二叉树----遍历、查找、删除

# 二叉树----遍历、查找、删除

## 1、为什么需要树?

> - 数组的查找效率高,但是插入效率低。
> - 链表的插入效率高,查找效率低。

## 2、二叉树

- 二叉树的基本概念:每个节点最多只能由**两个子节点**的一种树叫做二叉树
- 满二叉树:如果该二叉树的所有**叶子节点**都在**最后一层**,并且结点总数= **2n -1** , n为层数,则我们称为满二叉树。
- 完全二叉树:如果该二叉树的所有**叶子节点**都在**最后一层**或者**倒数第二层**,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树


## 3、二叉树的遍历

1. #### 前序遍历

   **先遍历父节点**,再遍历左子节点,最后遍历右子节点

2. #### 中序遍历

   先遍历左子节点,**再遍历父节点**,最后遍历右子节点

3. #### 后序遍历

   先遍历左子节点,再遍历右子节点,**最后遍历父节点**

**区别:看父节点什么时候遍历,在最前面就是前序,在中间就是中序,在最后就是后续**

## 4、二叉树的遍历代码实现

```java
class BinaryTree {
   /**
    * 根节点
    */
   private StuNode root;

   public void setRoot(StuNode root) {
      this.root = root;
   }

   public void preTraverse() {
      if(root != null) {
         System.out.println("前序遍历");
         root.preTraverse();
         System.out.println();
      }else {
         System.out.println("二叉树为空!");
      }
   }

   public void midTraverse() {
      if(root != null) {
         System.out.println("中序遍历");
         root.midTraverse();
         System.out.println();
      }else {
         System.out.println("二叉树为空!");
      }}

   public void lastTraverse() {
      if(root != null) {
         System.out.println("后序遍历");
         root.lastTraverse();
         System.out.println();
      }else {
         System.out.println("二叉树为空!");
      }}
}

/**
* 二叉树中的一个节点
* 保存了学生信息和左右孩子信息
*/
@Data
class StuNode {
   int id;
   String name;
   StuNode left;
   StuNode right;

   public StuNode(int id, String name) {
      this.id = id;
      this.name = name;
   }

   @Override
   public String toString() {
      return "StuNode{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
   }

   /**
    * 前序遍历
    */
   public void preTraverse() {
      //父节点最开始就输出
      System.out.println(this);
      if(left != null) {
         left.preTraverse();
      }
      if(right != null) {
         right.preTraverse();
      }
   }

   /**
    * 中序遍历
    */
   public void midTraverse() {
      if(left != null) {
         left.midTraverse();
      }
      //父节点在中间输出
      System.out.println(this);
      if(right != null) {
         right.midTraverse();
      }
   }

   public void lastTraverse() {
      if(left != null) {
         left.lastTraverse();
      }
      if(right != null) {
         right.lastTraverse();
      }
      //父节点在最后输出
      System.out.println(this);
   }
}
```

## 5、二叉树的查找

与3是类似的,在遍历的过程中加一个判断,找到对应的元素直接返回即可!

## 6、二叉树的查找代码实现

```java
class BinarySearchTree {
   private Student root;

   public void setRoot(Student root) {
      this.root = root;
   }

   public void preSearch(int id) {
      System.out.println("前序查找");
      if(root == null) {
         System.out.println("树为空!");
         return;
      }
      Student result = root.preSearch(id);
      if(result == null) {
         System.out.println("未找到该元素");
         System.out.println();
         return;
      }
      System.out.println(result);
      System.out.println();
   }

   public void midSearch(int id) {
      System.out.println("中序查找");
      if(root == null) {
         System.out.println("树为空!");
         return;
      }
      Student result = root.midSearch(id);
      if(result == null) {
         System.out.println("未找到该元素");
         System.out.println();
         return;
      }
      System.out.println(result);
      System.out.println();
   }

   public void lastSearch(int id) {
      System.out.println("后序查找");
      if(root == null) {
         System.out.println("树为空!");
         return;
      }
      Student result = root.lastSearch(id);
      if(result == null) {
         System.out.println("未找到该元素");
         System.out.println();
         return;
      }
      System.out.println(result);
      System.out.println();
   }
}

@Data
class Student {
   int id;
   String name;
   Student left;
   Student right;

   public Student(int id, String name) {
      this.id = id;
      this.name = name;
   }

   @Override
   public String toString() {
      return "Student{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
   }

   /**
    * 前序查找
    * @Param id 要查找的学生id
    * @Return 查找到的学生
    */
   public Student preSearch(int id) {
      //如果找到了,就返回
      if(this.id == id) {
         return this;
      }

      //在左子树中查找,如果找到了就返回
      Student student = null;
      if(left != null) {
         student = left.preSearch(id);
      }
      if(student != null) {
         return student;
      }

      //在右子树中查找,无论是否找到,都需要返回
      if(right != null) {
         student = right.preSearch(id);
      }
      return student;
   }

   /**
    * 中序查找
    * @param id 要查找的学生id
    * @return 查找到的学生
    */
   public Student midSearch(int id) {
      Student student = null;
      if(left != null) {
         student = left.midSearch(id);
      }
      if(student != null) {
         return student;
      }

      //找到了就返回
      if(this.id == id) {
         return this;
      }

      if(right != null) {
         student = right.midSearch(id);
      }
      return student;
   }

   /**
    * 后序查找
    * @param id 要查找的学生id
    * @return 查找到的学生
    */
   public Student lastSearch(int id) {
      Student student = null;
      if(left != null) {
         student = left.lastSearch(id);
      }
      if(student !=null) {
         return student;
      }

      if(right != null) {
         student = right.lastSearch(id);
      }

      if(this.id == id) {
         return this;
      }
      return student;
   }
}
```

## 7、二叉树节点的删除

- 如果删除的节点是叶子节点,则删除该节点

- 如果删除的节点是非叶子节点,则删除该子树.

### 7.1、删除节点的思路:

1. **先考虑是否是个空树**,即root节点是否为null
2. 因为我们的二叉树是**单向的**,所以我们是判断**当前结点的子结点**是否需要删除结点,而不能去判断当前这个结点是不是需要删除结点(假如找到了当前节点,就无法找到当前节点的父节点,也就意味着无法删除当前节点了)
3. 如果当前结点的**左子结点不为空**,并且左子结点就是要删除结点,就将 **this.left = null**; 并且就返回 (结束递归删除)
4. 如果当前结点的**右子结点不为空**,并且右子结点就是要删除结点,就将 **this.right= null** ;并且就返回 (结束递归删除)
5. 如果第2和第3步没有删除结点,那么我们就需要向左子树进行递归删除
6. 如果 4步也没有删除结点,则应当向右子树进行递归删除

### 7.2、删除节点的代码实现

```java
class BinaryDeleteTree {
   StudentNode root;

   public void setRoot(StudentNode root) {
      this.root = root;
   }

   /**
    * 删除节点
    * @param id 删除节点的id
    */
   public void deleteNode(int id) {
      System.out.println("删除节点");
      if(root.id == id) {
         root = null;
         System.out.println("根节点被删除");
         return;
      }
      //调用删除方法
      root.deleteNode(id);
   }
}

@Data
class StudentNode {
   int id;
   String name;
   StudentNode left;
   StudentNode right;

   public StudentNode(int id, String name) {
      this.id = id;
      this.name = name;
   }

   @Override
   public String toString() {
      return "StudentNode{" +
            "id=" + id +
            ", name='" + name + '\'' +
            '}';
   }

   /**
    * 删除节点
    * @param id 删除节点的id
    */
   public void deleteNode(int id) {
      //如果左子树不为空且是要查找的节点,就删除
      if(left != null && left.id == id) {
         left = null;
         System.out.println("删除成功");
         return;
      }

      //如果右子树不为空且是要查找的节点,就删除
      if(right != null && right.id == id) {
         right = null;
         System.out.println("删除成功");
         return;
      }

      //左递归,继续查找
      if(left != null) {
         left.deleteNode(id);
      }

      //右递归,继续查找
      if(right != null) {
         right.deleteNode(id);
      }
   }
}
```

红蓝黄 发表于 2021-3-25 20:23

以前学得很好,现在全忘了{:301_1004:}

yiwanyiwan 发表于 2021-3-25 20:33

学无止境

LucretiaAgi 发表于 2021-3-25 21:59

谢谢大佬分享数据结构相关教程和代码,受益良多。

Mandrake 发表于 2021-3-25 22:18

学过不用,渐渐忘记
页: [1]
查看完整版本: 二叉树----遍历、查找、删除