二叉树

256 阅读4分钟

数组表示

数组下标和二叉树编号一一对应(0 下标空出来)

image.png

这样有 n 个元素的二叉树最多需要 2n2^n 数组空间(也就是每一层放一个, 上图右侧)

如果根节点放在数组 0 处, 则需要 2n12^n-1 数组空间

这样空间利用率较低

链表表示

image.png

template<class T>
struct binaryTreeNode {
    T element; // 数据

    // 左右子节点
    binaryTreeNode<T>* leftChild, * rightChild;

    // 构造函数
    binaryTreeNode() {
        leftChild = rightChild = NULL;
    }

    binaryTreeNode(const T& theElement):element(theElement) {
        leftChild = rightChild = NULL;
    }

    binaryTreeNode(const T& theElement,
        binaryTreeNode* leftChild,
        binaryTreeNode* rightChild):element(theElement)
    {
        this->leftChild = leftChild;
        this->rightChild = rightChild;
    }
};

遍历

三种遍历具体步骤: 点击查看

前序遍历

template<class T>
void preOrder(binaryTreeNode<T>* t) {
    if (t != NULL) {
        // 访问根节点...

        preOrder(t->leftChild); // 访问左子树
        preOrder(t->rightChild); // 访问右子树
    }
}

中序遍历

template<class T>
void inOrder(binaryTreeNode<T>* t) {
    if (t != NULL) {
        inOrder(t->leftChild); // 访问左子树

        // 访问根节点...

        inOrder(t->rightChild); // 访问右子树
    }
}
非递归实现

这是 avl 树的中序遍历, 偷懒一下

void AVLTree::inOrder() const {
    stack<AVLNode*> s;

    AVLNode* t = root;

    while (t || !s.empty()) {
        while (t) {
            s.push(t);
            t = t->left;
        }

        t = s.top();
        s.pop();

        cout << t->key << " ";
        t = t->right;
    }
}

后序遍历

template<class T>
void postOrder(binaryTreeNode<T>* t) {
    if (t != NULL) {
        postOrder(t->leftChild); // 访问左子树
        postOrder(t->rightChild); // 访问右子树

        // 访问根节点...
    }
}

层次遍历

从顶层到底层, 从左到右

template<class T>
void levelOrder(binaryTreeNode<T>* t) {
    queue<binaryTreeNode<T>*> q;

    while (t != NULL) {
        // 访问节点...

        if (t->leftChild != NULL) {
            q.push(t->leftChild);
        }

        if (t->rightChild != NULL) {
            q.push(t->rightChild);
        }

        if (q.empty()) break; // 如果队空就退出
        
        t = q.front();
        q.pop();
    }
}
📢 获取高度

层次遍历法

template<class T>
int getHeight(binaryTreeNode<T>* t) {
    if (t == NULL) return 0;

    queue<binaryTreeNode<T>*> q;

    int levelSize = 1; // 当前层的节点数(根节点)
    int height = 0; // 树的高度(从零开始, 一层结束就+1)

    q.push(t);
    while (!q.empty()) {
        temp = q.front();
        q.pop();

        if (temp->leftChild != NULL) {
            q.push(t->leftChild);
        }

        if (temp->rightChild != NULL) {
            q.push(t->rightChild);
        }

        levelSize--; // 访问过元素减一

        if (levelSize == 0) {
            height++;
            levelSize = q.size(); // 下一层的元素个数
        }
    }

    return height;
}

递归法

template<class T>
int getHeight(binaryTreeNode<T>* t) {
    if (t == NULL) return 0;

    int h1 = getHeight(t->leftChild);
    int h2 = getHeight(t->rightChild);

    // 子树最大高度加根节点
    return h1 > h2 ? ++h1 : ++h2;
}

