數據結構之二叉樹(二)

二叉樹的遍歷

關於二叉樹的遍歷,總共有四種方式:

  1. 先序遍歷
  2. 中序遍歷
  3. 後序遍歷
  4. 層序遍歷

其中前三種都屬於深度優先遍歷,最後一種屬於廣度優先遍歷,下面逐一講解。

先序遍歷

先序遍歷是先訪問二叉樹的根節點,再訪問二叉樹根結點的左子樹,最後訪問其右子樹。圖示如下:
在這裏插入圖片描述
代碼實現如下:

//先序遍歷,將遍歷的結點上的value保存在List中
    public List<V> preOrder() {
        List<Node> nodeList = new LinkedList<>();
        List<V> valueList = new LinkedList<>();
        preOrder(root,nodeList);
        for(Node node : nodeList) {
            valueList.add(node.value);
        }
        return valueList;
    }
    private void preOrder(Node node, List<Node> nodeList) {
        if(node == null) {
            return;
        }
        nodeList.add(node);
        preOrder(node.left, nodeList);
        preOrder(node.right, nodeList);
    }
先序遍歷非遞歸實現

另外,除了遞歸的方式,還可以借用棧這種數據結構進行先序遍歷。它的思路是怎樣的呢?它每訪問一個結點都將其入棧,然後訪問該結點的左子樹,當該結點的左子樹爲空時,利用棧回到剛纔訪問的結點,然後去訪問其右子樹。圖示如下:
在這裏插入圖片描述
代碼實現如下:

    //借用棧實現非遞歸先序遍歷
    private void preOrder2(Node node, List<Node> nodeList) {
        Stack<Node> stack = new Stack<>();
        while(node != null || !stack.isEmpty()) {
            if(node != null) {
                //訪問該結點併入棧
                nodeList.add(node);
                stack.push(node);
                //訪問其左子樹
                node = node.left;
            }else{
                //左子樹爲null或右子樹爲null,將棧頂元素出棧
                node = stack.pop();
                //訪問棧頂元素右子樹
                node = node.right;
            }
        }
    }
中序遍歷

中序遍歷的順序是先訪問左子樹,再訪問根結點,最後訪問其右子樹。

在這裏插入圖片描述
遞歸實現代碼如下:

//中序遍歷,將遍歷的結點保存在List中
    public List<V> inOrder() {
        List<V> valueList = new LinkedList<>();
        List<Node> nodeList = new LinkedList<>();
        inOrder(root, nodeList);
        for(Node node : nodeList) {
            valueList.add(node.value);
        }
        return valueList;
    }
    private void inOrder(Node node, List<Node> nodeList) {
        if(node == null) {
            return;
        }
        inOrder(node.left, nodeList);
        nodeList.add(node);
        inOrder(node.right, nodeList);
    }

注意這裏保存的value的值,而比較的是key值,所以不是順序的,這個根據實際需要調整。
下面也來實現一下非遞歸方式實現中序遍歷:
思路和前面一樣,也是利用棧這種數據結構。只不過這裏當遍歷到結點時直接把它入棧記錄訪問的順序,知道左子樹爲null時才通過出棧的方式來訪問它。
代碼實現如下:

    //非遞歸方式實現中序遍歷
    private void inOrder2(Node node,List<Node> nodeList) {
        Stack<Node> stack = new Stack<>();
        while(node != null || stack.size()!=0) {
            if(node != null) {
                stack.push(node);
                node = node.left;
            }else {
                node = stack.pop();
                nodeList.add(node);
                node = node.right;
            }
        }
    }
後序遍歷

後序遍歷訪問二叉樹結點的順序是先訪問樹的左子樹,再訪問樹的右子樹,最後才訪問樹的根結點。它也有遞歸方式和非遞歸方式實現。和前面兩種區別不大,只是順序不一樣。就不羅嗦了,直接上代碼:

//後序遍歷,將遍歷的結點保存在List中
    public List<V> postOrder() {
        List<V> valueList = new LinkedList<>();
        List<Node> nodeList = new LinkedList<>();
        postOrder2(root, nodeList);
        for(Node node : nodeList) {
            valueList.add(node.value);
        }
        return valueList;
    }
    private void postOrder(Node node, List<Node> nodeList) {
        if(node == null){
            return;
        }
        postOrder(node.left, nodeList);
        postOrder(node.right, nodeList);
        nodeList.add(node);
    }
    //後序遍歷的非遞歸實現
    public void postOrder2(Node node, List<Node> nodeList) {
        Stack<Node> stack = new Stack<>();
        Node prev = root; //記錄前面訪問過的一個結點
        while(node != null || stack.size()!=0) {
            if(node != null) {
                stack.push(node);
                node = node.left;
            }else {
                node = stack.peek().right;
                //沒有右子樹或之前訪問過
                if(node == null || node == prev) {
                    node = stack.pop();
                    nodeList.add(node);
                    prev = node;
                    node = null;
                }
            }
        }
    }
層序遍歷

