java实现手写二叉搜索树:提供了二叉搜索树的添加、删除、查找、遍历、反转、前驱、后继等常用接口

自定义二叉搜索树

一、各个接口实现思路

add添加接口

  • 1.实现思路
    • 1)若是第一个元素,则放在rott节点
    • 2)非第一个,则寻找到存放该元素的父节点。通过元素大小比较,确定在左支还是右支寻找,直到找到叶子节点则停止寻找
    • 3)根据比较的结果,确定新元素存放在父节点的左支或者右支
  • 2.实现代码


	public void add(E element) {
		elementCheckNotNull(element);
		// 添加第一个元素
		if (root == null) {
			root = new Node<>(element, null);
			size++;
			return;
		}
		// 寻找要添加节点的父节点
		Node<E> parent = root;
		Node<E> node = root;
		int cmp = 0;
		while (node != null) {
			cmp = compare(element, node.element);
			parent = node;
			if (cmp > 0) {
				node = node.right;
			} else if (cmp < 0) {
				node = node.left;
			} else {
				return;
			}
		}

		// 根据比较大小,确地添加在父节点的位置
		if (cmp < 0) {
			parent.left = new Node<E>(element, parent);
		} else {
			parent.right = new Node<E>(element, parent);
		}
		size++;
	}
	private int compare(E e1, E e2) {
		if (comparator != null) {
			return comparator.compare(e1, e2);
		}
		return ((Comparable<E>) e1).compareTo(e2);
	}

删除

  • 1.实现思路
    • 1)若该节点的度为2
      用先驱节点或后继节点覆盖原节点的元素值,然后删除相应的前驱或后继节点
      一个节点的度为2,则其前驱、后继节点的度只能是0或者1,这样就将度为2节点的删除转为度为0或1节点的删除
    • 2)若该节点的度为1
      删除该节点node,用该节点的子节点代替原来的位置
      • 若node为左子节点,则左子节点的父节点为node节点的父节点,并将node父节点的左子节点指向新的节点
      • 若node为右子节点,则右子节点的父节点为node节点的父节点,并将node父节点的右子节点指向新的节点
      • 若node为根节点,则将root指向子节点,并将子节点的parent设为null
    • 3)若该节点的度为0
      直接删除,并根据该节点是左、右子节点,更改父节点的左右指向,若parent为null,则该节点为root,直接将root置为null
  • 2.实现代码
	public void remove(E element) {
		remove(node(element));
	}

	private void remove(Node<E> node) {
		if (node == null)
			return;

		size--;

		if (node.hasTwoChildren()) { // 度为2的节点,找后继节点代替该位置,然后删除后继节点(node=s)
			// 找到后继节点
			Node<E> s = successor(node);
			// 用后继节点的值覆盖度为2的节点的值
			node.element = s.element;
			// 删除后继节点
			node = s;
		}

		// 删除node节点(node的度必然是1或者0)
		Node<E> replacement = node.left != null ? node.left : node.right;

		if (replacement != null) { // node是度为1的节点
			// 更改parent
			replacement.parent = node.parent;
			// 更改parent的left、right的指向
			if (node.parent == null) { // node是度为1的节点并且是根节点
				root = replacement;
			} else if (node == node.parent.left) {
				node.parent.left = replacement;
			} else { // node == node.parent.right
				node.parent.right = replacement;
			} // 下面删除度为0的叶子节点
		} else if (node.parent == null) { // node是叶子节点并且是根节点
			root = null;
		} else { // node是叶子节点,但不是根节点
			if (node == node.parent.left) {
				node.parent.left = null;
			} else { // node == node.parent.right
				node.parent.right = null;
			}
		}
	}

遍历

  • 1.输出元素的遍历
    • 1.前序
public void preorderTraversal() {
	preorderTraversal(root);
}

