二叉樹的基本算法

結構

class Node{
    int value;
    Node left;
    Node right;
    public Node(int value){
        this.value = value;
    }
}

二叉樹的遍歷

對于所有子樹來說,遞歸思想
理解:對于所有子樹來說,都要符合某個序遍歷的順序
先序:先頭結點->左子樹->右子樹

/**
     * 先序遍歷
     * @param head
     */
    public void preOrder(Node head){
        if(head == null){
            return;
        }
        System.out.print(head.value+"=>");
        preOrder(head.left);
        preOrder(head.right);
    }

中序:左子樹->頭結點->右子樹

/**
     * 中序遍歷
     * @param head
     */
    public void midOrder(Node head){
        if(head == null){
            return;
        }
        midOrder(head.left);
        System.out.print(head.value+"=>");
        midOrder(head.right);
    }

后序:左子樹->右子樹->頭結點

/**
     * 后序遍歷
     * @param head
     */
    public void posOrder(Node head){
        if(head == null){
            return;
        }
        posOrder(head.left);
        posOrder(head.right);
        System.out.print(head.value+"=>");
    }

遞歸序:根據(jù)遞歸邏輯遍歷了整個樹(每一個節(jié)點都會到達三次)
先序:第一次到達一個節(jié)點打印
中序:第二次到達一個節(jié)點
后序:第三次到達一個節(jié)點

image.png

如上圖(忽略里面的數(shù)字順序):

遞歸序,尋找節(jié)點為(本身節(jié)點->左子節(jié)點->右子節(jié)點)
A->B->D->G->G(G的左子節(jié)點未找到回到G)->G(G的右子節(jié)點未找到回到G)->D->H->H->H->D->B->B->A->C->E->E->I->I->I->E->C->F->F->F->C->A
每一個節(jié)點都到達了三次
先序遍歷(第一次到達節(jié)點打印即為)=A,B,D,G,H,C,E,I,F
中序遍歷(第二次到達節(jié)點打印)=G,D,H,B,A,E,I,C,F
后序遍歷(第三次到達節(jié)點打印)=G,H,D,B,I,E,F,C,A

非遞歸的方式實現(xiàn)先序,中序和后序
1.任何遞歸方式都可以改成非遞歸
2.自己設計壓棧的方式實現(xiàn)

1.先序

1.1利用壓棧方式
1.1彈出就打印
1.2.先右后左(彈出時即先左后右)

/**
 * 先序遍歷(壓棧)
     * @param head
     */
    public void preOrder(Node head){
        if(head == null){
            return;
        }
        Stack<Node> stack = new Stack<>();
        stack.push(head);
        while(!stack.isEmpty()){
            head = stack.pop();
            System.out.print(head.value + "=>");
            if(head.right != null){
                stack.push(head.right);
            }
            if(head.left != null){
                stack.push(head.left);
            }
        }
    }

實現(xiàn)(后序):
方式1:
1.彈出壓入另一個棧
2.先左后右
3.逆序返回另一個棧
理解:先序為根->右->左進棧,后序為左->右->根進棧,正好為先序棧的逆序情況

/**
     * 后序?qū)崿F(xiàn)1
     * @param head
     */
    public void after1(Node head){
        if(head == null){
            return;
        }
        Stack<Node> stack = new Stack<>();
        Stack<Node> rever = new Stack<>();
        stack.push(head);
        while(!stack.isEmpty()){
            head = stack.pop();
            rever.push(head);
            if(head.left != null){
                stack.push(head.left);
            }
            if(head.right != null){
                stack.push(head.right);
            }
        }
        while(!rever.isEmpty()){
            System.out.print(rever.pop().value + "=>");
        }
    }

方式2:
用兩個指針head,cur標記是否左右頭子樹被輸出過了
head指向上次被處理過的節(jié)點

