小白数据结构学习--二叉树基础

树与二叉树

树是最常用的数据结构之一,它是一种非线性表结构,较之前的线性表结构要复杂一些。

树的基本概念

1、树的定义

树是由n个元素组成的有限集合,有三大特点:
1、每个元素称为结点(node);
2、有一个特定的结点,称为根结点或根(root);
3、除根结点外,其余结点被分成m(m>=0)个互不相交的有限集合,而每个子集又都是一棵树(称为原树的子树)
树(借用王争老师的图)

2、节点、高度、深度、层

1、节点:树中的元素即被称为节点,连线则代表父子关系如图:
树其中A为B\C\D的父节点,相应的B\C\D就称为A的子节点,而没有父节点的E就被称为根节点,没有子节点的G\H\I\J\K\L等则被称为叶子节点。
2、高度:节点的高度为节点到叶子节点的最长路径(即边数)
3、深度:根节点到此节点的路程(遍历的边数)
4、层:节点的深度+1
5、树的高度:根节点的高度

二叉树

二叉树是树中最常用的,顾名思义二叉树就是每个节点最多两个子节点的树,两个子节点也分别称为左子节点和右子节点。
特殊二叉树如图,编号为2的特殊二叉树被称为满二叉树,叶子节点全部在最底层,并且除了叶子节点,其他节点都有左子树和右子树。
编号为3的二叉树被称为完全二叉树,完全二叉树叶子节点全部在最底层,并且靠左排列,除了底层外其他节点都有左右子树。满二叉树是特殊的完全二叉树。

最常用的树为二叉查找树,其特点为任一节点,其左子节点小于此节点,右子节点大于此节点
二叉查找树

树的遍历

遍历树有三种经典的遍历方法:前序遍历中序遍历后序遍历
前序遍历的顺序为:先遍历根节点,然后左子树,最后右子树。
中序遍历顺序为:先左子树,然后根节点,最后右子树。
后序遍历顺序为:先左子树,然后右子树,最后根节点。
其遍历的代码如下:


void preOrder(Node* root) {
  if (root == null) return;
  print root // 此处为伪代码,表示打印root节点
  preOrder(root->left);
  preOrder(root->right);
}

void inOrder(Node* root) {
  if (root == null) return;
  inOrder(root->left);
  print root // 此处为伪代码,表示打印root节点
  inOrder(root->right);
}

void postOrder(Node* root) {
  if (root == null) return;
  postOrder(root->left);
  postOrder(root->right);
  print root // 此处为伪代码,表示打印root节点
}

树的查找、插入、删除

二叉查找树的查找

一般树会使用二叉查找树,其查找简单快捷。
二叉查找树的查找方法比较容易理解:
在查找所找的值时,从根节点开始,
(1)如果所查值等于根节点则返回根节点
(2)如果所查值小于根节点,则遍历其左子树
(3)如果所查值大于根节点,则遍历其右子树
其代码如下:


public class BinarySearchTree {
  private Node tree;

  public Node find(int data) {
    Node p = tree;
    while (p != null) {
      if (data < p.data) p = p.left;
      else if (data > p.data) p = p.right;
      else return p;
    }
    return null;
  }

  public static class Node {
    private int data;
    private Node left;
    private Node right;

    public Node(int data) {
      this.data = data;
    }
  }
}

二叉查找树的插入

二叉查找树的插入过程有点类似查找操作。新插入的数据一般都是在叶子节点上,所以我们只需要从根节点开始,依次比较要插入的数据和节点的大小关系。
(1)如果要插入的数据比节点的数据大,并且节点的右子树为空,就将新数据直接插到右子节点的位置;如果不为空,就再递归遍历右子树,查找插入位置。
(2)如果要插入的数据比节点数值小,并且节点的左子树为空,就将新数据插入到左子节点的位置;如果不为空,就再递归遍历左子树,查找插入位置

其插入代码如下:


public void insert(int data) {
  if (tree == null) {
    tree = new Node(data);
    return;
  }

  Node p = tree;
  while (p != null) {
    if (data > p.data) {
      if (p.right == null) {
        p.right = new Node(data);
        return;
      }
      p = p.right;
    } else { // data < p.data
      if (p.left == null) {
        p.left = new Node(data);
        return;
      }
      p = p.left;
    }
  }
}

二叉查找树的删除

二叉查找树的查找、插入操作都比较简单易懂,但是它的删除操作就比较复杂了 。针对要删除节点的子节点个数的不同,需要分三种情况来处理。
第一种情况是,如果要删除的节点没有子节点,我们只需要直接将父节点中,指向要删除节点的指针置为 null。
第二种情况是,如果要删除的节点只有一个子节点(只有左子节点或者右子节点),我们只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。
第三种情况是,如果要删除的节点有两个子节点,这就比较复杂了。我们需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节点(如果有左子结点,那就不是最小节点了),所以,我们可以应用上面两条规则来删除这个最小节点。

删除的代码如下:


public void delete(int data) {
  Node p = tree; // p指向要删除的节点,初始化指向根节点
  Node pp = null; // pp记录的是p的父节点
  while (p != null && p.data != data) {
    pp = p;
    if (data > p.data) p = p.right;
    else p = p.left;
  }
  if (p == null) return; // 没有找到

  // 要删除的节点有两个子节点
  if (p.left != null && p.right != null) { // 查找右子树中最小节点
    Node minP = p.right;
    Node minPP = p; // minPP表示minP的父节点
    while (minP.left != null) {
      minPP = minP;
      minP = minP.left;
    }
    p.data = minP.data; // 将minP的数据替换到p中
    p = minP; // 下面就变成了删除minP了
    pp = minPP;
  }

  // 删除节点是叶子节点或者仅有一个子节点
  Node child; // p的子节点
  if (p.left != null) child = p.left;
  else if (p.right != null) child = p.right;
  else child = null;

  if (pp == null) tree = child; // 删除的是根节点
  else if (pp.left == p) pp.left = child;
  else pp.right = child;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值