private void preorderTraversal(Node<E> node) {
	if (node == null)
		return;
	System.out.println(node.element);
	preorderTraversal(node.left);
	preorderTraversal(node.right);
}
  • 2.中序
public void inorderTraversal() {
	inorderTraversal(root);
}

private void inorderTraversal(Node<E> node) {
	if (node == null)
		return;
	inorderTraversal(node.left);
	System.out.println(node.element);
	inorderTraversal(node.right);
}
  • 3.后序
	public void postorderTraversal() {
	postorderTraversal(root);
}

private void postorderTraversal(Node<E> node) {
	if (node == null)
		return;
	postorderTraversal(node.left);
	postorderTraversal(node.right);
	System.out.println(node.element);
}
  • 4.层序
    层序遍历 (遍历时,先遍历的节点,在下一层其子节点也要先遍历,具有先进先出特点,故可用队列进行模拟) 先遍历该节点,然后再将该节点的左、右子树入列


		public void levelOrderTraversal() {
	
		if (root == null)
			return;
		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);

		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			System.out.println(node.element);

			if (node.left != null) {
				queue.offer(node.left);
			}

			if (node.right != null) {
				queue.offer(node.right);
			}
		}
	}


  • 2.带遍历逻辑的遍历

    • 1.前序
	public void preorder(Visitor<E> visitor) {
	if (visitor == null)
		return;
	preorder(root, visitor);
}

private void preorder(Node<E> node, Visitor<E> visitor) {
	if (node == null || visitor.stop)
		return;

	visitor.stop = visitor.visit(node.element); // 遍历后确定是否需要停止
	preorder(node.left, visitor);
	preorder(node.right, visitor);
}
  • 2.中序
	public void inorder(Visitor<E> visitor) {
	if (visitor == null)
		return;
	inorder(root, visitor);
}

private void inorder(Node<E> node, Visitor<E> visitor) {
	if (node == null || visitor.stop)
		return;

	inorder(node.left, visitor);
	if (visitor.stop)
		return; // 真正遍历当前节点前,需要判定是否需要遍历
	visitor.stop = visitor.visit(node.element);
	inorder(node.right, visitor);
}
  • 3.后序
public void postorder(Visitor<E> visitor) {
	if (visitor == null)
		return;
	postorder(root, visitor);
}

private void postorder(Node<E> node, Visitor<E> visitor) {
	if (node == null || visitor.stop)
		return;

	postorder(node.left, visitor);
	postorder(node.right, visitor);
	if (visitor.stop)
		return;// 真正遍历当前节点前,需要判定是否需要遍历
	visitor.stop = visitor.visit(node.element);
}
  • 4.层序
public void levelOrder(Visitor<E> visitor) {
		if (root == null || visitor == null)
			return;

		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);

		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			if (visitor.visit(node.element))
				return;

			if (node.left != null) {
				queue.offer(node.left);
			}

			if (node.right != null) {
				queue.offer(node.right);
			}
		}
	}

前驱节点

  • 1.实现思路
    • 1)若左子树非null,则找寻找其右节点,直至其右节点为null,此时该节点为前驱节点
    • 2)若左子树为null,则看其父节点
      • 若父节点非null,则一直找父节点且该节点为父节点的左 支,直至该节点在父节点的右子树中(为节点的右子树,说明是比节点大的数),此时的父节点即为前驱节点
      • 若父节点为null,则没有前驱节点,如根节点
  • 2.实现代码
private Node<E> predecessor(Node<E> node) {
		if (node == null)
			return null;

		// 前驱节点在左子树当中(left.right.right.right....)
		Node<E> p = node.left;
		if (p != null) {
			while (p.right != null) {
				p = p.right;
			}
			return p;
		}

		// 左子树为null时,从父节点、祖父节点中寻找前驱节点(当node为第一个父节点的right节点的该父节点)
		while (node.parent != null && node == node.parent.left) {
			node = node.parent;
		}

		// node.parent == null
		// node == node.parent.right
		return node.parent;
	}

