二分搜索樹(Binary Search Tree)的遞歸與非遞歸實現

性質

  • 本質上是一顆二叉樹
  • 二分搜索樹的每個節點的值大於其左子樹的所有節點的值,小於其右子樹的所有節點的值。(存儲的元素必須要有可比性 )
  • 每一顆子樹也都是二分搜索樹
  • Talk is cheap. Show you the code.

添加元素

根據二分搜索樹的性質,判斷被添加元素需要放置的位置,遞歸直到可放置位置(node爲null)時增加節點。

public void add(E e) {
        if (root == null) {
            root = new Node(e);
            size++;
        } else {
            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.leftChild = add(node.leftChild, e);
        } else if (e.compareTo(node.e) > 0) {
            node.rightChild = add(node.rightChild, e);
        }
        return root;
    }

刪除元素

刪除最小值

算法步驟如下:
查找節點
刪除節點
遞歸完成

public E removeMin() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty");
        }
        E e = miniElement();//多餘步驟,爲了方便測試正確性
        root = removeMin(root);
        return e;
    }

    private Node removeMin(Node root) {
        if (root.leftChild == null) {
            size--;
            return root.rightChild;
        }
        root.leftChild = removeMin(root.leftChild);
        return root;
    }

刪除最大值

方法跟刪除最小值一樣,只要把left、right互換一下就行了。

刪除任意值

這裏用到了hibbard deletion方法,依然是使用遞歸方法,找到需要刪除的元素,將其左子樹的最大值節點或右子樹最小節點找出頂替需要被刪除的元素,我這裏選擇了右子樹的最小節點。

public void remove(E e) {

	root = remove(root, e);
}

private Node remove(Node node, E e) {

	if (node == null) {
	    return null;
	}
	int cmp = e.compareTo(node.e);
	if (cmp > 0) {
	    node.rightChild = remove(node.rightChild, e);
	} else if (cmp < 0) {
	    node.leftChild = remove(node.leftChild, e);
	} else {
		if (node.rightChild == null) {
		   return node.leftChild;
		}
		if (node.leftChild == null) {
		   return node.rightChild;
		}
		Node tmp = node;
		node = mini(tmp.rightChild);
		node.rightChild = removeMin(tmp.rightChild);
		node.leftChild = tmp.leftChild;
   }

   return node;
}

遍歷的遞歸實現

前序遍歷 中序遍歷 後序遍歷

使用遞歸方式遍歷,前中後的遍歷代碼幾乎一樣,只有節點數據記錄位置不同,如下。

  public void preOrder() {
        preOrder(root);
    }

    private void preOrder(Node root) {
        if (root == null) {
            return;
        }
        //在這個位置接收節點數據爲前序遍歷,每次訪問根節點的時候記錄
        preOrder(root.leftChild);
        //在這個位置接收節點數據爲中序遍歷,每次訪問先記錄下左節點
        preOrder(root.rightChild);
        //在這個位置接收節點數據爲後序遍歷,每次訪問先記錄下左右節點
    }

遍歷的非遞歸實現

NR前序遍歷

前序遍歷的規則:
(1)訪問根節點(2)前序遍歷左子樹(3)前序遍歷右子樹
前序遍歷可以說是最簡單的了,利用棧結構的先進後出特性,將根節點入棧,隨後進入循環開始pop出元素,將該元素的左右節點全部添加入棧,然後依次出棧最新入棧的元素去添加下一個節點

 public void preOrderNonRecursion() {
 
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        
        while (!stack.isEmpty()) {
            Node curr = stack.pop();
            System.out.print(curr.e + " ");
            
            if (curr.rightChild != null) {
                stack.push(curr.rightChild);
            }
            if (curr.leftChild != null) {
                stack.push(curr.leftChild);
            }
        }
 }

NR中序遍歷

首先依次將當前節點所有的左節點加入棧中,直到下一個左節點爲null,則當前節點爲需要遍歷的第一個節點,然後依次從棧中彈出已經加入的左節點,並且把該節點的右節點給設爲當前節點,如果右節點不存在則繼續彈出上一次添加的左節點,如果右節點存在,則重複本步驟。

    public void inOrderNotRecursion() {
        Node curr = root;
        Stack<Node> stack = new Stack<>();
        while (!stack.isEmpty() || curr != null) {

            if (curr != null) {
                stack.push(curr);
                curr = curr.leftChild;
            } else {
                curr = stack.pop();
                System.out.print(curr.e + " ");
                curr = curr.rightChild;
            }
        }
    }

