(写给未来遗忘的自己)
1.二叉树的种类
1. 满二叉树:所有分支都有数(都填满)

2. 完全二叉树:除了最底层没填满外其他的都满了,而且最底层从左到右是存在数的位置是连续的

3.二叉搜索树:二叉搜索树是一个有序树
$ 左子树不空,则左子树上所有节点的值小于根节点
$右子树不空,则右子树所有节点的值大于根节点
$ 左右子树下面也符合上述的两条

4.平衡二叉搜索树:空树或者左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是二 叉搜索树

2.二叉树的存储方式:
1.链式存储
链式存储以类似链表的方式存储。
代码如下:
#include <iostream>
#include <queue>
using namespace std;
// 定义二叉树的节点结构
struct TreeNode {
    int data;
    TreeNode* left;
    TreeNode* right;
    // 构造函数
    TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};
// 插入节点的函数
TreeNode* insert(TreeNode* root, int val) {
    // 如果根节点为空,创建新的节点作为根节点
    if (root == nullptr) {
        return new TreeNode(val);
    }
    // 使用队列进行层次遍历,找到第一个可以插入的空位置
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        TreeNode* node = q.front();
        q.pop();
        // 插入左子节点
        if (node->left == nullptr) {
            node->left = new TreeNode(val);
            break;
        }
        else {
            q.push(node->left);
        }
        // 插入右子节点
        if (node->right == nullptr) {
            node->right = new TreeNode(val);
            break;
        }
        else {
            q.push(node->right);
        }
    }
    return root;
}
int main() {
    TreeNode* root=nullptr;
    // 插入节点
    root = insert(root, 1);
    root = insert(root, 2);
    root = insert(root, 3);
    root = insert(root, 4);
    return 0;
}
2.顺序存储
二叉树的节点按照层次遍历的顺序存储在数组中。对于一个节点在数组中的位置,可以通过以下规则来访问其子节点和父节点:
- 节点在数组中的索引为 i:- 左子节点的位置为 2*i + 1。
- 右子节点的位置为 2*i + 2。
- 父节点的位置为 (i - 1) / 2(对整数除法取整)
 
- 左子节点的位置为 
         1
       /   \
      2     3
     / \   / \
    4   5 6   7
在顺序存储中,这棵树表示为一个数组:
[1, 2, 3, 4, 5, 6, 7]
#include <iostream>
#include <vector>
class BinaryTree {
public:
    // 构造函数,接受一个数组来初始化树
    BinaryTree(const std::vector<int>& elements) : tree(elements) {}
    // 获取指定节点的左子节点
    int leftChild(int index) {
        int leftIndex = 2 * index + 1;
        if (leftIndex < tree.size()) {
            return tree[leftIndex];
        } else {
            throw std::out_of_range("No left child.");
        }
    }
    // 获取指定节点的右子节点
    int rightChild(int index) {
        int rightIndex = 2 * index + 2;
        if (rightIndex < tree.size()) {
            return tree[rightIndex];
        } else {
            throw std::out_of_range("No right child.");
        }
    }
    // 获取指定节点的父节点
    int parent(int index) {
        if (index == 0) {
            throw std::out_of_range("No parent for root node.");
        }
        int parentIndex = (index - 1) / 2;
        return tree[parentIndex];
    }
    // 打印树的内容
    void printTree() {
        for (int value : tree) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }
private:
    std::vector<int> tree;
};
int main() {
    // 创建一个树 [1, 2, 3, 4, 5, 6, 7]
    std::vector<int> elements = {1, 2, 3, 4, 5, 6, 7};
    BinaryTree bt(elements);
    // 打印树
    bt.printTree();
    // 获取节点和打印其子节点
    try {
        std::cout << "Left child of 0: " << bt.leftChild(0) << std::endl;
        std::cout << "Right child of 0: " << bt.rightChild(0) << std::endl;
        std::cout << "Parent of 1: " << bt.parent(1) << std::endl;
        std::cout << "Parent of 2: " << bt.parent(2) << std::endl;
    } catch (const std::out_of_range& e) {
        std::cout << e.what() << std::endl;
    }
    return 0;
}
优点:构造函数使用初始化列表直接初始化成员变量,避免额外的拷贝
缺点:不适合频繁变化的树结构(如节点插入和删除),因为需要移动大量节点。也不适用于不完全的二叉树,因为会浪费数组空间
3.二叉树的遍历方式:
               1
          /         \
        2            3
      /   \        /   \
    4      5     6     7
   / \    / \   / \   / \
  8   9 10  11 12 13 14 15