后继节点

  • 1.实现思路
    • 1)若右子树非null,则找寻找其左节点,直至其左节点为null,此时该节点为前驱节点
    • 2)若右子树为null,则看其父节点
      • 若父节点非null,则一直找父节点且该节点为父节点的由 支,直至该节点在父节点的左子树中(为节点的左子树,说明是比节点小的数),此时的父节点即为前驱节点
      • 若父节点为null,则没有后继节点,如根节点(node.parent == null 此时可能node为根节点(当最初的node为最后一个节点时))
  • 2.实现代码
private Node<E> successor(Node<E> node) {
	if (node == null)
		return null;

	// 前驱节点在右子树当中(right.left.left.left....)
	Node<E> p = node.right;
	if (p != null) {
		while (p.left != null) {
			p = p.left;
		}
		return p;
	}

	// 从父节点、祖父节点中寻找前驱节点
	while (node.parent != null && node == node.parent.right) {
		node = node.parent;
	}
	
	// node.parent == null  此时可能node为根节点(当最初的node为最后一个节点时)
	// node == node.parent.right
	return node.parent;
}

计算高度

  • 1.利用递归关系
    一个节点的高度等于该节点左子树、右子树两者的高度最大值再加1
public int height2() {
		return height(root);
	}
	
	private int height(Node<E> node) {
		if (node == null) return 0;
		return 1 + Math.max(height(node.left), height(node.right));
	}
  • 2.利用层序遍历计算高度
    • 1)存储每一层的节点数size,每出列一个节点,size减1
    • 2)当size为0时,表示该层已经出列完,此时队列中的元素个数为下一层的节点数目,更新层的节点数目以及高度
      代码
public int height() {
	if (root == null) return 0;
	
	// 树的高度
	int height = 0;
	// 存储着每一层的元素数量
	int levelSize = 1;
	Queue<Node<E>> queue = new LinkedList<>();
	queue.offer(root);
	
	while (!queue.isEmpty()) {
		Node<E> node = queue.poll();
		levelSize--;
		
		if (node.left != null) {
			queue.offer(node.left);
		}
		
		if (node.right != null) {
			queue.offer(node.right);
		}

		if (levelSize == 0) { // 意味着即将要访问下一层,此时队列的元素就是下一层的节点数目
			levelSize = queue.size();
			height++;
		}
	}
	return height;
}

判断是否是完全二叉树

  • 1.实现思路
    • 1)利用层序遍历进行判断
    • 2)看左子树left
      left不为空,则入列
      left为空,且right不为空,则直接返回false
    • 3)看右子树right
      若right不为空,则入列
      若right为空,则表明后面的节点都是叶子节点,否则不是完全二叉树
  • 2.实现代码
public boolean isComplete() {
	if (root == null) return false;
	
	Queue<Node<E>> queue = new LinkedList<>();
	queue.offer(root);

	boolean leaf = false;
	while (!queue.isEmpty()) {
		Node<E> node = queue.poll();
		if (leaf && !node.isLeaf()) return false;
		
		if (node.left != null) {
			queue.offer(node.left);
		} else if (node.right != null) { // node.left == null && node.right != null
			return false;
		}
		
		if (node.right != null) {
			queue.offer(node.right);
		} else { // node.right == null
			leaf = true;
		}
	}
	
	return true;
}

查找某个节点

  • 1.实现思路
    从根节点开始寻找,根据节点的元素大小,确定在左支还是又支寻找,直到叶子节点,则结束
  • 2.代码
public boolean contains(E element) {
		return node(element) != null;
	}
	
	private Node<E> node(E element) {
		Node<E> node = root;
		while (node != null) {
			int cmp = compare(element, node.element);
			if (cmp == 0) return node;
			if (cmp > 0) {
				node = node.right;
			} else { // cmp < 0
				node = node.left;
			}
		}
		return null;
	}