NR後序遍歷

後序遍歷跟前序遍歷正好相反,前序遍歷是根左右,後序遍歷是左右根。只要將左右子節點的入棧順序更改一下就能得到逆序的後序遍歷結果,因此增加一個棧用來存儲結果,就能得到最終的後序遍歷序列。

    public void postOrderNotRecursion() {
        Stack<Node> stack = new Stack<>();
        Stack<Node> out = new Stack<>();
        Node curr = root;
        stack.push(curr);
        while (!stack.isEmpty()) {
            curr = stack.pop();
//            System.out.print(curr.e + ",");
            out.push(curr);
            if (curr.leftChild != null) {
                stack.push(curr.leftChild);
            }
            if (curr.rightChild != null) {
                stack.push(curr.rightChild);
            }
        }
        while (!out.isEmpty()) {
            System.out.print(out.pop().e + " ");
        }
    }

層序遍歷

層序遍歷是一個典型的廣度優先遍歷。
層序遍歷可以維護一個隊列來進行輔助遍歷,首先將根節點入隊,然後出隊,將出隊的元素的左右子節點依次入隊,重複上述步驟即可遍歷成功。

    public void layerTraverse() {
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            Node curr = queue.remove();
            System.out.print(curr.e + " ");
            if (curr.leftChild != null) {
                queue.add(curr.leftChild);
            }
            if (curr.rightChild != null) {
                queue.add(curr.rightChild);
            }
        }


    }

完整代碼

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

/**
*該數據結構存儲內容必須具備可比性,因此使用<E extends Comparable<E>>
*/
public class BST<E extends Comparable<E>> {
 
 	/**
 	*存儲節點
 	*/
    private class Node {
        public E e;
        public Node leftChild, rightChild;

        public Node(E e) {
            this.e = e;
            leftChild = null;
            rightChild = null;
        }
 
    }

	//樹根
    private Node root;
    private int size;

    public BST() {
        root = null;
        size = 0;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return root == null;
    }

    public void add(E e) {
        if (root == null) {
            root = new Node(e);
            size++;
        } else {
            root = add(root, e);
        }
    }

    //以root爲根的二分搜索樹中插入元素E,遞歸算法
    //返回插入新節點後二分搜索樹的根
    private Node add(Node node, E e) {
 
        if (node == null) {
            size++;
            return new Node(e);
        }
        if (e.compareTo(node.e) < 0) {
            node.leftChild = add(node.leftChild, e);
        } else if (e.compareTo(node.e) > 0) {
            node.rightChild = add(node.rightChild, e);
        }
        return node;
    }

    public boolean contains(E e) {
        return contains(root, e);
    }

    private boolean contains(Node root, E e) {
        if (root == null) {
            return false;
        }
        if (e.compareTo(root.e) > 0) {
            return contains(root.rightChild, e);
        } else if (e.compareTo(root.e) < 0) {
            return contains(root.leftChild, e);
        } else {
            return true;
        }
    }

    /**
     * 前序遍歷
     * Pre-Order Traversal
     */
    public void preOrder() {
        preOrder(root);
    }

    private void preOrder(Node root) {
        if (root == null) {
            return;
        }
        System.out.print(root.e + " ");
        preOrder(root.leftChild);
        preOrder(root.rightChild);
    }

