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

数据结构——树(中篇)

今日名言

人生碌碌,竞短论长,却不道枯荣有数,得失难量


 

上次我们讲了树的相关知识,接下来就进一步了解二叉树吧。本文为个人学习笔记,如有侵权,请

联系删除,如有错误,欢迎批评指正!!!

上次讲到了建堆的做法,这次我们来讲讲有关堆的应用吧

堆排序

堆排序就是利用堆的思想来排序,总共分为两个步骤


1.建堆

  • 升序:建大堆
  • 降序:建小堆

这里可能有人有疑问,为什么升序是建大堆,而降序是建小堆呢?以小堆为例,我们建完小堆后,第一个元素就是最小的,那么为了得到第二小的元素我们就得继续建小堆,这样时间你复杂度就比较高;而如果我们利用堆删除的方法来排序,将第一个元素和最后一个元素交换,取最后一个元素,然后采用向下调整的方法调整,这样依次进行,就可以得到一个降序序列。

2.利用堆删除的方法进行排序

代码实现

for (int i = (n - 2) / 2;i >= 0;i--) {AdjustDown(a, n, i);int end = n - 1;//找到最后一个元素while (end > 0) {swap(&a[0], &a[end]);//交换第一个与最后一个AdjustDown(a, n, i);//向下调整end--;//排除最后一个继续调整}
}

图解:

Top-K问题

什么是TOP-K问题呢?

即是求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大,其实这类问题在我们的日常生活中十分常见,例如专业前十名,世界五百强等等。

对于这类问题,我们一般的方法就是排序,但是数据量大,排序可能就不适合了。


 

如果用堆来解决,我们有两种方法

  • 方法一:在大堆中pop k次就能找到最大的前K个,在小队中pop k次就能找到最小的前K个。但是这种方法会有弊端,数据量大的时候就需要非常多的空间,这样内存可能不够用
  • 方法二:如果是查找前K个最大的元素,我们就用前K个元素建小堆, 再用剩余的元素与堆顶元素比较,如果比堆顶元素大,就如替换堆顶元素;如果是查找前K个最小的元素,我们就用前K个元素见大堆,再用剩余的元素与堆顶元素比较,如果比堆顶元素小,则替换堆顶元素。

下面我们通过一道具体的题来体会一下

题目来源:面试题 17.14. 最小K个数 - 力扣(LeetCode)

题目描述:

代码解答:
 