public void after2(Node head){
        if(head == null){
            return;
        }
        Stack<Node> stack = new Stack<>();
        stack.push(head);
        Node cur = null;
        while(!stack.isEmpty()){
            cur = stack.peek();
            if(cur.left != null && head != cur.left && head != cur.right){
                stack.push(cur.left);
            }else if(cur.right != null && head != cur.right){
                stack.push(cur.right);
            }else{
                System.out.print(stack.pop().value + "=>");
                head = cur;
            }
        }
    }

實現(xiàn)(中序):
1.整條左邊界依次進棧
2.彈出再進入右節(jié)點
整棵樹可以被左邊界拆解,對于棧中數(shù)據(jù),都是先左后頭

/**
     * 中序遍歷
     * @param head
     */
    public void midOrder(Node head){
        if(head == null){
            return;
        }
        Stack<Node> stack = new Stack<>();
        Node cur = head;
        while(!stack.isEmpty() || cur != null){
            if(cur != null){
                stack.push(cur);
                cur = cur.left;
            }else{
                cur = stack.pop();
                System.out.print(cur.value + "=>");
                cur = cur.right;
            }
        }
    }

按層遍歷(寬度優(yōu)先遍歷)
隊列方式:一層一層加,從左到右

/**
     * 按層遍歷二叉樹
     * @param head
     */
    public void fl(Node head){
        if(head == null){
            return;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        while(!queue.isEmpty()){
            head = queue.poll();
            System.out.print(head.value + "=>");
            if(head.left != null){
                queue.add(head.left);
            }
            if(head.right != null){
                queue.add(head.right);
            }
        }
    }

題目:哪一層寬度最大,寬度為多少
解析:
方式1:用map的方式,key是Node,value是哪一層
2.變量哪一層,層寬度,全局最大寬度

/**
     * 求層最大寬度
     * @param head
     */
    public int maxWidth(Node head){
        if(head == null){
            throw new RuntimeException("二叉樹為空");
        }
        //層最大寬度
        int max = 0;
        //key=node,value=node所在的層
        Map<Node,Integer> map = new HashMap<>();
        map.put(head,1);
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        //當前所在哪一層
        int curLevel = 1;
        //當前層的最大寬度
        int curLevelWidth = 0;
        while(!queue.isEmpty()){
            head = queue.poll();
            //此節(jié)點在哪一層
            int curNodeLevel = map.get(head);
            if (head.left != null){
                map.put(head.left,curNodeLevel + 1);
                queue.add(head.left);
            }
            if (head.right != null){
                map.put(head.right,curNodeLevel + 1);
                queue.add(head.right);
            }
            //如果還在當前層,寬度就++
            if(curLevel == curNodeLevel){
                curLevelWidth++;
            }else{//如果不在當前層,說明將進入下一層,比較記錄當前層的最大寬度與整體最大寬度
                max = Math.max(curLevelWidth,max);
                curLevel++;
                curLevelWidth = 1;
            }
        }
        return max;
    }

方式二:1.當前層最右節(jié)點
2.下一層最右節(jié)點
3.每一個節(jié)點找下一層的最右節(jié)點

/**
     * 不使用map求層最大寬度
     * @param head
     */
    public int maxWidthNoMap(Node head){
        if(head == null){
            throw new RuntimeException("二叉樹不存在");
        }
        int max = 0;
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        //當前層最右節(jié)點
        Node curEnd = head;
        //下一層最右節(jié)點
        Node nextEnd = head;
        //層寬
        int curNodeWidth = 0;
        while(!queue.isEmpty()){
            Node cur = queue.poll();
            if (cur.left != null){
                queue.add(cur.left);
                nextEnd = cur.left;
            }
            if (cur.right != null){
                queue.add(cur.right);
                nextEnd = cur.right;
            }
            curNodeWidth++;
            //如果已經(jīng)到了當前層最右節(jié)點,則進行結算
            if (cur == curEnd){
                max = Math.max(max,curNodeWidth);
                //重置當前層最右節(jié)點到下一層最右節(jié)點
                curEnd = nextEnd;
                curNodeWidth = 0;
            }
        }
        return max;
    }

二叉樹的序列化和反序列化

1,可以用先序和后序遍歷,實現(xiàn)序列化(需要加空節(jié)點)
2.反序列化同理
3.中序方式則不行
如:

*         __2
*        /
*       1
*       和
*       1__
*          \
*           2
補足空位置的中序遍歷結果都是{ null, 1, null, 2, null}
public Queue<Node> serializble(Node head){
        if(head == null){
            return null;
        }
        Queue<Node> queue = new LinkedList<>();
        serialerByPreOrder(head,queue);
        return queue;
    }

    /**
     * 先序方式進行序列化二叉樹
     * @param head
     * @param queue
     */
    private void serialerByPreOrder(Node head, Queue<Node> queue) {
        if(head == null){
            queue.add(head);
            return;
        }
        queue.add(head);
        serialerByPreOrder(head.left,queue);
        serialerByPreOrder(head.right,queue);
    }

先序方式進行反序列化
后序方式進行反序列化,需將nodes列表中的節(jié)點放入棧中,用棧來實現(xiàn)序列化

/**
     * 先序方式進行反序列化
     * @param nodes
     * @return
     */
    public Node buildByPreOrder(Queue<Node> nodes){
        if (nodes == null || nodes.size() == 0){
            return null;
        }
        Node head = preb(nodes);
        return head;
    }

    private Node preb(Queue<Node> nodes) {
        Node poll = nodes.poll();
        if (poll == null){
            return null;
        }
        poll.left = preb(nodes);
        poll.right = preb(nodes);
        return poll;
    }

后序方式反序列化

public Node buildByPosOrder(Queue<Integer> nodes){
        if(nodes == null || nodes.size() == 0){
            return null;
        }
        //先序為 頭->左->右 放入棧中彈出即為 右->左->頭
        Stack<Integer> stack = new Stack<>();
        while(!nodes.isEmpty()){
            stack.add(nodes.poll());
        }
        Node head = prob(stack);
        return head;
    }

    private Node prob(Stack<Integer> stack) {
        Integer pop = stack.pop();
        if(pop == null){
            return null;
        }
        //彈出 右->左的順序,設置也先設置右,后設置左
        Node head = new Node(pop);
        head.right = prob(stack);
        head.left = prob(stack);
        return head;
    }

按層序列化

public Queue<Node> serializer(Node head){
        if(head == null){
            return null;
        }
        //用戶按層遍歷的隊列
        Queue<Node> queue = new LinkedList<>();
        //序列化后的隊列
        Queue<Node> nodes = new LinkedList<>();
        queue.add(head);
        while(!queue.isEmpty()){
            //從層隊列彈出放入序列化隊列
            Node poll = queue.poll();
            nodes.add(poll);
            //如果poll為null,說明沒有節(jié)點,繼續(xù)彈出
            while (poll == null && !queue.isEmpty()){
                poll = queue.poll();
                nodes.add(poll);
            }
            //隊列彈空還為null,說明到了最后一個節(jié)點,直接跳出
            if (poll == null){
                break;
            }
            queue.add(poll.left);
            queue.add(poll.right);
        }
        return nodes;
    }

按層進行反序列化

/**
     * 按層進行反序列化
     * @param levelList
     * @return
     */
    public static Node buildByLevelQueue(Queue<String> levelList) {
        if (levelList == null || levelList.size() == 0) {
            return null;
        }
        Node head = generateNode(levelList.poll());
        Queue<Node> queue = new LinkedList<Node>();
        if (head != null) {
            queue.add(head);
        }
        Node node = null;
        while (!queue.isEmpty()) {
            node = queue.poll();
            node.left = generateNode(levelList.poll());
            node.right = generateNode(levelList.poll());
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        return head;
    }

    public static Node generateNode(String val) {
        if (val == null) {
            return null;
        }
        return new Node(Integer.valueOf(val));
    }
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容