二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为BST又被称为:二叉查找树、二叉排序树,任意一个节点的值都大于其左子树所有节点的值,任意一个节点的值都小于其右子树所有节点的值,它的左右子树也是一棵二叉搜索树。二叉搜索树可以大大提高搜索数据的效率,二叉搜索树存储的元素必须具备可比较性。
1.2 存储结构1、顺序存储
二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置,也就是数组的下标要能体现出之间的逻辑关系。
顺序存储结构在极端情况下浪费空间,只适合平衡树的存储。
2、链式存储
既然顺序表存储适用性性不强,则考虑链式存储结构,二叉树每个结点最多有两个孩子,所以为它设计一个数据域和两个指针域,这种链表也叫作二叉链表。
其中data是数据域,lchild和rchild都是指针域,分别存放指向左孩子和右孩子的指针。二叉链表示意图如下:
1.3 添加结点1、思路分析
如果当前二分搜索树一个元素都没有,添加一个新元素【43】,则当前元素为根结点。
二次添加新元素比如添加【23】和【59】,直接让它跟根节点进行比较,大于插入根节点右边,小于插入根节点左边。
依次添加元素
再次添加元素,添加【60】这个新节点,先跟根结点【41】比较,发现【60】比根结点大,插入根节点的右边,继续跟右边结点【59】比较,发现大于【59】,则插入到【59】的右边。
再次插入【28】这个新节点,先跟根结点【41】进行比较,发现小于根结点,往左边插入。一直比较,最终插入到结点【35】的左边。
1.4 删除结点删除二分搜索树最小值
所谓二分搜索树的最小值,一定是一颗二叉树从根节点开始,不停的向左走,直到走不动为止,那个最左的节点一定是最小值。
对于上面这颗二叉搜索树来说,删除最小节点,直接删除就好了。
特殊情况,此时二分搜索树最小节点是【23】,该节点存在叶子数。
此时只需要将【23】这个节点删除,然后将【23】整个右子树直接接上根结点的左子树就好了。
删除二分搜索树最大值
对于下面这颗二叉搜索树来说,删除最大节点【如果是叶子节点】,直接删除就好了。
删除叶子结点成功
如果不是叶子节点,存在左子树。先将结点【59】删除,然后将58的左子树整体当成41的右子树,接上即可。
删除二分搜索树任意节点
比如删除58这个节点,该节点左右都有孩子的节点,假设删除的结点为d。
先找到删除节点的右子树的相应最小值的结点,s = min(d->min),s 节点是d的后继,然后在d的右子树中,删除最小的值。
让S的右子树等于被删除节点的右子树,s->right = removeMin(d->right)。
最后让s的左子树等价于d的左子树,最后删除d,S是新的子树的根,s->left = d->left。
1.5 深度优先遍历深度优先遍历主要包括:先序遍历,中序遍历,后序遍历
1、先序遍历
规则是若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树。
2、中序遍历
规则是若树为空,则空操作返回,否则从根节点开始【注意并不是先访问根节点】,中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树。
3、后序遍历
规则是若树为空,则空操作返回,否则从根结点开始,后序遍历根节点的左子树,然后后序遍历右子树,最后访问根节点。
1.6 广度优先遍历1、层序遍历
规则是若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。
1.7 代码实现接口类:List
package cn.binarysearch.demo; public interface Listextends Iterable { void add(E element); void add(int index, E element) ; void remove(E element); E remove(int index); E get(int index); E set(int index, E element) ; int size(); int indexOf(E element) ; boolean contains(E element); boolean isEmpty(); void clear(); }
链表类实现:linkedList
package cn.binarysearch.demo; import cn.set.demo.List; import java.util.Iterator; // 链表 public class linkedListimplements List { // 创建Node节点 private class Node { // 数据域 E data; // 指向直接前驱的指针 Node prev; // 指向直接后继的指针 Node next; // 构造函数 public Node() { this(null, null, null); } public Node(E data) { this(data, null, null); } public Node(E data, Node prev, Node next) { this.data = data; this.prev = prev; this.next = next; } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (prev != null) { sb.append(prev.data); } else { sb.append("null"); } sb.append("->").append(data).append("->"); if (next != null) { sb.append(next.data); } else { sb.append("null"); } return sb.toString(); } } // 链表元素的数量 private int size; // 声明头结点 private Node head; // 声明尾节点 private Node tail; // 初始化头结点 public linkedList() { head = null; tail = null; size = 0; } public linkedList(E[] arr) { for (E e : arr) { add(e); } } //默认向表尾添加元素 @Override public void add(E element) { add(size, element); } //在链表当中指定索引index处添加一个元素 @Override public void add(int index, E element) { if (index < 0|| index > size) { throw new ArrayIndexOutOfBoundsException("add index out of bounds"); } // 创建新的结点对象 Node node = new Node(element); // 链表为空 if(isEmpty()){ head = node; tail = node; tail.next = head; head.prev = tail; }else if(index == 0){ // 在链表头部添加元素 // 头结点的上一跳指向新节点的上一跳 node.prev = head.prev; node.next = head; head.prev = node; head = node; tail.next = head; }else if(index == size){ // 在链表尾部添加元素 node.next = tail.next; tail.next = node; node.prev = tail; tail = node; head.prev = tail; }else{ // 在链表中添加元素 Node p,q; // 定义两个指针变量 if(index <= size / 2){ p = head; for(int i =0; i < index -1 ; i++){ p = p.next; } q = p.next; p.next = node; node.prev = p; q.prev = node; node.next = q; }else{ p = tail; for(int i=size -1; i > index; i--){ p = p.prev; } q = p.prev; q.next = node; node.prev = q; p.prev = node; node.next = p; } } size++; } //删除链表中指定的元素element @Override public void remove(E element) { int index = indexOf(element); if(index != -1){ remove(index); } } //删除链表中指定角标处index的元素 @Override public E remove(int index) { if (index < 0|| index > size) { throw new ArrayIndexOutOfBoundsException("remove index out of bounds"); } // 定义ret变量 E ret = null; Node node; // 当链表只剩一个元素 if(size ==1){ ret = head.data; head = null; tail = null; // 删除表头 }else if(index == 0){ ret = head.data; node = head.next; head.next = null; node.prev = head.prev; head.prev = null; head = node; tail.next = head; // 删除表尾 }else if(index == size -1){ ret = tail.data; node = tail.prev; tail.prev = null; node.next = tail.next; tail.next = null; tail = node; head.prev = tail; }else{ // 删除链表中间的某一个元素 Node p, q, r; if(index <= size / 2){ p = head; for(int i=0; i < index-1; i++){ p = p.next; } q = p.next; ret = q.data; r = q.next; p.next = r; r.prev = p; q.next = null; q.prev = null; }else{ p = tail; for(int i = size -1; i > index + 1; i--){ p = p.prev; } q = p.prev; ret = q.data; r = q.prev; r.next = p; p.prev = r; q.next = null; q.prev = null; } } size --; return ret; } //获取链表中指定索引处的元素 @Override public E get(int index) { if (index < 0|| index > size) { throw new ArrayIndexOutOfBoundsException("get index out of bounds"); } // 获取头 if(index == 0){ return head.data; }else if(index == size -1){ // 获取尾部 return tail.data; }else{ // 获取中间 Node p = head; for (int i = 0; i < index; i++) { p = p.next; } return p.data; } } // 修改链表中指定索引index的元素为element @Override public E set(int index, E element) { if (index < 0|| index > size) { throw new ArrayIndexOutOfBoundsException("set index out of bounds"); } E ret = null; // 获取头 if(index == 0){ // 修改头 ret = head.data; head.data = element; }else if(index == size -1){ // 修改尾部元素 ret = tail.data; tail.data = element; }else{ // 修改中间 Node p = head; for (int i = 0; i < index; i++) { p = p.next; } ret = p.data; p.data = element; } return ret; } @Override public int size() { return size; } //查找元素在链表中第一次出现的索引 @Override public int indexOf(E element) { if(isEmpty()){ return -1; } Node p = head; int index = 0; while (!p.data.equals(element)){ p = p.next; index++; if(p == head){ return -1; } } return index; } //在链表中判断是否包含元素element @Override public boolean contains(E element) { return indexOf(element) != -1; } @Override public boolean isEmpty() { return size== 0 && head == null && tail == null; } @Override public void clear() { head = null; tail = null; size = 0; } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append("size=").append(size).append(", ["); Node node = head; for (int i = 0; i < size; i++) { if (i != 0) { res.append(", "); } res.append(node); node = node.next; } res.append("]"); return res.toString(); } @Override public Iterator iterator() { return new DoubleCirclelinkedListIterator(); } class DoubleCirclelinkedListIterator implements Iterator { private Node cur = head; private boolean flag = true; @Override public boolean hasNext() { if(isEmpty()){ return false; } return flag; } @Override public E next() { E ret = cur.data; cur = cur.next; if(cur == head){ flag = false; } return ret; } } }
二分搜索树实现:BinarySearchTree
package cn.binarysearch.demo; import java.util.Iterator; import java.util.linkedList; import java.util.Queue; // 二分搜索树 public class BinarySearchTree> implements Iterable { // 二分搜索树结点定义 private class Node { // 数据域 public E e; // 左右指针域 public Node left, right; // 构造方法 public Node (E e){ this.e = e; left = null; right = null; } } // 二分搜索树的根节点的指针 private Node root; // 二分搜索树的元素的个数 private int size; // 创建一颗空的二分搜索树 public BinarySearchTree(){ root = null; size = 0; } // 获取二分搜索树的元素个数 public int size(){ return size; } // 判断二分搜索树是否为空 public boolean isEmpty(){ return size == 0 && root == null; } public void add(E e){ root = add(root, e); } private Node add(Node node, E e){ if(node == null){ size ++; return new Node(e); } if(e.compareTo(node.e) < 0){ // 以node左子树为根节点,再添加一个元素 node.left = add(node.left, e); }else if(e.compareTo(node.e) > 0){ // 以node右子树为根节点,再添加一个元素 node.right = add(node.right, e); } return node; } public boolean contains(E e){ return contains(root, e); } private boolean contains(Node node, E e){ if(node == null){ return false; } if(e.compareTo(node.e) == 0){ return true; } // 以当前节点的左孩子为根的左子树,去寻找元素e if(e.compareTo(node.e) < 0){ return contains(node.left, e); }else { // 以当前节点的右孩子为根的右子树,去寻找元素e return contains(node.right, e); } } // 前序遍历 public void preOrder(){ preOrder(root); } public void preOrder(Node node){ if(node == null){ return; } System.out.println(node.e); preOrder(node.left); preOrder(node.right); } // 前序遍历的非递归实现方式 public void preOrderNR(){ // 先访问根节点,然后前序遍历左子树,再前序遍历右子树 linkedList stack = new linkedList<>(); // 入栈根节点 stack.push(root); while (!stack.isEmpty()){ // 先弹出一个元素 Node cur = stack.pop(); System.out.println(cur.e); // 判断左右孩子是否为空,然后执行入栈操作 if(cur.right != null){ stack.push(cur.right); } if(cur.left != null){ stack.push(cur.left); } } } // 中序遍历 public void inOrder(){ inOrder(root); } private void inOrder(Node node){ if(node == null){ return; } inOrder(node.left); System.out.println(node.e); inOrder(node.right); } // 中序遍历非递归实现 public void inOrderNR(){ // 中序遍历根结点的左子树,然后是访问根结点,最后中序遍历右子树 linkedList stack = new linkedList<>(); // 创建pre指针 Node prev = root; // 将现将根节点和根节点的左边全部入栈 while (prev != null){ stack.push(prev); prev = prev.left; } while (!stack.isEmpty()){ // 先弹出元素 Node cur = stack.pop(); System.out.println(cur.e); // 判断该结点右边是否为空 if(cur.right != null){ // 如果右孩子不为空,继续执行入栈操作 prev = cur.right; while (prev != null){ // 将左孩子执行入栈操作 stack.push(prev); prev = prev.left; } } } } // 后序遍历 public void postOrder(){ postOrder(root); } private void postOrder(Node node){ if(node == null){ return; } postOrder(node.left); postOrder(node.right); System.out.println(node.e + " "); } // 后序遍历非递归实现 public void postOrderNR(){ if(root == null){ return; } // 申请两个栈s1,s2,头节点入栈s1 linkedList s1 = new linkedList<>(); linkedList s2 = new linkedList<>(); // 辅助栈,存储根 -> 右节点 ->左节点 的结果 // 先插入根节点 s1.push(root); // 如果栈s1不为空,执行以下操作:弹出一个元素,入栈s2 while (!s1.isEmpty()){ Node cur = s1.pop(); s2.push(cur); // 如果该节点左孩子不空入栈s1,如果该节点右孩子不空入栈s1. if(cur.left != null){ s1.push(cur.left); } if(cur.right != null){ s1.push(cur.right); } } // 将栈s2中的节点一次出栈,打印 while (!s2.isEmpty()){ Node cur = s2.pop(); System.out.println(cur.e + " "); } } // 层序遍历 public void levelOrder(){ // 创建队列 Queue queue = new linkedList<>(); // 先入队根节点 queue.offer(root); while (!queue.isEmpty()){ // 先出队一个元素 Node cur = queue.poll(); System.out.println(cur.e); // 分别入队左右子树 if(cur.left != null){ queue.offer(cur.left); } if(cur.right != null){ queue.offer(cur.right); } } } // 返回二分搜索树的最小值【迭代实现】 public E minimum2(){ if (isEmpty()){ throw new IllegalArgumentException("BST is empty!!!"); } // 定义指针prev指向根节点 Node prev = root; while (prev.left != null){ prev = prev.left; } return prev.e; } // 返回二分搜索树的最小值【递归实现】 public E minimum(){ if(isEmpty()){ throw new IllegalArgumentException("BST is empty!!!"); } Node minNode = minimum(root); return minNode.e; } private Node minimum(Node node){ if(node.left == null){ return node; } return minimum(node.left); } // 返回二分搜索树的最大值【递归实现】 public E maximum(){ if(isEmpty()){ throw new IllegalArgumentException("BST is empty!!!"); } Node maxNode = maximum(root); return maxNode.e; } private Node maximum(Node node){ if(node.right == null){ return node; } return maximum(node.right); } // 从二分搜索树中删除最小值所在节点, 返回最小值 public E removeMin(){ // 调用函数 E ret = minimum(); root = removeMin(root); return ret; } private Node removeMin(Node node){ // 向左走再也走不动了 if(node.left == null){ // 定义变量保存当前结点的右子树 Node rightNode = node.right; node.right = null; size --; return rightNode; } // 以node的左边为根节点,删除当前左子树的最小值,然后返回给左边接上 node.left = removeMin(node.left); return node; } // 从二分搜索树中删除最大值所在节点, 返回最大值 public E removeMax(){ // 调用函数 E ret = maximum(); root = removeMax(root); return ret; } private Node removeMax(Node node){ if(node.right == null){ // 定义变量保存当前结点的左子树 Node leftNode = node.left; node.left = null; size --; return leftNode; } // 以node的右边为根节点,删除当前右子树的最大值,然后返回给右边接上 node.right = removeMax(node.right); return node; } // 从二分搜索树中删除元素为e的节点 public void remove(E e){ root = remove(root, e); } private Node remove(Node node, E e){ if(node == null){ return null; } if(e.compareTo(node.e) < 0){ node.left = remove(node.left, e); return node; }else if(e.compareTo(node.e) > 0){ node.right = remove(node.right, e); return node; }else { // e.compareTo(node.e) == 0 // 如果左子树为空 if(node.left == null){ Node rightNode = node.right; // 置空操作 node.right = null; size --; return rightNode; } // 待删除节点右子树为空的情况 if(node.right == null){ Node leftNode = node.left; // 置空操作 node.left = null; size --; return leftNode; } // 待删除节点左右子树均不为空的情况 // 找到node结点的后继 Node successor = minimum(node.right); // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点 successor.right = removeMin(node.right); // 用这个节点顶替待删除节点的位置 successor.left = node.left; // 置空操作 node.left = node.right = null; return successor; } } @Override public String toString(){ StringBuilder sb = new StringBuilder(); // 前序遍历实现 generateBST(root, 0, sb); return sb.toString(); } // 生成以node为根节点,深度为depth的描述二叉树的字符串 private void generateBST(Node node, int depth, StringBuilder sb){ if(node == null){ sb.append(generateDepth(depth) + "nulln"); return; } // 当前结点存储的元素 sb.append(generateDepth(depth) + node.e + "n"); // 访问当前结点的左子树 generateBST(node.left, depth + 1, sb); // 访问当前结点的右子树 generateBST(node.right, depth + 1, sb); } private String generateDepth(int depth){ StringBuilder sb = new StringBuilder(); for(int i = 0 ; i < depth ; i ++) sb.append("--"); return sb.toString(); } @Override public Iterator iterator() { return new BinarySearchTreeIterator(); } private class BinarySearchTreeIterator implements Iterator { private linkedList list = new linkedList<>(); public BinarySearchTreeIterator(){ linkedList stack = new linkedList<>(); // 创建pre指针 Node prev = root; // 将现将根节点和根节点的左边全部入栈 while (prev != null){ stack.push(prev); prev = prev.left; } while (!stack.isEmpty()){ // 先弹出元素 Node cur = stack.pop(); list.add(cur.e); // 判断该结点右边是否为空 if(cur.right != null){ // 如果右孩子不为空,继续执行入栈操作 prev = cur.right; while (prev != null){ // 将左孩子执行入栈操作 stack.push(prev); prev = prev.left; } } } } @Override public boolean hasNext() { return !list.isEmpty(); } @Override public E next() { return list.removeFirst(); } } }
测试类:BinarySearchTreeDemo
package cn.binarysearch.demo;
public class BinarySearchTreeDemo {
public static void main(String[] args) {
BinarySearchTree tree = new BinarySearchTree<>();
// 向二叉树中添加节点
tree.add(6);
tree.add(9);
tree.add(1);
tree.add(3);
tree.add(11);
tree.add(5);
// 判断元素是否存在
System.out.println(tree.contains(11));
System.out.println(tree.contains(10));
// 调用前序遍历
System.out.println("===前序遍历【递归】===");
tree.preOrder();
System.out.println("===前序遍历【非递归】===");
tree.preOrderNR();
System.out.println("==========");
System.out.println(tree);
// 调用中序遍历
// 调用后序遍历
// 层序遍历
// 返回最小值
// 删除二叉搜索树中的最小值
// 中序遍历
// tree.inOrderNR();
//tree.remove(3);
//System.out.println(tree);
}
}