/*** Note: The returned array must be malloced, assume caller calls free().*/
void AdjustDown(int*arr,int n,int root){int parent=root;int child=2*root+1;while(child<n){if(child+1<n&&arr[child]<arr[child+1]){//防止越界child++;//如果左孩子小于右孩子,那么就交换二者的值}if(arr[parent]<arr[child]){int t=arr[parent];arr[parent]=arr[child];arr[child]=t;//交换二者的值}parent=child;child=2*parent+1;//更新二者的值}}
int* smallestK(int* arr, int arrSize, int k, int* returnSize) {if(arrSize==0||k<=0){//如果数组大小为0或者前k个数小于等于0,那么返回的是空数组,且大小为0return NULL;*returnSize=0;}int *returnarr=(int*)malloc(sizeof(int)*k);int i=0;for(i=0;i<k;i++){returnarr[i]=arr[i];//将前K个数存入返回数组中}for(i=(k-2);i>=0;i--){AdjustDown(returnarr,k,i);//建大堆}for(i=k;i<arrSize;i++){//如果数组中剩余的值比堆顶元素小,则替换堆顶元素if(arr[i]<returnarr[0]){returnarr[0]=arr[i];AdjustDown(returnarr,k,0);}}*returnSize=k;return returnarr;}

图解:

 

二叉树的遍历

学习二叉树结构,最简单的方式就是遍历,所谓二叉树的遍历就是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。


 

按照规则,二叉树的遍历有:前序,中序,后序的递归结构遍历(这三种遍历是根据访问根的先后顺序进行划分的)

  • 前序遍历(也叫做先序遍历)——访问根节点的操作发生在遍历其左右子树之前(根—左子树—右子树)
  • 中序遍历——访问根节点的操作发生在遍历其左右子树之中(左子树—根—右子树)
  • 后序遍历——访问根节点的操作发生在遍历其左右子树之后(左子树—右子树—根)

下面我们来看一个具体的例子,当然,在写他们的遍历时,可以省去NULL

前序遍历的代码实现

void preorderTraversal(TreeNode* root) {if (root == NULL) {printf("NULL ");}if (root != NULL) {printf("%d ", root->val);preorderTraversal(root->left);preorderTraversal(root->right);}
}

 

 后序遍历的代码实现

void postorderTraversal(TreeNode* root) {if (root == NULL) {printf("NULL ");}if (root != NULL) {postorderTraversal(root->left);postorderTraversal(root->right);printf("%d ", root->val);}
}

中序遍历的代码实现

void inorderTraversal(TreeNode* root) {if (root == NULL) {printf("NULL ");}if (root != NULL) {inorderTraversal(root->left);printf("%d ", root->val);inorderTraversal(root->right);}
}

 最终效果

层序排列:

除了以上三种遍历方法之外,还可以对二叉树进行层序遍历。假设二叉树根节点所在层数是1,那么层序遍历就是从根节点出发,首先访问第一层的节点,然后从左到右访问第二层上的节点,以此类推从上至下,从左往右逐层访问树的节点的过程就是层序遍历,也就是一层层遍历

 

当给定前序/后序+中序就能确定唯一的二叉树,而当给定前序+后序的话是不能确定唯一的二叉树的。

1.已知前序和后序

前序(根左右):EFHIGJK、中序(左右根):HFIEJKG

那么我们现在来判断二叉树

2.已知后序和中序

后序:bdeca,中序:badce;

 

有关二叉树的一些题目

1.求二叉树中得的节点个数 

代码实现

//方法一:与前中后序的遍历原理相同
int size = 0;//用全局变量来记录节点的个数
void BTreeSize1(BTNode* node) {if (node == NULL) {return;如果为空则直接返回}size++;BTreeSize1(node->left);//分别遍历左右子树,每次遍历加一就可以计算出节点的个数BTreeSize1(node->right);
}
//方法二:递归调用,返回自身的节点个数和左子树的所有节点以及右子树的所有节点
int BTreeSize2(BTNode* node) {if (node == NULL) {return 0;//如果为空就返回0}return 1 + BTreeSize2(node->left) + BTreeSize2(node->right);}

下面分析一下递归的方法(在初学二叉树的时候,画图真的是一个很友好的方式啦)

 

2,计算二叉树的高度

思路分析

递归思路,每个节点记录自己左右子树返回来的值,判断左右孩子哪边返回的值最大,最终用自身的加上大的那一边

代码实现

int BTreeHeight(BTreeNode* node) {if (node == NULL) {//如果为空,则返回0return 0;}int left = BTreeHeight(node->left);//计算左子树的高度int right = BTreeHeight(npde->right);//计算右子树的高度return left > right ? left + 1 : right + 1;//返回高度大的一遍,并且加上自身高度
}

3. 求二叉树中 第K层的节点个数     

思路:

  • 当k=1时,表示我们已经到达目标层级

  • 当前非空节点就是我们要计数的节点,返回1

  • 如果k>1,我们需要向下一层探索

  • 分别计算左子树和右子树中第k-1层的节点数

  • 将左右子树的结果相加返回

代码描述


int BTreeLevelKSize(BTNode* node,int k) {aseert(k > 0);//防止遇到不合法的kif (node == NULL) {return 0;}if (k == 1) {//第K层,节点数肯定为1return 1;}return BTreeLevelKSize(node->left, k - 1) + BTreeLevelKSize(node->right, k - 1);//加上左右子树的节点个数
}

                     4.查找节点中的值

思路:

  • 如果节点为空,则不满足条件
  • 分别在左右子树中查找
  • 如果节点中的值与所要查找的值相等,就返回该节点的位置

代码描述

BTNode* BTreeFind(BTNode* root, BTDataType x) {if (root == NULL) {return NULL;}if (root->data == x) {return root;}BTNode* left = BTreeFind(root->left, x);BTNode* right = BTreeFind(root->right, x);if (left != NULL) {return left;}if (right != NULL) {return right;}
}

 


更多内容,敬请期待。

http://www.xdnf.cn/news/243631.html

相关文章:

  • 论文笔记——QWen2.5 VL
  • 基于大模型预测的输尿管癌诊疗全流程研究报告
  • PDF24 Tools:涵盖20+种PDF工具,简单高效PDF工具箱,支持一键编辑/转换/合并
  • Selenium:模拟真实用户的爬虫
  • 【Python Web开发】04-Cookie和Session
  • 彩带飘落效果
  • 大学之大:香港理工大学2025.5.1
  • 返回类型后置 和 auto推导返回值类型
  • Vue 3 中通过 this. 调用 setup 暴露的函数
  • 使用CubeMX新建DMA工程——存储器到外设模式
  • 21 课时精通生成式 AI:微软官方入门指南详解
  • 人工智能发展对未来IT从业岗位的展望
  • Java大厂硬核面试:Flink流处理容错、Pomelo JVM调优、MyBatis二级缓存穿透防护与Kubernetes服务网格实战解析
  • Rust多线程性能优化:打破Arc+锁的瓶颈,效率提升10倍
  • SpringBoot研究生双选系统开发实现
  • 图与网络模型
  • C#实现主流PLC读写工具类封装
  • 设计模式简述(十五)观察者模式
  • OpenGL-ES 学习(15) ----纹理
  • x86_64 Linux使用avx指令(补充)
  • RISC-V AIA SPEC学习(四)
  • python如何把pdf转word
  • (33)VTK C++开发示例 ---图片转3D
  • Lucene多种数据类型使用说明
  • 文献阅读篇#5:5月一区好文阅读,BFA-YOLO,用于建筑信息建模!(上)
  • 段永平浙大访谈精华:长期主义的知行合一
  • 类成员函数编译链接的过程
  • Spark-小练试刀
  • centos7 离线安装python3 保留python2
  • 华为eNSP:多区域集成IS-IS