比较元素大小的设计

  • 1.允许外界传入一个Comparator,自定义比较方案

      	private Comparator<E> comparator;
    
  • 2.如果没有传入比较器,则默认元素实现了Comparable接口

      private int compare(E e1, E e2) {
      		if (comparator != null) {
      			return comparator.compare(e1, e2);
      		}
      		return ((Comparable<E>)e1).compareTo(e2);
      	}
    

9.反转二叉树

遍历二叉树,将每个节点的左右子树互换即可
public Node<E> invertTree(Node<E> root) {
		if (root==null) {
			return null;
		}
        
		LinkedList<Node<E>> list = new LinkedList<Node<E>>();
		list.add(root);
		while (!list.isEmpty()) {
			Node<E> node = list.poll();
			Node<E> tmp=node.right;
			node.right=node.left;
			node.left=tmp;
			if (node.left!=null) {
				list.add(node.left);
			}
			if (node.right!=null) {
				list.add(node.right);
			}
		}
		
		return root;
    }

打印

@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		toString(root, sb, "");
		return sb.toString();
	}
	
	private void toString(Node<E> node, StringBuilder sb, String prefix) {
		if (node == null) return;

		toString(node.left, sb, prefix + "L---");
		sb.append(prefix).append(node.element).append("\n");
		toString(node.right, sb, prefix + "R---");
	}

二、完整代码如下:

package day06;

import java.util.Comparator;
import java.util.LinkedList;
import java.util.Queue;

import day06.Test.TreeNode;


public class BinarySearchTree<E> {

	private Node<E> root;
	private int size;
	private Comparator<E> comparator;

	public BinarySearchTree() {

	}

	public int size() {
		return size;
	}

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

	public void clear() {
		root = null;
		size = 0;
	}

	/**
	 * 添加节点
	 * 
	 * @param element
	 */
	public void add(E element) {
		elementCheckNotNull(element);
		// 添加第一个元素
		if (root == null) {
			root = new Node<>(element, null);
			size++;
			return;
		}
		// 寻找要添加节点的父节点
		Node<E> parent = root;
		Node<E> node = root;
		int cmp = 0;
		while (node != null) {
			cmp = compare(element, node.element);
			parent = node;
			if (cmp > 0) {
				node = node.right;
			} else if (cmp < 0) {
				node = node.left;
			} else {
				return;
			}
		}

		// 根据比较大小,确地添加在父节点的位置
		if (cmp < 0) {
			parent.left = new Node<E>(element, parent);
		} else {
			parent.right = new Node<E>(element, parent);
		}
		size++;
	}

	public void remove(E element) {
		remove(node(element));
	}

	private void remove(Node<E> node) {
		if (node == null)
			return;

		size--;

		if (node.hasTwoChildren()) { // 度为2的节点,找后继节点代替该位置,然后删除后继节点(node=s)
			// 找到后继节点
			Node<E> s = successor(node);
			// 用后继节点的值覆盖度为2的节点的值
			node.element = s.element;
			// 删除后继节点
			node = s;
		}

		// 删除node节点(node的度必然是1或者0)
		Node<E> replacement = node.left != null ? node.left : node.right;

		if (replacement != null) { // node是度为1的节点
			// 更改parent
			replacement.parent = node.parent;
			// 更改parent的left、right的指向
			if (node.parent == null) { // node是度为1的节点并且是根节点
				root = replacement;
			} else if (node == node.parent.left) {
				node.parent.left = replacement;
			} else { // node == node.parent.right
				node.parent.right = replacement;
			} // 下面删除度为0的叶子节点
		} else if (node.parent == null) { // node是叶子节点并且是根节点
			root = null;
		} else { // node是叶子节点,但不是根节点
			if (node == node.parent.left) {
				node.parent.left = null;
			} else { // node == node.parent.right
				node.parent.right = null;
			}
		}
	}

