二叉树的定义

  • 二叉树具有唯一根节点
  • 二叉树每个节点最多有两个孩子
  • 二叉树每个节点最多有一个父亲
  • 二叉树具有天然的递归结构
    • 每个节点的左子树也是二叉树
    • 每个节点的右子树也是二叉树
    • 二叉树不一定是“满”的(意思是可能只有左孩子或右孩子,因为null也可能是二叉树)

1-2 二分搜索树基础【一手微信itit11223344】.mp4_20211104_151702769.jpg

二分搜索树

  • 二分搜索树首先是二叉树
  • 二分搜索树的每个节点的值:
    • 大于其左子树的所有节点的值
    • 小于其右子树的所有节点的值
    • 因此存储的元素必须具有可比较性

1-2 二分搜索树基础【一手微信itit11223344】.mp4_20211104_152711274.jpg

  • 目前二分搜索树不包含重复的元素,如果想包含重复元素,只需要定义
    • 左子树小于等于节点
    • 或者右子树大于等于节点

二叉搜索树的代码

  1. private class Node {
  2. public E e;
  3. public Node left, right;
  4. public Node(E e) {
  5. this.e = e;
  6. left = null;
  7. right = null;
  8. }
  9. }
  10. private Node root;
  11. private int size;
  12. public BST() {
  13. root = null;
  14. size = 0;
  15. }
  16. public int size() {
  17. return size;
  18. }
  19. public boolean inEmpty() {
  20. return size == 0;
  21. }

添加子节点递归代码

  1. //往二叉树中插入一个元素
  2. public void add(E e) {
  3. root = add(root, e);
  4. }
  5. //递归插入一个元素
  6. //返回插入新节点后二分搜索树的跟
  7. private Node add(Node node, E e) {
  8. if (node == null) {
  9. size++;
  10. return new Node(e);
  11. }
  12. //递归
  13. if (node.e.compareTo(e) > 0) {
  14. node.left = add(node.left, e);
  15. } else if (node.e.compareTo(e) < 0) {
  16. node.right = add(node.right, e);
  17. }
  18. return node;
  19. }

添加子节点非递归代码

  1. //非递归写法插入一个元素
  2. public void addNR(E e){
  3. if (root == null){
  4. root = new Node(e);
  5. size++;
  6. return;
  7. }
  8. Node p = root;
  9. while (p!=null){
  10. //如果插入的值小于当前值则插到左边
  11. if (e.compareTo(p.e) < 0){
  12. //如果左子树为空就放在左子树
  13. if (p.left == null){
  14. p.left = new Node(e);
  15. size++;
  16. return;
  17. }else{
  18. p = p.left;
  19. }
  20. }else if (e.compareTo(p.e) > 0){
  21. if (p.right == null){
  22. p.right = new Node(e);
  23. size++;
  24. return;
  25. }else{
  26. p = p.right;
  27. }
  28. }else {
  29. return;
  30. }
  31. }
  32. }

前序遍历

1-8 二分搜索树的前序遍历【一手微信itit11223344】.mp4_20211105_133244726.jpg

  • 前序遍历过程

二叉树 - 图4

  • 前序遍历代码
  • 图示(递归)

二叉树 - 图5

前序遍历的非递归形式

  1. /**
  2. * 前序遍历的非递归写法
  3. */
  4. public void preOrderNR(){
  5. Stack<Node> stack = new Stack<>();
  6. stack.push(root);
  7. while (!stack.isEmpty()){
  8. Node cur = stack.pop();
  9. System.out.println(cur.e);
  10. if (cur.right != null){
  11. stack.push(cur.right);
  12. }
  13. if (cur.left != null){
  14. stack.push(cur.left);
  15. }
  16. }
  17. }
  • 图示

二叉树 - 图6

中序遍历

  • 中序遍历图示

二叉树 - 图7

  • 中序遍历代码

    1. /**
    2. * 中序遍历
    3. * 中序遍历的结果是顺序的
    4. */
    5. public void inOrder() {
    6. inOrder(root);
    7. }
    8. private void inOrder(Node node) {
    9. if (node == null)
    10. return;
    11. inOrder(node.left);
    12. System.out.println(node.e);
    13. inOrder(node.right);
    14. }


    后序遍历

  • 应用

    • 为二分搜索树释放内存
  • 后续遍历图示

二叉树 - 图8

  • 后序遍历代码

    1. /**
    2. * 后续遍历
    3. * @return
    4. */
    5. public void postOrder() {
    6. postOrder(root);
    7. }
    8. private void postOrder(Node node) {
    9. if (node == null)
    10. return;
    11. postOrder(node.left);
    12. postOrder(node.right);
    13. System.out.println(node.e);
    14. }

    二分搜索树的层序遍历

  • 层序遍历图示

二叉树 - 图9

  • 广度优先遍历的意义
    • 常用于算法设计中-最短路径
  • 广度优先算法代码

    1. public List<List<Integer>> levelOrder(TreeNode root) {
    2. if (root == null){
    3. return new ArrayList<>();
    4. }
    5. List<List<Integer>> res = new ArrayList<>();
    6. Queue<TreeNode> queue = new LinkedList<>();
    7. queue.add(root);
    8. while (!queue.isEmpty()){
    9. List<Integer> sublist = new ArrayList<>();
    10. int queueLength = queue.size();
    11. for (int i = 0; i < queueLength; i++) {
    12. TreeNode node = queue.remove();
    13. sublist.add(node.val);
    14. if (node.left != null){
    15. queue.add(node.left);
    16. }
    17. if (node.right != null){
    18. queue.add(node.right);
    19. }
    20. }
    21. res.add(sublist);
    22. }
    23. return res;
    24. }

删除二分搜索树的最小值和最大值

  • 很明显,最小值是从根节点开始最左边,最大值是从根节点开始最右边的点

删除节点

  1. /**
  2. * 二分搜索树删除节点
  3. * @return
  4. */
  5. //删除以node为根的二分搜索树中值为e的节点,递归算法
  6. //返回删除节点后新的的二分搜索树的根
  7. public Node remove(Node node , E e){
  8. if (node == null)
  9. return null;
  10. if (e.compareTo(node.e) < 0 ){
  11. node.left = remove(node.left,e);
  12. return node;
  13. }else if(e.compareTo(node.e) > 0){
  14. node.right = remove(node.right,e);
  15. return node;
  16. }else{ // e == node.e
  17. //待删除的节点的左子树为空
  18. if (node.left == null){
  19. Node rightNode = node.right;
  20. node.right = null;
  21. size--;
  22. return rightNode;
  23. }
  24. //待删除的节点的右子树为空的情况
  25. if (node.right == null){
  26. Node leftNode = node.left;
  27. node.left = null;
  28. size--;
  29. return leftNode;
  30. }
  31. //待删除的节点左右子树均不为空的情况
  32. //找到比要删除节点大的最小子节点,即待删除节点右子树的最小节点
  33. //用这个节点顶替待删除节点的位置
  34. Node successor = minimum(node.right);
  35. successor.right = removeMin(node.right);
  36. successor.left = node.left;
  37. node.left = node.right = null;
  38. return successor;
  39. }
  40. }