🌈 获取最大宽度
template<class T>
int getMaxWidth(binaryTreeNode<T>* t) {
    if (t == NULL) return 0;

    queue<binaryTreeNode<T>*> q;

    int maxWidth = 1, levelSize = 1;

    q.push(t);
    while (!q.empty()) {
        temp = q.front();
        q.pop();

        if (temp->leftChild != NULL) {
            q.push(t->leftChild);
        }

        if (temp->rightChild != NULL) {
            q.push(t->rightChild);
        }

        levelSize--;

        if (levelSize == 0) {
            levelSize = q.size();
            maxWidth = maxWidth > levelSize ? maxWidth : levelSize;
        }
    }

    return maxWidth;
}

递归操作需要栈空间 O(n)O(n), 层次遍历需要队列空间 O(n)O(n)

完整代码

#include<queue>

// 节点类
template<class T>
struct binaryTreeNode {
    T element; // 数据

    // 左右子节点
    binaryTreeNode<T>* leftChild, * rightChild;

    // 构造函数
    binaryTreeNode() {
        leftChild = rightChild = NULL;
    }

    binaryTreeNode(const T& theElement) {
        element(theElement);
        leftChild = rightChild = NULL;
    }

    binaryTreeNode(const T& theElement,
        binaryTreeNode* leftChild,
        binaryTreeNode* rightChild)
    {
        element(theElement);
        this->leftChild = leftChild;
        this->rightChild = rightChild;
    }
};

// 二叉树
template<class T>
class binaryTree {
public:
    virtual ~binaryTree() {}
    virtual bool empty() const = 0;
    virtual int size() const = 0;
    virtual void preOrder(void(*) (T*)) = 0;
    virtual void inOrder(void(*) (T*)) = 0;
    virtual void postOrder(void(*) (T*)) = 0;
    virtual void levelOrder(void(*) (T*)) = 0;
};

// 链表二叉树
template<class T>
class linkedBinaryTree : public binaryTree<binaryTreeNode<T>> {
public:
    binaryTreeNode<T>* root;
    int treeSize;
    
    linkedBinaryTree() {
        root = NULL;
        treeSize = 0;
    }
    ~linkedBinaryTree() {
        erase();
    }
    bool empty() const {
        return treeSize == 0;
    }
    int size() const {
        return treeSize;
    }

    // 遍历方法, 传入一个函数(theVisit)的指针
    void preOrder(void(*theVisit)(binaryTreeNode<T>*)) {
        visit = theVisit;
        preOrder(root);
    }
    void inOrder(void(*theVisit)(binaryTreeNode<T>*)) {
        visit = theVisit;
        inOrder(root);
    }
    void postOrder(void(*theVisit)(binaryTreeNode<T>*)) {
        visit = theVisit;
        postOrder(root);
    }
    void levelOrder(void(*theVisit)(binaryTreeNode<T>*)) {
        visit = theVisit;
        levelOrder(root);
    }
    
    // 删除二叉树
    void erase() {
        postOrder(dispose);
        root = NULL;
        treeSize = 0;
    }

    // 遍历输出二叉树节点
    void preOrderOutput() {
        preOrder(output);
        cout << endl;
    }

    void inOrderOutput() {
        inOrder(output);
        cout << endl;
    }

    void postOrderOutput() {
        postOrder(output);
        cout << endl;
    }

    int height() const {
        return height(root);
    }

private:
    static void (*visit)(binaryTreeNode<T>*);
    static void preOrder(binaryTreeNode<T>* t);
    static void inOrder(binaryTreeNode<T>* t);
    static void postOrder(binaryTreeNode<T>* t);
    static void levelOrder(binaryTreeNode<T>* t);
    static void dispose(binaryTreeNode<T>* t) {
        delete t;
    }
    static void output(binaryTreeNode<T>* t) {
        cout << "节点数据" << t->element << endl;
    }
    int height(binaryTreeNode<T>* t);
};

// 获取二叉树高度
template<class T>
inline int linkedBinaryTree<T>::height(binaryTreeNode<T>* t) {
    if (t == NULL) return 0;

    int h1 = height(t->leftChild);
    int h2 = height(t->rightChild);

    return h1 > h2 ? ++h1 : ++h2;
}