層序遍歷的順序是這樣的,從樹的根結點開始,從上至下、再從左至右一次訪問樹上的結點。舉個例子:
在這裏插入圖片描述
代碼實現如下:

//層序遍歷,,將遍歷的結點保存在List中
    public List<V> levelOrder() {
        List<V> valueList  = new LinkedList<>();
        List<Node> nodeList = new LinkedList<>();
        levelOrder(root, nodeList);
        for(Node node : nodeList) {
            valueList.add(node.value);
        }
        return valueList;
    }
    private void levelOrder(Node node, List<Node> nodeList) {
        Queue<Node> nodes = new ArrayDeque<>();
        while(node != null) {
            nodeList.add(node);
            if(node.left != null){
                nodes.add(node.left);
            }
            if(node.right != null){
                nodes.add(node.right);
            }
            //隊列的頭部元素出隊
            if(nodes.size() > 0){
                node = nodes.remove();
            }else{
                node = null;
            }
        }
    }

最後貼上二叉樹的完整代碼。

import java.util.*;

public class BinaryTree<K extends Comparable<K>,V> {

    private class Node {
        K key;
        V value;
        Node left;
        Node right;
        int height;

        public Node(K key, V value) {
            this.key = key;
            this.value = value;
            this.left = null;
            this.right = null;
            //結點默認添加爲葉子結點,故高度爲1
            this.height = 1;
        }
    }

    private Node root;
    private int size;

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

    //獲取二叉樹樹中結點個數
    public int getSize() {
        return size;
    }

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

    //獲得結點的高度
    public int getHeight(Node node) {
        if(node == null){
            return 0;
        }
        return node.height;
    }

//    //判斷以node爲根的樹是否平衡
//    //平衡因子爲-1、0、1代表該樹平衡
//    public boolean isBalance(Node node) {
//        int balanceFactor = getBalanceFactor(node);
//        if(Math.abs(balanceFactor) > 1){
//            return false;
//        }
//        return isBalance(node.left) && isBalance(node.right);
//    }

//    //獲取node爲根的樹的平衡因子
//    private int getBalanceFactor(Node node) {
//        int l = getHeight(node.left);
//        int r = getHeight(node.right);
//        return l - r;
//    }

    //先序遍歷,將遍歷的結點上的value保存在List中
    public List<V> preOrder() {
        List<Node> nodeList = new LinkedList<>();
        List<V> valueList = new LinkedList<>();
        preOrder2(root,nodeList);
        for(Node node : nodeList) {
            valueList.add(node.value);
        }
        return valueList;
    }
    private void preOrder(Node node, List<Node> nodeList) {
        if(node == null) {
            return;
        }
        nodeList.add(node);
        preOrder(node.left, nodeList);
        preOrder(node.right, nodeList);
    }
    //借用棧實現非遞歸先序遍歷
    private void preOrder2(Node node, List<Node> nodeList) {
        Stack<Node> stack = new Stack<>();
        while(node != null || !stack.isEmpty()) {
            if(node != null) {
                //訪問該結點併入棧
                nodeList.add(node);
                stack.push(node);
                //訪問其左子樹
                node = node.left;
            }else{
                //左子樹爲null或右子樹爲null,將棧頂元素出棧
                node = stack.pop();
                //訪問棧頂元素右子樹
                node = node.right;
            }
        }
    }



    //中序遍歷,將遍歷的結點保存在List中
    public List<V> inOrder() {
        List<V> valueList = new LinkedList<>();
        List<Node> nodeList = new LinkedList<>();
        inOrder2(root, nodeList);
        for(Node node : nodeList) {
            valueList.add(node.value);
        }
        return valueList;
    }
    private void inOrder(Node node, List<Node> nodeList) {
        if(node == null) {
            return;
        }
        inOrder(node.left, nodeList);
        nodeList.add(node);
        inOrder(node.right, nodeList);
    }
    //非遞歸方式實現中序遍歷
    private void inOrder2(Node node,List<Node> nodeList) {
        Stack<Node> stack = new Stack<>();
        while(node != null || stack.size()!=0) {
            if(node != null) {
                stack.push(node);
                node = node.left;
            }else {
                node = stack.pop();
                nodeList.add(node);
                node = node.right;
            }
        }
    }

    //後序遍歷,將遍歷的結點保存在List中
    public List<V> postOrder() {
        List<V> valueList = new LinkedList<>();
        List<Node> nodeList = new LinkedList<>();
        postOrder2(root, nodeList);
        for(Node node : nodeList) {
            valueList.add(node.value);
        }
        return valueList;
    }
    private void postOrder(Node node, List<Node> nodeList) {
        if(node == null){
            return;
        }
        postOrder(node.left, nodeList);
        postOrder(node.right, nodeList);
        nodeList.add(node);
    }
    //後序遍歷的非遞歸實現
    public void postOrder2(Node node, List<Node> nodeList) {
        Stack<Node> stack = new Stack<>();
        Node prev = root; //記錄前面訪問過的一個結點
        while(node != null || stack.size()!=0) {
            if(node != null) {
                stack.push(node);
                node = node.left;
            }else {
                node = stack.peek().right;
                //沒有右子樹或之前訪問過
                if(node == null || node == prev) {
                    node = stack.pop();
                    nodeList.add(node);
                    prev = node;
                    node = null;
                }
            }
        }
    }

