1. 程式人生 > >資料結構與演算法簡記:通過前序中序或中序後序構建二叉樹

資料結構與演算法簡記:通過前序中序或中序後序構建二叉樹

上次記錄了廣義表生成二叉樹的過程,我們也可以通過前序和中序,或者中序和後序,來確定和構建一棵唯一的二叉樹。

還是同樣的圖,它的前序,中序,後序遍歷序列分別是:

pre: ABCDEF
in: CBDAEF
post: CDBFEA

以下是通過前序和中序構建二叉樹的過程:

  1. 獲取前序字串的第一個字元A,它作為當前根節點,然後掃描中序字串,找到A的位置,建立根節點儲存結構。
  2. 然後在中序字串中確定左子樹中序為CBD,再去前序字串中擷取相同長度的子串,確定左子樹前序為BCD,有了左子樹的前序和中序,就可以遞迴呼叫建立左子樹,跟當前根節點關聯起來。
  3. 同樣地,我們可以確定右子樹的前序為EF,中序為EF,然後遞迴呼叫建立右子樹,跟當前根節點關聯起來。
  4. 整個過程是遞迴的,直到前序和中序字串只有一個字元時,直接建立葉子節點並跟父節點關聯起來。

通過中序和後序構建二叉樹跟上述過程類似,由後序確定當前根節點後,再通過中序確定左子樹和右子樹序列,然後遞迴建立左子樹和右子樹,並與當前根節點關聯。

下面是實現程式碼:

JS版:

//二叉樹節點結構
function BinTreeNode(data) {
  this.data = data;
  this.leftChild = null;
  this.rightChild = null;
}

