当前位置: 首页 > news >正文

【数据结构】之二叉树

二叉树是我们在数据结构中学到的第一个非线性结构,是后续学习更为复杂的树、图结构的基础。本文整理了二叉树的概念定义、基本操作、遍历算法、伪代码与代码实现以及实例说明,方便大家随时查找对应。

一、定义与基本术语

二叉树是一种树形结构,每个节点最多有两个子节点,分别称为左节点右节点

基本术语:

  • 根节点:树的顶部节点。
  • 叶节点:没有子节点的节点。
  • 父节点:某个节点的直接上级节点。
  • 子节点:某个节点的直接下级节点。
  • 兄弟节点:具有相同父节点的节点。
  • 深度:从根节点到某个节点的边的数量。
  • 高度:从某个节点到最远叶节点的边的数量。

        A (Depth: 0, Root)
       /    
     B (Depth: 1)
    /   \
 C1   C2(Depth: 2)
   \
    D (Depth: 3, Right)
     \
      E (Depth: 4, Right)
     /  \
    F1   F2(Depth: 4) 

二、主要特点

  1. 递归性质:二叉树的许多操作可以通过递归实现。
  2. 动态集合:二叉树可以用来实现动态集合,支持插入、删除和查找操作。
  3. 层次结构:二叉树具有明确的层次结构,适合表示具有层次关系的数据。

三、基本操作

1. 构建二叉树:

① 构建节点:

struct TreeNode{
    int key;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int k): key(k),left(nullptr),right(nullptr){}
};

② 插入节点:

插入操作会根据节点的值来决定插入的位置。例如,在二叉搜索树(BST)中,插入操作遵循以下规则:

  • 如果当前节点的值大于插入值,则递归地插入到左子树。
  • 如果当前节点的值小于插入值,则递归地插入到右子树。
  • 如果当前节点的值等于插入值,则可以选择不插入或插入到左/右子树。
TreeNode* insert(TreeNode* node, int key){
    if(node == nullptr){
        return new TreeNode(key);
    }
    if(key < node->key){
        node->left = insert(node->left, key);
    }else if(key > node->key){
        node->right = insert(node->right,key);
    }
    return node;
}

③ 构建树:

从一个空树开始,通过多次插入节点,可以逐步构建二叉树。

TreeNode* root = nullptr;
// 如果是基于一个给定数组构建二叉树
for(int i = 0;i<length;i++){
    root = insert(root,nums[i]);
}
//手动输入构建
root = insert(root,10);
root = insert(root,4);
root = insert(root, 7);
root = insert(root, 20);
root = insert(root, 34);
root = insert(root, 16);
root = insert(root, 8);

 2. 删除节点:

TreeNode* deleteNode(TreeNode* node, int key){
    if(node == nullptr) return node;
    if(key < node->key){
        node->left = deleteNode(node->left, key);
    } else if (key > node->key) {
        node->right = deleteNode(node->right, key);
    }else{
        if(node->left == nullptr){
            TreeNode* tmp = node->right;
            delete node;
            return tmp;
        }
        else if(node->right == nullptr){
            TreeNode* tmp = node->left;
            delete node;
            return tmp;
        }
        TreeNode* temp = findMin(node->right);
        node->key = temp->key;
        node->right = deleteNode(node->right, temp->key);
    }
    return node;
}

TreeNode* findMin(TreeNode* node){
    while(node->left != nullptr){
        node = node->left;
    }
    return node;
}

3. 查找节点:

TreeNode* searchNode(TreeNode* node, int key){
    if(node == nullptr||node->key = key) return node;
    if(key < node->key){
        return searchNode(node->left,key);
    }
    if(key > node->key){
        return searchNode(node->right, key);
    }
}

四、遍历算法

需要了解:前序遍历、中序遍历、后序遍历、层序遍历

1. 前序遍历:

        A
       / \
      B   C
     / \   \
    D   E   F

遍历顺序:A->B->D->E->C->F

        1
       / \
      2   3
     / \   \
    4   5   6
   / \
  7   8

遍历顺序:1->2->4->7->8->5->3->6

前序遍历的访问顺序:根节点-->左子树-->右子树

// 以int整数型为例
vector<int> result
void preorder(TreeNode* node){
    if(node == nullptr){
        return;
    }
    // 如果题目要求的是前序遍历打印节点值,那么可以直接:
    // cout<<node->key<<" ";
    // 如果题目要求前序遍历将节点值存储到数组中,需要先定义一个vector数组(如↑),再进行存储
    result.push_back(node_key);
    preorder(node->left);
    preorder(nofe->right);
}

 2. 中序遍历

        A
       / \
      B   C
     / \   \
    D   E   F

遍历顺序:D->B->E->A->C->F

        1
       / \
      2   3
     / \   \
    4   5   6
   / \
  7   8

遍历顺序:7->4->8->2->5->1->3->6 

        10
       /    \
      6    14
     /  \    /    \
    4   8 12  16

遍历顺序:4->6->8->10->12->14->16

中序遍历的访问顺序:左子树-->根节点-->右子树

