Algorithms_二叉树&二分搜索树初探


在这里插入图片描述

我们简明扼要的整理下二叉树,以及二分搜索树的特征及代码实现


二叉树

在这里插入图片描述

我们分析下,二叉树的节点元素 ,除了要存储数据,还有两个引用分别指向其他节点

class Node {
	E e;
	Node left ;  // 左孩子 
    Node right ; // 右孩子
}

特征

  • 和链表一样,是动态数据结构

  • 每个节点最多只能分出来两棵树 (同样类比多叉树)

  • 二叉树具有唯一的根节点
    在这里插入图片描述

    在这里插入图片描述
    按照上图, 16就是 28根节点的左孩子, 30 就是28的右孩子 。 依次类推 13是16的左孩子, 22是16的右孩子。 29是30的左孩子, 42是30的右孩子。

  • 二叉树每个节点最多有两个孩子

    在这里插入图片描述

    叶子节点是没有任何孩子的节点。 上图是一个完整的二叉树 ,实际应用中并不都是这种情况。

  • 二叉树每个节点最多有一个父亲节点,只有根节点没有父亲节点。

  • 二叉树具有天然的递归结构

    每个节点的左子树也是二叉树

    每个节点的右子树也是二叉树
    在这里插入图片描述

  • 二叉树不一定是“满”的


二分搜索树 Binary Search Tree

特征

  • 二分搜索树是二叉树,二叉树的特征全部适用于二分搜索树

  • 二分搜索树的每个节点的值

    大于其左子树的所有节点的值

    小于其右子树的所有节点的值

    在这里插入图片描述

  • 每一棵子树也是二分搜索树

    比如下面的二分搜索树

    在这里插入图片描述


限制(存储的元素必须具有可比性)

为什么要这么设计呢?

其实是为了特定的场景,我们使用二分搜索树查询数据的话,这两个数可以比较的话,那么就可以明确的知道这个数据在左边还是在右边了,查询效率高。

所以什么样的数据存到二分搜索树中才能搜索呢? —> 存储的元素必须具有可比较性

整型自然不必说了,可以比较大小,如果我们存储的是个对象,那这个对象一定要能比较 。 举个例子存储的对象为Car , 要么这个Car 可以按照价格比较,要么可以按照重量比较,总之能比较就可以。

在Java中这个对象要继承Comparable接口


Code

添加数据

/**
 * 
 * 
 * @ClassName: BinarySearchTree
 * 
 * @Description: 二分搜索树 --> 元素必须可以比较
 * 
 * @author: Mr.Yang
 * 
 * @date: 2020年2月8日 下午12:59:02
 * 
 * @param <E>
 */
public class BinarySearchTree<E extends Comparable<E>> {

	/**
	 * 
	 * @ClassName: Node
	 * 
	 * @Description: 数据节点类
	 * 
	 * @date: 2020年2月8日 下午12:58:44
	 */
	private class Node {
		private E e;// 数据
		private Node left; // 左孩子
		private Node right;// 右孩子

		public Node(E e) {
			this.e = e;
			this.left = null;
			this.right = null;
		}
	}

	private Node root;// 根节点
	private int size; // 当前数据量

	/**
	 * 
	 * 
	 * @Title:BinarySearchTree
	 * 
	 * @Description:默认构造函数
	 */
	public BinarySearchTree() {
		this.root = null;
		this.size = 0;
	}

	public int getSize() {
		return size;
	}

	public boolean isEmpty() {
		return size == 0;
	}

	/**
	 * 
	 * 
	 * @Title: add
	 * 
	 * @Description: 向二分搜索树中添加新的元素e
	 * 
	 * @param e
	 * 
	 * @return: void
	 */
	public void add(E e) {
        // 根节点的非空判断
		if (root == null) {
			root = new Node(e);
			size++;
		} else
			add(root, e);
	}

	/**
	 * 
	 * 
	 * @Title: add
	 * 
	 * @Description: 向以node为根的二分搜索树中插入元素e,递归算法
	 * 
	 * @param node
	 * @param e
	 * 
	 * @return: void
	 */
	private void add(Node node, E e) {
		if (e.equals(node.e))
			return;
		else if (e.compareTo(node.e) < 0 && node.left == null) { // 插入到左子树
			node.left = new Node(e);
			size++;
			return;
		} else if (e.compareTo(node.e) > 0 && node.right == null) {// 插入到右子树
			node.right = new Node(e);
			size++;
			return;
		}
	    // 递归调用
		if (e.compareTo(node.e) < 0)
			add(node.left, e);
		else // e.compareTo(node.e) > 0
			add(node.right, e);
	}
}


添加数据V2.0

上面的添加方法,OK ,没问题。 但代码量还是有点长

  1. 我们对Root节点做了特殊的非空判断处理,逻辑上不统一
  2. 每次插入都有判断节点必须为null的判断
  3. e.compareTo(node.e)进行了两次的操作才能插入
  4. 终止条件臃肿
  5. 空本身就是一颗二叉树,所以遇到空的情况直接插入即可

e.compareTo(node.e) >(<) 0 && node.right(left) == null 其实并没有递归到底。

/**
	 * 
	 * 
	 * @Title: add public 属性供外部调用
	 * 
	 * @Description: 向二分搜索树中添加新的元素e
	 * 
	 * @param e
	 * 
	 * @return: void
	 */
	public void add(E e) {
		root = add(root, e);
	}

	/**
	 * 
	 * 
	 * @Title: add 内部调用 私有 递归函数
	 * 
	 * @Description: 返回插入新节点后的二分搜索树的根
	 * 
	 * @param node
	 * @param e
	 * 
	 * @return: void
	 */
	private Node add(Node node, E e) {

		if (node == null) {
			size++;
			return new Node(e);
		}

		// 相等,不做任何操作
		if (e.compareTo(node.e) < 0) {
			node.left = add(node.left, e);
		} else if (e.compareTo(node.e) > 0) {
			node.right = add(node.right, e);
		}

		return node;
	}

查找 数据

/**
	 * 
	 * 
	 * @Title: contains 供外部调用
	 * 
	 * @Description: 判断元素e是否存二分搜索树中
	 * 
	 * @param e
	 * 
	 * @return: boolean
	 */
	public boolean contains(E e) {
		return contains(root, e);
	}

	/**
	 * 
	 * 
	 * @Title: contains 内部调用
	 * 
	 * @Description: 判断e是否存在于以node为根的二分搜索树中 递归算法
	 * 
	 * @param node
	 * @param e
	 * 
	 * @return: boolean
	 */
	private boolean contains(Node node, E e) {

		if (node == null) { // node为空 返回false
			return false;
		}

		if (e.compareTo(node.e) == 0) { // 相等 返回true
			return true;
		} else if (e.compareTo(node.e) < 0) { // 小, 继续递归左子树
			return contains(node.left, e);
		} else { // e.compareTo(node.e) > 0 大, 继续递归右子树
			return contains(node.right, e);
		}

	}

发布了819 篇原创文章 · 获赞 2030 · 访问量 415万+

猜你喜欢

转载自blog.csdn.net/yangshangwei/article/details/104214232