//三种方式都是深度优先算法,所以中(将中一直搜索到最底,没有了才开始下一个左)
//前序遍历:(中左右)
 1, 2, 4, 8, 9, 5, 10, 11, 3, 6, 12, 13, 7, 14, 15
//中序遍历:(左中右)
8, 4, 9, 2, 10, 5, 11, 1, 12, 6, 13, 3, 14, 7, 15
//后序遍历
 8, 9, 4, 10, 11, 5, 2, 12, 13, 6, 14, 15, 7, 3, 13.1递归遍历:
递归是一种简洁的实现方式,适合自然的分治问题。每次递归调用函数时,都会自动处理子树
前序遍历:(中左右)
#include <iostream>
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// 递归前序遍历
void preorderTraversalRecursive(TreeNode* root) {
    if (root == nullptr) return;
    std::cout << root->val << " ";  // 访问根节点
    preorderTraversalRecursive(root->left);  // 遍历左子树
    preorderTraversalRecursive(root->right);  // 遍历右子树
}
中序遍历:(左中右)
void inorderTraversalRecursive(TreeNode* root) {
    if (root == nullptr) return;
    inorderTraversalRecursive(root->left);  // 遍历左子树
    std::cout << root->val << " ";  // 访问根节点
    inorderTraversalRecursive(root->right);  // 遍历右子树
}
后序遍历:(左右中)
#include <iostream>
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
// 后序遍历的递归实现
void postorderTraversalRecursive(TreeNode* root) {
    if (root == nullptr) return;
    postorderTraversalRecursive(root->left);   // 递归遍历左子树
    postorderTraversalRecursive(root->right);  // 递归遍历右子树
    std::cout << root->val << " ";             // 访问根节点
}
3.2迭代遍历:
非递归遍历使用栈来显式地保存状态,可以避免函数调用栈的开销,并且更适合处理非常深的树结构,防止栈溢出。
前序遍历:
#include <iostream>
#include <stack>
void preorderTraversalIterative(TreeNode* root) {
    if (root == nullptr) return;
    std::stack<TreeNode*> s;
    s.push(root);
    while (!s.empty()) {
        TreeNode* node = s.top();
        s.pop();
        std::cout << node->val << " ";  // 访问根节点
        // 注意这里先压右子节点,再压左子节点,因为栈是后进先出
        if (node->right) s.push(node->right);
        if (node->left) s.push(node->left);
    }
}
中序遍历:
void inorderTraversalIterative(TreeNode* root) {
    std::stack<TreeNode*> s;
    TreeNode* curr = root;
    while (curr != nullptr || !s.empty()) {
        // 不断访问左子节点并压入栈中
        while (curr != nullptr) {
            s.push(curr);
            curr = curr->left;
        }
        // 弹出栈顶节点,访问该节点
        curr = s.top();
        s.pop();
        std::cout << curr->val << " ";
        // 遍历右子树
        curr = curr->right;
    }
}
后序遍历:
#include <iostream>
#include <stack>
#include <vector>
void postorderTraversalIterative(TreeNode* root) {
    if (root == nullptr) return;
    std::stack<TreeNode*> s1, s2;
    s1.push(root);
    while (!s1.empty()) {
        TreeNode* node = s1.top();
        s1.pop();
        s2.push(node);
        // 先将左子节点压入栈
        if (node->left) s1.push(node->left);
        // 再将右子节点压入栈
        if (node->right) s1.push(node->right);
    }
    // 最后从栈2中弹出节点并访问
    while (!s2.empty()) {
        std::cout << s2.top()->val << " ";
        s2.pop();
    }
}
广度优先算法(层次遍历):
#include <iostream>
#include <queue>
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
void levelOrderTraversal(TreeNode* root) {
    if (root == nullptr) return;  // 若根节点为空,直接返回
    std::queue<TreeNode*> q;  // 定义一个队列
    q.push(root);  // 将根节点入队
    while (!q.empty()) {
        TreeNode* node = q.front();  // 取队列头部的节点
        q.pop();  // 出队列
        std::cout << node->val << " ";  // 访问节点
        // 若有左子节点,将左子节点入队
        if (node->left != nullptr) {
            q.push(node->left);
        }
        // 若有右子节点,将右子节点入队
        if (node->right != nullptr) {
            q.push(node->right);
        }
    }
}
int main() {
    // 构造一个简单的二叉树
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->left->left = new TreeNode(4);
    root->left->right = new TreeNode(5);
    root->right->left = new TreeNode(6);
    root->right->right = new TreeNode(7);
    // 执行广度优先遍历
    std::cout << "二叉树的广度优先遍历结果: ";
    levelOrderTraversal(root);
    return 0;
}



