// 前序遍历
template<class T>
void linkedBinaryTree<T>::preOrder(binaryTreeNode<T>* t) {
    if (t != NULL) {
        linkedBinaryTree<T>::visit(t);
        preOrder(t->leftChild); // 访问左子树
        preOrder(t->rightChild); // 访问右子树
    }
}

// 中序遍历
template<class T>
inline void linkedBinaryTree<T>::inOrder(binaryTreeNode<T>* t) {
    if (t != NULL) {
        inOrder(t->leftChild); // 访问左子树
        linkedBinaryTree<T>::visit(t);
        inOrder(t->rightChild); // 访问右子树
    }
}

// 后续遍历
template<class T>
inline void linkedBinaryTree<T>::postOrder(binaryTreeNode<T>* t) {
    if (t != NULL) {
        postOrder(t->leftChild); // 访问左子树 
        postOrder(t->rightChild); // 访问右子树
        linkedBinaryTree<T>::visit(t);
    }
}

// 层次遍历
template<class T>
inline void linkedBinaryTree<T>::levelOrder(binaryTreeNode<T>* t) {
    queue<binaryTreeNode<T>*> q;

    while (t != NULL) {
        linkedBinaryTree<T>::visit(t);

        if (t->leftChild != NULL) {
            q.push(t->leftChild);
        }

        if (t->rightChild != NULL) {
            q.push(t->rightChild);
        }

        if (q.empty()) break; // 如果队空就退出

        t = q.front();
        q.pop();
    }
}

先序遍历转后序遍历(递归)

仅完全二叉树(或满二叉树)可用

#include<iostream>
#include<string>
using namespace std;

int len = 0, index = 0;

// 前序字符串生成完全二叉树
void makeTree(int x, const string& pre, string& tree) {
    if (x > len) return;
    tree[x] = pre[index++];
    makeTree(x * 2, pre, tree);
    makeTree(x * 2 + 1, pre, tree);
}

// 后序遍历
void postOrder(int x, const string& tree, string& pos) {
    if (x > len) return;
    postOrder(x * 2, tree, pos);
    postOrder(x * 2 + 1, tree, pos);
    pos[index++] = tree[x];
}

// 前序字符串转后序字符串
string preToPost(const string& pre) {
    len = pre.length();

    string tree(len + 1, '0');
    string pos(len, '0');

    index = 0;
    makeTree(1, pre, tree);
    index = 0;
    postOrder(1, tree, pos);
    index = 0;

    return pos;
}

int main() {
    string pre;
    cin >> pre;
    string pos = preToPost(pre);

    for (int i = 0; i < pos.length(); i++) {
        cout << pos[i];
    }

    return 0;
}

先序和中序求树的高度

#include<iostream>
#include<string>
using namespace std;

/*
* @param root: 根节点在先序遍历中的索引
* @param child: 子树在中序中的起始位置
* @param nums: 节点数
*/
int height(const string& pre, const string& in, int root, int child, int nums) {
    if (nums != 0) {
        // 节点数不为 0

        // 查找根节点在中序遍历中的索引
        int n = 0;
        for (int i = child; i < nums + child; i++) {
            if (in[i] == pre[root]) break;
            n++;
        }

        // 左子树根 = root(根节点) + 1(左子树根)
        // 左子树起始 = child(当前树起始)
        int left = height(pre, in, root + 1, child, n);
        // 右子树剩余节点 = nums - n(左子树节点) - 1(根节点)
        // 右子树根 = root(根节点位置) + n(左子树节点个数) + 1(右子树根)
        // 右子树起始 = child(当前子树起始) + n(左子树个数) + 根(1)
        int right = height(pre, in, root + n + 1, child + n + 1, nums - n - 1);
        return left > right ? left + 1 : right + 1;
    }
    else {
        return 0;
    }
}

int main() {
    string pre = "ABDGHCEIF";
    string in = "GDHBAEICF";

    cout << height(pre, in, 0, 0, 9);
    return 0;
}