vector<int> result;
void inorder(TreeNode* node) {
    if (node == nullptr) return;
    inorder(node->left);
    visit(node);
    inorder(node->right);
}

void visit(TreeNode* node){
    // cout<< node->key <<" ";
    result.push_back(node->key);
}

3. 后序遍历

        A
       / \
      B   C
     / \     \
    D   E   F

遍历顺序:D->E->B->F->C->A

        1
       / \
      2   3
     /  \    \
    4   5   6
   / \
  7   8

遍历顺序:7->8->4->5->2->6->3->1 

后序遍历的访问顺序:左子树->右子树->根节点。

void postorder(TreeNode* node){
    if(node==nullptr) return;
    postorder(node->left);
    postorder(node->right);
    visit(node);
}
//visit函数要根据题目要求来定义,示例可参考中序遍历的定义

4. 层序遍历

        A
       / \
      B   C
     / \     \
    D   E   F

遍历顺序:A->B->C->D->E->F

        1
       / \
      2   3
     /    /  \
    4   5   6
        /
      7 

遍历顺序:1->2->3->4->5->6->7

层序遍历的顺序是:从上到下,从左到右

层序遍历的结果特别好写,从上往下盯齐就没问题,但是代码是最长的。。

先来看看伪代码:

LevelOrder(root)
    queue = new Queue()
    queue.enqueue(root)
    while not queue.isEmpty()
        node = queue.dequeue()
        visit(node)
        if node.left != null
            queue.enqueue(node.left)
        if node.right != null
            queue.enqueue(node.right)

void levelorder(TreeNode* root){
    if(root == nullptr) return;
    queue<TreeNode* > q; // 创建一个队列,用于存储待访问的节点
    q.push(root); // 把现在的根节点存进去
    while(!q.empty()){
        TreeNode* node = q.front(); // 取出队列的第一个节点
        q.pop(); // 将该节点从队列中移除
        visit(node);
        if (node->left != nullptr) q.push(node->left);
        if (node->right != nullptr) q.push(node->right); 
        // 这里加入队列的顺序是先左再右,队列又是FIFO结构,遍历得到的顺序也就满足了先左再右
    }
}

五、适用场景举例

  1. 表达式树:用于表示和计算算术表达式。
  2. 二叉搜索树(BST):用于实现动态集合,支持高效的插入、删除和查找操作。
  3. 哈夫曼树:用于数据压缩。
  4. :用于实现优先队列。
  5. 线索二叉树:用于高效地遍历二叉树。

六、Leetcode 二叉树 练习题汇总

1. 初级题目

题号题目名称知识点
94二叉树的中序遍历中序遍历,递归与迭代
144二叉树的前序遍历前序遍历,递归与迭代
145二叉树的后序遍历后序遍历,递归与迭代
102二叉树的层序遍历层序遍历,队列的应用
226翻转二叉树递归,树的翻转
617合并二叉树递归,树的合并
589N叉树的前序遍历N叉树,前序遍历
897递增顺序搜索树二叉搜索树,中序遍历的应用

2. 中级题目 

题号题目名称知识点
98验证二叉搜索树二叉搜索树的性质,递归
113路径总和 II深度优先搜索,路径求和
257二叉树的所有路径深度优先搜索,路径记录
114二叉树展开为链表递归,树的展开
116填充每个节点的下一个右侧节点指针层序遍历,队列的应用
104二叉树的最大深度深度优先搜索或层序遍历
543二叉树的直径深度优先搜索,树的直径
236二叉树的最近公共祖先递归,最近公共祖先

 希望对你有帮助!

相关文章:

  • 《嵌套调用与链式访问:C语言中的函数调用技巧》
  • 关于Genspark.ai的使用体验
  • Linux基础4
  • SMT贴片组装工艺优化与高效生产
  • 【说明书#1】Node.js 和 npm安装与使用
  • 波束形成(BF)从算法仿真到工程源码实现-第五节-线性约束最小方差波束形成算法(LCMV)
  • 3DMAX建筑可视化插件RetailStore零售商店生成器安装及使用方法详解
  • 基于MCP-Client实现Manus思路扩展 和Demo设计
  • 重构艺术 | 内联与查询替代临时变量
  • 格式工厂 v5.18最新免安装绿色便携版
  • pgsql:关联查询union(并集)、except(差集)、intersect(交集)
  • Linux基础14
  • ASP.NET Core 性能优化:内存缓存
  • 3.1多状态专题:LeetCode面试题17.16 按摩师
  • vite,Vue3,ts项目关于axios配置
  • asm汇编源代码之文件操作相关
  • sql server 字段逗号分割取后面的值
  • Socket 编程中的基本步骤
  • OSPF的接口网络类型【复习篇】
  • Unity 动画
  • 视屏网站制作/自助建站系统开发
  • 咖啡色网站模板/网络销售 市场推广
  • 石家庄做网站价格/太原网站制作推广
  • 怎样手机网站建设/高平网站优化公司
  • 做外贸 访问国外网站/国外域名
  • 西安企业网站建设模板/百度竞价推广账户