	private Node<E> node(E element) {
		Node<E> node = root;
		while (node != null) {
			int cmp = compare(element, node.element);
			if (cmp == 0)
				return node;
			if (cmp > 0) {
				node = node.right;
			} else { // cmp < 0
				node = node.left;
			}
		}
		return null;
	}

	/**
	 * 比较元素的大小
	 * 
	 * @param element
	 * @param element2
	 * @return
	 */
	private int compare(E e1, E e2) {
		if (comparator != null) {
			return comparator.compare(e1, e2);
		}
		return ((Comparable<E>) e1).compareTo(e2);
	}

	private void elementCheckNotNull(E element) {
		if (element == null) {
			throw new IllegalArgumentException("element must not be null");
		}

	}

	/**
	 * 前序遍历
	 */
	public void preorderTraversal() {
		preorderTraversal(root);
	}

	private void preorderTraversal(Node<E> node) {
		if (node == null)
			return;
		System.out.println(node.element);
		preorderTraversal(node.left);
		preorderTraversal(node.right);
	}

	/**
	 * 中序遍历
	 */
	public void inorderTraversal() {
		inorderTraversal(root);
	}

	private void inorderTraversal(Node<E> node) {
		if (node == null)
			return;
		inorderTraversal(node.left);
		System.out.println(node.element);
		inorderTraversal(node.right);
	}

	/**
	 * 后序遍历
	 */
	public void postorderTraversal() {
		postorderTraversal(root);
	}

	private void postorderTraversal(Node<E> node) {
		if (node == null)
			return;
		postorderTraversal(node.left);
		postorderTraversal(node.right);
		System.out.println(node.element);
	}

	/**
	 * 层序遍历 (遍历时,先遍历的节点,在下一层其子节点也要先遍历,具有先进先出特点,故可用队列进行模拟) 先遍历该节点,然后再将该节点的左、右子树入列
	 */
	public void levelOrderTraversal() {
		if (root == null)
			return;
		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);

		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			System.out.println(node.element);

			if (node.left != null) {
				queue.offer(node.left);
			}