    /**
     * 前序遍歷  非遞歸實現
     */
    public void preOrderNonRecursion() {
        Stack<Node> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()) {
            Node curr = stack.pop();
            System.out.print(curr.e + " ");

            if (curr.rightChild != null) {
                stack.push(curr.rightChild);
            }
            if (curr.leftChild != null) {
                stack.push(curr.leftChild);
            }
        }
    }

    /**
     * 中序遍歷
     * 根據二叉樹性質中序遍歷結果爲排序好的順序
     * In-Order Traversal
     */
    public void inOrder() {
        inOrder(root);
    }

    private void inOrder(Node node) {
        if (node == null) {
            return;
        }
        inOrder(node.leftChild);
        System.out.print(node.e + " ");
        inOrder(node.rightChild);
    }
	 /**
     * 中序遍歷
     * 非遞歸
     */
    public void inOrderNotRecursion() {
        Node curr = root;
        Stack<Node> stack = new Stack<>();
        while (!stack.isEmpty() || curr != null) {

            if (curr != null) {
                stack.push(curr);
                curr = curr.leftChild;
            } else {
                curr = stack.pop();
                System.out.print(curr.e + " ");
                curr = curr.rightChild;
            }
        }
    }

    /**
     * 後續遍歷
     * Post-Order Traversal
     */
    public void postOrder() {
        postOrder(root);
    }

    private void postOrder(Node node) {
        if (node == null) {
            return;
        }
        postOrder(node.leftChild);
        postOrder(node.rightChild);
        System.out.print(node.e + " ");
    }

    /**
     * 後序遍歷 非遞歸
     */
    public void postOrderNotRecursion() {
        Stack<Node> stack = new Stack<>();
        Stack<Node> out = new Stack<>();
        Node curr = null;
        stack.push(root);
        while (!stack.isEmpty()) {
            curr = stack.pop();
//            System.out.print(curr.e + ",");
            out.push(curr);
            if (curr.leftChild != null) {
                stack.push(curr.leftChild);
            }
            if (curr.rightChild != null) {
                stack.push(curr.rightChild);
            }
        }
        while (!out.isEmpty()) {
            System.out.print(out.pop().e + " ");
        }
    }

    /**
     * 層序遍歷
     */
    public void layerTraverse() {
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()) {
            Node curr = queue.remove();
            System.out.print(curr.e + " ");
            if (curr.leftChild != null) {
                queue.add(curr.leftChild);
            }
            if (curr.rightChild != null) {
                queue.add(curr.rightChild);
            }
        }
    }

    /**
     * 查找最小值
     */
    public E miniElement() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty.");
        }
        return mini(root).e;
    }

    private Node mini(Node node) {
        if (node.leftChild == null) {
            return node;
        }
        return mini(node.leftChild);
    }

    /**
     * 查找最大值
     */
    public E maxElement() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty.");
        }
        return max(root).e;
    }

    private Node max(Node node) {
        if (node.rightChild == null) {
            return node;
        }
        return max(node.rightChild);
    }

    /**
     * 刪除最小值
     */
    public E removeMin() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty");
        }
        E e = miniElement();
        root = removeMin(root);
        return e;
    }
   /**
     * 刪除最小值  
     * @param root 需要刪除的樹根
     * @return 被刪除樹樹根
     */
    private Node removeMin(Node root) {
        if (root.leftChild == null) {
            size--;
            return root.rightChild;
        }
        root.leftChild = removeMin(root.leftChild);
        return root;
    }

    public E removeMax() {
        if (isEmpty()) {
            throw new IllegalArgumentException("empty");
        }
        E e = maxElement();
        root = removeMax(root);
        return e;
    }

    //刪除以root爲根節點的樹種最小的元素,返回新的樹根
    private Node removeMax(Node root) {
        if (root.rightChild == null) {
            size--;
            return root.leftChild;
        }
        root.rightChild = removeMax(root.rightChild);

        return root;
    }

    /**
     * 刪除任意一個節點
     */
    public void remove(E e) {

        root = remove(root, e);

    }

    private Node remove(Node node, E e) {

        if (node == null) {
            return null;
        }
        int cmp = e.compareTo(node.e);
        if (cmp > 0) {
            node.rightChild = remove(node.rightChild, e);
        } else if (cmp < 0) {
            node.leftChild = remove(node.leftChild, e);
        } else {
            if (node.rightChild == null) {
                return node.leftChild;
            }
            if (node.leftChild == null) {
                return node.rightChild;
            }
            Node tmp = node;
            node = mini(tmp.rightChild);
            node.rightChild = removeMin(tmp.rightChild);
            node.leftChild = tmp.leftChild;

        }

        return node;
    }

擴展鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章