//根據前序和中序建立二叉樹
function createBinTreeByPreIn(preOrder, inOrder)
{
//如果只剩一個字元,則直接建立節點並返回 if (preOrder.length === 1) { return new BinTreeNode(preOrder.charAt(0)); } //從前序獲取當前根節點字元 var c = preOrder.charAt(0); //中序索引和前序節點個數 var i = 0; var number = preOrder.length; //遍歷中序序列,直到發現當前根節點 while (i < number && inOrder.charAt(i) != c) i++; //求出左子樹和右子樹節點個數
var leftNumber = i; var rightNumber = number - i - 1; //建立當前根節點 var node = new BinTreeNode(c); //建立左子樹 if (leftNumber >= 1) { var leftPre = preOrder.substring(1, 1 + leftNumber); var leftIn = inOrder.substring(0, leftNumber); node.leftChild = createBinTreeByPreIn(leftPre, leftIn); } //建立右子樹 if (rightNumber >= 1) { var rightPre = preOrder.substring(leftNumber + 1); var rightIn = inOrder.substring(leftNumber + 1); node.rightChild = createBinTreeByPreIn(rightPre, rightIn); } //返回當前根節點 return node; } //根據中序和後序建立二叉樹 function createBinTreeByInPost(inOrder, postOrder) { //如果只剩一個字元,則直接建立節點並返回 if (postOrder.length === 1) { return new BinTreeNode(postOrder.charAt(0)); } //從後序獲取當前根節點字元 var c = postOrder.charAt(postOrder.length - 1); //中序索引和前序節點個數 var i = 0; var number = postOrder.length; //遍歷中序序列,直到發現當前根節點 while (i < number && inOrder.charAt(i) != c) i++; //求出左子樹和右子樹節點個數 var leftNumber = i; var rightNumber = number - i - 1; //建立當前根節點 var node = new BinTreeNode(c); //建立左子樹 if (leftNumber >= 1) { var leftIn = inOrder.substring(0, leftNumber); var leftPost = postOrder.substring(0, leftNumber); node.leftChild = createBinTreeByInPost(leftIn, leftPost); } //建立右子樹 if (rightNumber >= 1) { var rightIn = inOrder.substring(leftNumber + 1); var rightPost = postOrder.substring(leftNumber, postOrder.length - 1); node.rightChild = createBinTreeByInPost(rightIn, rightPost); } //返回當前根節點 return node; } //前序遍歷 function preOrderTraverse(node, orderArray) { if (node) { orderArray.push(node.data); preOrderTraverse(node.leftChild, orderArray); preOrderTraverse(node.rightChild, orderArray); } } //後序遍歷 function postOrderTraverse(node, orderArray) { if (node) { postOrderTraverse(node.leftChild, orderArray); postOrderTraverse(node.rightChild, orderArray); orderArray.push(node.data); } } //前序,中序,後序 var preOrder = 'ABCDEF'; var inOrder = 'CBDAEF'; var postOrder = 'CDBFEA'; //根據前序和中序建立二叉樹 var binTree = createBinTreeByPreIn(preOrder, inOrder); //用於存放節點遍歷序列 var orderArray = []; //後序遍歷驗證其正確性 postOrderTraverse(binTree, orderArray); console.log('post order: ', orderArray.join(' ')); //清空遍歷序列陣列 orderArray.length = 0; //根據中序和後序建立二叉樹 binTree = createBinTreeByInPost(inOrder, postOrder); //前序遍歷驗證其正確性 preOrderTraverse(binTree, orderArray); console.log('pre order: ', orderArray.join(' '));

Java版:

//BinTreeNode.java

package algorithm;

//二叉樹節點結構
public class BinTreeNode {
    private char data;
    private BinTreeNode leftChild;
    private BinTreeNode rightChild;

    public BinTreeNode(char data) {
        this.data = data;
    }

    public char getData() {
        return data;
    }

    public void setData(char data) {
        this.data = data;
    }

    public BinTreeNode getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(BinTreeNode leftChild) {
        this.leftChild = leftChild;
    }

    public BinTreeNode getRightChild() {
        return rightChild;
    }

    public void setRightChild(BinTreeNode rightChild) {
        this.rightChild = rightChild;
    }
}

//BinTreeCreator.java

package algorithm;

public class BinTreeCreator {

    //根據前序和中序建立二叉樹
    public static BinTreeNode createBinTreeByPreIn(String pre, String in) {
        if (pre.length() == 1) {
            return new BinTreeNode(pre.charAt(0));
        }

        char c = pre.charAt(0);

        int i = 0;
        int number = pre.length();

        while (i < number && in.charAt(i) != c) i++;

        int leftNumber = i;
        int rightNumber = number - i - 1;

        BinTreeNode node = new BinTreeNode(c);

        if (leftNumber >= 1) {
            String leftPre = pre.substring(1, 1 + leftNumber);
            String leftIn = in.substring(0, leftNumber);

            node.setLeftChild(createBinTreeByPreIn(leftPre, leftIn));
        }

        if (rightNumber >= 1) {
            String rightPre = pre.substring(leftNumber + 1);
            String rightIn = in.substring(leftNumber + 1);

            node.setRightChild(createBinTreeByPreIn(rightPre, rightIn));
        }

        return node;
    }

    //根據中序和後序建立二叉樹
    public static BinTreeNode createBinTreeByInPost(String in, String post) {
        if (post.length() == 1) {
            return new BinTreeNode(post.charAt(0));
        }

        char c = post.charAt(post.length() - 1);

        int i = 0;
        int number = post.length();

        while (i < number && in.charAt(i) != c) i++;

        int leftNumber = i;
        int rightNumber = number - i - 1;

        BinTreeNode node = new BinTreeNode(c);

        if (leftNumber >= 1) {
            String leftIn = in.substring(0, leftNumber);
            String leftPost = post.substring(0, leftNumber);

            node.setLeftChild(createBinTreeByInPost(leftIn, leftPost));
        }

        if (rightNumber >= 1) {
            String rightIn = in.substring(leftNumber + 1);
            String rightPost = post.substring(leftNumber, post.length() - 1);

            node.setRightChild(createBinTreeByInPost(rightIn, rightPost));
        }

        return node;
    }

    //前序遍歷
    public static void preOrderTraverse(BinTreeNode node) {
        if (node != null) {
            System.out.print(node.getData());
            preOrderTraverse(node.getLeftChild());
            preOrderTraverse(node.getRightChild());
        }
    }

    //後序遍歷
    public static void postOrderTraverse(BinTreeNode node) {
        if (node != null) {
            postOrderTraverse(node.getLeftChild());
            postOrderTraverse(node.getRightChild());
            System.out.print(node.getData());
        }
    }

    public static void main(String[] args) {
        String pre = "ABCDEF";
        String in = "CBDAEF";
        String post = "CDBFEA";

        BinTreeNode rootNode = BinTreeCreator.createBinTreeByPreIn(pre, in);
        System.out.print("post order: ");
        BinTreeCreator.postOrderTraverse(rootNode);

        rootNode = BinTreeCreator.createBinTreeByInPost(in, post);
        System.out.print(System.lineSeparator() + "pre order: ");
        BinTreeCreator.preOrderTraverse(rootNode);
    }
}

C語言版:

#include <stdio.h>
#include <stdlib.h>

typedef struct node {
    char data;
    struct node *lchild, *rchild;
} BinTreeNode;

BinTreeNode * createBinTreeByPreIn(char *pre, char *in, int number);
BinTreeNode * createBinTreeByInPost(char *in, char *post, int number);
void preOrderTraverse(BinTreeNode *node);
void inOrderTraverse(BinTreeNode *node);
void postOrderTraverse(BinTreeNode *node);

int main(int argc, const char * argv[]) {

    char *pre = "ABCDEF";
    char *in = "CBDAEF";
    char *post = "CDBFEA";

    BinTreeNode *rootNode = createBinTreeByPreIn(pre, in, 6);

    printf("post order: ");
    postOrderTraverse(rootNode);

    rootNode = createBinTreeByInPost(in, post, 6);

    printf("\npre order: ");
    preOrderTraverse(rootNode);

    return 0;
}

//根據前序和中序建立二叉樹
BinTreeNode * createBinTreeByPreIn(char *pre, char *in, int number) {
    if (number == 0) return NULL;

    char c = pre[0];

    int i = 0;

    while (i < number && in[i] != c) i++;

    int leftNumber = i;
    int rightNumber = number - i - 1;

    BinTreeNode *node = (BinTreeNode *) malloc(sizeof(BinTreeNode));
    node->data = c;

    node->lchild = createBinTreeByPreIn(&pre[1], &in[0], leftNumber);

    node->rchild = createBinTreeByPreIn(&pre[leftNumber + 1], &in[leftNumber + 1], rightNumber);

    return node;
}

//根據中序和後序建立二叉樹
BinTreeNode * createBinTreeByInPost(char *in, char *post, int number) {
    if (number == 0) return NULL;

    char c = post[number - 1];

    int i = 0;

    while (i < number && in[i] != c) i++;

    int leftNumber = i;
    int rightNumber = number - i - 1;

    BinTreeNode *node = (BinTreeNode *) malloc(sizeof(BinTreeNode));
    node->data = c;

    node->lchild = createBinTreeByInPost(&in[0], &post[0], leftNumber);

    node->rchild = createBinTreeByInPost(&in[leftNumber + 1], &post[leftNumber], rightNumber);

    return node;
}

//前序遍歷
void preOrderTraverse(BinTreeNode *node) {
    if (node != NULL) {
        printf("%c", node->data);

        preOrderTraverse(node->lchild);

        preOrderTraverse(node->rchild);
    }
}

//後序遍歷
void postOrderTraverse(BinTreeNode *node) {
    if (node != NULL) {
        postOrderTraverse(node->lchild);

        postOrderTraverse(node->rchild);

        printf("%c", node->data);
    }
}