二叉树

二叉树是一种特殊的树,每个节点最多只能有两个子节点。

二叉搜索树要求:

若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树
image.png

查找节点

查找节点从根节点开始遍历查找

1 、查找值比当前节点值大,则搜索右子树;

2、 查找值等于当前节点值,停止搜索(终止条件);

3、查找值小于当前节点值,则搜索左子树;
image.png

插入节点

要插入节点,必须先找到插入的位置。与查找操作相似,由于二叉搜索树的特殊性,待插入的节点也需要从根节点开始进行比较,小于根节点则与根节点左子树比较,反之则与右子树比较,直到左子树为空或右子树为空,则插入到相应为空的位置,在比较的过程中要注意保存父节点的信息 及 待插入的位置是父节点的左子树还是右子树,才能插入到正确的位置

 public boolean insert(int data) {
        count++;
        //如果第一个节点为空 设置第一个节点
        Node newNode = new Node(data);
        if (root == null) {
            root = newNode;
            return true;
        }
        Node current = root;
        Node parentNode = null;
 
        while (current != null) {
            parentNode = current;
            //当前值比新插入值大
            if (current.data > data) {
                current = current.leftNode;
                //若左节点为空 则直接插入即可
                if (current == null) {
                    parentNode.leftNode = newNode;
                    return true;
                }
            } else {
                //当前值小于新插入值
                current = current.rightNode;
                if (current == null) {
                    parentNode.rightNode = newNode;
                    return true;
                }
            }
        }
        return false;
    }

删除节点

删除节点相对于查询和删除稍微复杂点,主要是删除时考虑的情况多一点点。

  • 删除节点没有子节点
    这种的话就直接删除当前节点就好了,只需将当前节点的父节点指向的节点置为null即可
//当前节点是否为左节点
        boolean isLeftNode = false;

// 1 第一种情况 此节点为叶子节点
        if (current.leftNode == null && current.rightNode == null) {
            if (current == root) {
                root = null;
            } else if (isLeftNode) {
                //如果要删除的节点为父节点的左节点 把父节点的左节点置为空
                parentNode.leftNode = null;
            } else {
                parentNode.rightNode = null;
            }
            return true;
        }
  • 删除节点有一个子节点
    删除有一个子节点的节点,我们只需要将其父节点原本指向该节点的引用,改为指向该节点的子节点即可。
if (current.leftNode == null && current.rightNode != null) {
            if (root == current) {
                root = current.rightNode;
            } else if (isLeftNode) {
                parentNode.leftNode = current.rightNode;
            } else {
                parentNode.rightNode = current.rightNode;
            }
        } else if (current.leftNode != null && current.rightNode == null) {
            if (root == current) {
                root = current.leftNode;
            } else if (isLeftNode) {
                parentNode.leftNode = current.leftNode;
            } else {
                parentNode.rightNode = current.leftNode;
            }
        }
  • 删除节点有两个子节点
    当删除的节点存在两个子节点,那么删除之后,两个子节点的位置我们就没办法处理了。既然处理不了,我们就想到一种办法,用另一个节点来代替被删除的节点,那么用哪一个节点来代替呢?
    这实际上就是要找比删除节点关键值大的节点集合中最小的一个节点,只有这样代替删除节点后才能满足二叉搜索树的特性。
    程序找到删除节点的右节点,(注意这里前提是删除节点存在左右两个子节点,如果不存在则是删除情况的前面两种),然后转到该右节点的左子节点,依次顺着左子节点找下去,最后一个左子节点即是后继节点;如果该右节点没有左子节点,那么该右节点便是后继节点。
    二叉树

     if(current.leftNode != null && current.rightNode != null){
              //获取删除节点的后继结点
              Node successor = getSuccessor(current);
              if (root == current) {
                  root = successor;
              } else if (isLeftNode) {
                  parentNode.leftNode = successor;
              } else {
                  parentNode.rightNode = successor;
              }
          }
          return false;
    public Node getSuccessor(Node delNode) {
     
          Node successorParent = delNode;
          Node successor = delNode;
          Node current = delNode.rightNode;
          while (current != null) {
              successorParent = successor;
              successor = current;
              current = current.leftNode;
          }
          if (successor != delNode.rightNode) {
              successorParent.leftNode = successor.rightNode;
              successor.leftNode= delNode.leftNode;
          }
          return successor;
      }
    

前序遍历

前序遍历:首先访问根结点,然后遍历左子树,最后遍历右子树(根->左->右)

顺序:访问根节点->前序遍历左子树->前序遍历右子树

中序遍历

中序遍历:首先遍历左子树,然后访问根节点,最后遍历右子树(左->根->右)

顺序:中序遍历左子树->访问根节点->中序遍历右子树

后序遍历

后序遍历:首先遍历左子树,然后遍历右子树,最后访问根节点(左->右->根)

顺序:后序遍历左子树->后序遍历右子树->访问根节点

层序遍历;按层从左到右访问

前序中序后序
image.png
前序遍历:根->左->右:12473568
中序遍历:左->根->右:47215386
后序遍历:左->右->根:74258631

重构二叉树

image.png

/**
 * Definition for binary tree
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
public class Solution {
   //前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}
    public TreeNode reConstructBinaryTree(int[] pre, int[] in) {

        int len = pre.length;

        TreeNode root = new TreeNode(pre[0]);
        //说明只剩下一个了,表示叶子节点,递归可以退出了
        if (pre.length == 1) {
            root.left = null;
            root.right = null;
            return root;
        }

        //中间值 在{4,7,2,1,5,3,8,6} 这个中间值第一次应该是3
        int flag = 0;
        for (int i = 0; i < len; i++) {
            //在中序中找到
            if (pre[0] == in[i]) {
                flag = i;
                break;
            }
        }
        if (flag > 0) {
            //左子树的先序
            int[] leftPre = new int[flag];
            //左子树的中序
            int[] leftIn = new int[flag];
            for (int j = 0; j < flag; j++) {
                leftPre[j] = pre[j + 1];
            }
            for (int j = 0; j < flag; j++) {
                leftIn[j] = in[j];
            }
            //左子树递归
            root.left = reConstructBinaryTree(leftPre, leftIn);
        } else {
            root.left = null;
        }
        if (pre.length - flag - 1 > 0) {
            //右子树的先序,长度为 总-根-左子树
            int[] rightPre = new int[pre.length - 1 - flag];
            //右子树的中序
            int[] rightIn = new int[pre.length - 1 - flag];

            for (int j = flag + 1; j < len; j++) {
                //右子树中序,为什么要j-flag-1呢 因为我的rightIn要从0开始 而j是k+1开始的 ,所以很尴尬,只能用j-flag-1
                rightIn[j - flag - 1] = in[j];

                rightPre[j - flag - 1] = pre[j];
            }

            root.right = reConstructBinaryTree(rightPre, rightIn);
        } else {
            root.right = null;
        }

        return root;

    }
 
}

Loccy
4 声望1 粉丝