			if (node.right != null) {
				queue.offer(node.right);
			}
		}
	}

	/**
	 * 带遍历逻辑的前序遍历
	 * 
	 * @param visitor
	 */
	public void preorder(Visitor<E> visitor) {
		if (visitor == null)
			return;
		preorder(root, visitor);
	}

	private void preorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop)
			return;

		visitor.stop = visitor.visit(node.element); // 遍历后确定是否需要停止
		preorder(node.left, visitor);
		preorder(node.right, visitor);
	}

	public void inorder(Visitor<E> visitor) {
		if (visitor == null)
			return;
		inorder(root, visitor);
	}

	private void inorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop)
			return;

		inorder(node.left, visitor);
		if (visitor.stop)
			return; // 真正遍历当前节点前,需要判定是否需要遍历
		visitor.stop = visitor.visit(node.element);
		inorder(node.right, visitor);
	}

	public void postorder(Visitor<E> visitor) {
		if (visitor == null)
			return;
		postorder(root, visitor);
	}

	private void postorder(Node<E> node, Visitor<E> visitor) {
		if (node == null || visitor.stop)
			return;

		postorder(node.left, visitor);
		postorder(node.right, visitor);
		if (visitor.stop)
			return;// 真正遍历当前节点前,需要判定是否需要遍历
		visitor.stop = visitor.visit(node.element);
	}

	public void levelOrder(Visitor<E> visitor) {
		if (root == null || visitor == null)
			return;

		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);

		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			if (visitor.visit(node.element))
				return;

			if (node.left != null) {
				queue.offer(node.left);
			}

			if (node.right != null) {
				queue.offer(node.right);
			}
		}
	}

	/**
	 * 前驱节点
	 * 
	 * @param node
	 * @return
	 */
	@SuppressWarnings("unused")
	private Node<E> predecessor(Node<E> node) {
		if (node == null)
			return null;

		// 前驱节点在左子树当中(left.right.right.right....)
		Node<E> p = node.left;
		if (p != null) {
			while (p.right != null) {
				p = p.right;
			}
			return p;
		}

		// 左子树为null时,从父节点、祖父节点中寻找前驱节点(当node为第一个父节点的right节点的该父节点)
		while (node.parent != null && node == node.parent.left) {
			node = node.parent;
		}

		// node.parent == null
		// node == node.parent.right
		return node.parent;
	}

	/**
	 * 后继节点
	 * 
	 * @param node
	 * @return
	 */
	private Node<E> successor(Node<E> node) {
		if (node == null)
			return null;

		// 前驱节点在右子树当中(right.left.left.left....)
		Node<E> p = node.right;
		if (p != null) {
			while (p.left != null) {
				p = p.left;
			}
			return p;
		}

		// 从父节点、祖父节点中寻找前驱节点
		while (node.parent != null && node == node.parent.right) {
			node = node.parent;
		}
		
		// node.parent == null  此时可能node为根节点(当最初的node为最后一个节点时)
		// node == node.parent.right
		return node.parent;
	}
	
	
	/**
	 * 	层次遍历,判断二叉树是否为完全二叉树
	 * @return
	 */
	public boolean isComplete() {
		if (root == null) return false;
		
		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);

		boolean leaf = false;
		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			if (leaf && !node.isLeaf()) return false;
			
			if (node.left != null) {
				queue.offer(node.left);
			} else if (node.right != null) { // node.left == null && node.right != null
				return false;
			}
			
			if (node.right != null) {
				queue.offer(node.right);
			} else { // node.right == null
				leaf = true;
			}
		}
		
		return true;
	}
	
	/**
	 * 利用层级遍历计算二叉树的高度
	 * @return
	 */
	public int height() {
		if (root == null) return 0;
		
		// 树的高度
		int height = 0;
		// 存储着每一层的元素数量
		int levelSize = 1;
		Queue<Node<E>> queue = new LinkedList<>();
		queue.offer(root);
		
		while (!queue.isEmpty()) {
			Node<E> node = queue.poll();
			levelSize--;
			
			if (node.left != null) {
				queue.offer(node.left);
			}
			
			if (node.right != null) {
				queue.offer(node.right);
			}

			if (levelSize == 0) { // 意味着即将要访问下一层,此时队列的元素就是下一层的节点数目
				levelSize = queue.size();
				height++;
			}
		}
		return height;
	}
	
	/**
	 * 通过递归计算二叉树的高度
	 * @return
	 */
	public int height2() {
		return height(root);
	}
	
	private int height(Node<E> node) {
		if (node == null) return 0;
		return 1 + Math.max(height(node.left), height(node.right));
	}
	
	public Node<E> invertTree(Node<E> root) {
		if (root==null) {
			return null;
		}
        
		LinkedList<Node<E>> list = new LinkedList<Node<E>>();
		list.add(root);
		while (!list.isEmpty()) {
			Node<E> node = list.poll();
			Node<E> tmp=node.right;
			node.right=node.left;
			node.left=tmp;
			if (node.left!=null) {
				list.add(node.left);
			}
			if (node.right!=null) {
				list.add(node.right);
			}
		}
		
		return root;
    }
	
	

	public static abstract class Visitor<E> {
		boolean stop;

		/**
		 * @return 如果返回true,就代表停止遍历
		 */
		abstract boolean visit(E element);
	}

	private static class Node<E> {
		// 存放元素,同时维护左、右节点及父节点
		E element;
		Node<E> left;
		Node<E> right;
		Node<E> parent;

		public Node(E element, Node<E> parent) {
			this.element = element;
			this.parent = parent;
		}

		public boolean isLeaf() {
			return left == null && right == null;
		}

		// 度为2的节点
		public boolean hasTwoChildren() {
			return left != null && right != null;
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值