    //層序遍歷,,將遍歷的結點保存在List中
    public List<V> levelOrder() {
        List<V> valueList  = new LinkedList<>();
        List<Node> nodeList = new LinkedList<>();
        levelOrder(root, nodeList);
        for(Node node : nodeList) {
            valueList.add(node.value);
        }
        return valueList;
    }
    private void levelOrder(Node node, List<Node> nodeList) {
        Queue<Node> nodes = new ArrayDeque<>();
        while(node != null) {
            nodeList.add(node);
            if(node.left != null){
                nodes.add(node.left);
            }
            if(node.right != null){
                nodes.add(node.right);
            }
            //隊列的頭部元素出隊
            if(nodes.size() > 0){
                node = nodes.remove();
            }else{
                node = null;
            }
        }
    }

    //在以node爲根的樹上尋找key的結點
    private Node findNode(Node node, K key) {
        if(node == null){
            return null;
        }
        if(node.key.compareTo(key) == 0) {
            return node;
        }else if(node.key.compareTo(key) > 0) {
            return findNode(node.left, key);
        }else{
            return findNode(node.right, key);
        }
    }

    //是否包含某個鍵值
    public boolean contains(K key) {
        return findNode(root, key) != null;
    }

    //獲取key的結點的value
    public V get(K key) {
        Node node = findNode(root, key);
        if(node != null){
            return node.value;
        }
        return null;
    }

    //是否包含某個V

    public boolean containsValue(V value) {
        List<V> valueList = inOrder();
        for(V v : valueList) {
            if(v.equals(value)) {
                return true;
            }
        }
        return false;
    }

    //獲取以node爲根,key爲鍵的樹上的結點
    private Node getNode(Node node, K key) {
        return findNode(node, key);
    }

    //設置key的結點的值
    public void set(K key, V value) {
        Node node = findNode(root, key);
        if(node != null) {
            node.value = value;
        }else{
            throw new IllegalStateException("Node doesn't exist.");
        }
    }

    //找到以node爲根結點的樹上的最大結點
    private Node findMax(Node node) {
        if(node == null) {
            return null;
        }
        while(node.right != null) {
            node = node.right;
        }
        return node;
    }

    //找到以node爲根結點的樹上的最小結點
    private Node findMin(Node node) {
        if(node == null){
            return null;
        }
        while(node.left != null){
            node = node.left;
        }
        return node;
    }

    //刪除以node爲根的樹上的最小結點,返回該樹的根結點
    public Node removeMin(Node node){
        if(node.left == null) {
            //此時node爲最小結點
            Node rightNode = node.right;
            node.right = null;
            size --;
            return rightNode;
        }
        node.left = removeMin(node.left);
        return node;
    }

    //添加結點
    public void add(K key, V value) {
        root = add(root, key, value);
    }
    private Node add(Node node, K key, V value) {
        if(node == null) { //找到了合適的位置,直接添加
            node = new Node(key,value);
            size ++;
            //維護結點的高度
            node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1;
        }
        if(node.key.compareTo(key) > 0) {
            //比當前根結點小,遞歸添加到其左子樹上
            node.left = add(node.left, key, value);
        }else if(node.key.compareTo(key) < 0){
            //比當前結點大,遞歸添加到其右子樹上
            node.right = add(node.right, key, value);
        }else {
            node.value = value;
        }
        return node;
    }


    //刪除指定的結點
    public V remove(K key) {
        Node node = getNode(root, key);
        if(node != null) {
            return node.value;
        }
        return null;
    }
    private Node remove(Node node, K key) {
        if(node == null) {
            return null;
        }
        if(node.key.compareTo(key) > 0) {
            node.left = remove(node.left, key);
            return node;
        }else if(node.key.compareTo(key) < 0) {
            node.right = remove(node.right, key);
            return node;
        }else { //找到了要刪除的結點
            if(node.left == null) { //沒有左孩子或者沒有孩子結點
                Node rightNode = node.right;
                size --;
                node.right = null;
                return rightNode;
            }else if(node.right == null) { //沒有右孩子
                Node leftNode = node.left;
                size --;
                node.left = null;
                return leftNode;
            }else {
                Node successor = findMin(node.right);
                successor.right = removeMin(node.right);
                successor.left = node.left;
                return successor;
            }
        }
    }

    public static void main(String[] args) {
        BinaryTree<Integer,Integer> binaryTree = new BinaryTree<>();
        Random random = new Random();
        Integer[] keys = new Integer[10];
        for(int i=0;i<10;i++){
            keys[i] = random.nextInt(25);
            binaryTree.add(keys[i],random.nextInt(100));
        }

        List<Integer> list = binaryTree.levelOrder();
        for(Integer i : list) {
            System.out.print(i+" ");
        }
    }
}

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