数据结构——二叉树的基本应用

news2024/11/17 17:26:12

在此之前我们已经初步了解了二叉树,在介绍堆的基本应用时,我们已经具体介绍了完全二叉树的基本应用,本章我们介绍二叉树的基本应用,这个不止指的是完全二叉树,而是指泛型的二叉树。

二叉树的基本应用,由于其左右子树层层连接的特性,大多都是由双向递归实现的,在本章我们要大量使用递归思想,希望能帮助到你

目录

二叉树的遍历

1.前序遍历(深度优先遍历DFS)

2.中序遍历

3.后序遍历

二叉树遍历的代码实现

1.实现前序遍历        

2. 实现中序遍历

3.实现后序遍历 

二叉树的基本函数接口

二叉树函数实现

1.二叉树结构体

2.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树

3. 二叉树销毁

4.二叉树节点个数

5.二叉树叶子节点个数

6. 二叉树第k层节点个数

7.二叉树查找值为x的节点

层序遍历(广度优先遍历BFS)

判断完全二叉树

总结:


二叉树的遍历

我们在介绍二叉树基本函数之前,先来介绍一下二叉树的遍历

二叉树的基本遍历分为三种,前序遍历,中序遍历,后序遍历

1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

我们下面用这棵树进行讲解

1.前序遍历(深度优先遍历DFS)

即根据 根节点->左子树->右子树的顺序进行遍历,遇到叶结点即返回

逻辑思路:按照根节点 ->左子树->右子树的顺序我们可以排序,第一个就是根节点 1 ,之后去找左子树,再按照顺序可得 节点 2 ,再找2的左子树为节点 3 ,再去找3的子树,此时发现3的左右子树都为NULL,则返回到2节点,此时2节点的左子树已经找完,按照顺序,我们该去找右子树,而2的右子树为NULL,此时2节点已经找完,返回到1,1的左子树找完,去到1的右子树4节点,取根节点4,再找左子树5以及右子树6;

完成这个顺序就完成了前序遍历

前序遍历结果:1 2 3 4 5 6

2.中序遍历

即根据 左子树->根节点->右子树的顺序进行遍历,遇到叶结点返回。

具体逻辑和前序遍历类似,只是访问顺序不同

中序遍历结果:3 2 1 5 4 6

3.后序遍历

即根据 左子树->右子树->根节点的顺序进行遍历,遇到叶结点返回

具体逻辑和前序遍历类似,只是访问顺序不同

后序遍历结果:3 2 5 6 4 1

下面是3种遍历的顺序示意图:

 

二叉树遍历的代码实现

1.实现前序遍历        

我们详细介绍前序遍历

后序遍历和中序遍历我们用流程图解释

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
 
typedef int BTDataType;
 
typedef struct BinaryTreeNode    //定义二叉树结构体
{
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
	BTDataType data;
}BTNode;
 
BTNode* BuyBTNode(BTDataType x)    //创建二叉树节点函数
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
 
	node->data = x;
	node->left = node->right = NULL;
	return node;
}
 
BTNode* CreatBinaryTree()    //手动创建一个如上图所示的二叉树
{
	BTNode* node1 = BuyBTNode(1);
	BTNode* node2 = BuyBTNode(2);
	BTNode* node3 = BuyBTNode(3);
	BTNode* node4 = BuyBTNode(4);
	BTNode* node5 = BuyBTNode(5);
	BTNode* node6 = BuyBTNode(6);
 
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
 
	return node1;
}
 
void PrevOrder(BTNode* root) {    //先序遍历函数
	if (root == NULL) {
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

以下具体介绍二叉树前序遍历的函数调用,前序遍历将会调用13个栈帧

栈帧1:将根节点传入到prevOrder函数中,打印数据1,然后分别调用1左子树的prevOrder,再调用1右子树的prevOrder

栈帧2:打印2后,分别调用2左子树的prevOrder,再调用2右子树prevOrder

栈帧3:打印3后,分别调用3左子树的prevOrder,再调用3右子树prevOrder

栈帧4:由于3的左子树为NULL,那么就打印NULL,递归回上一层函数,到3右子树prevOrder,同时销毁栈帧4

栈帧5:由于3的右子树为NULL,那么就打印NULL,递归回上一层函数,到2右子树prevOrder,同时栈帧销毁5,3

栈帧6:栈帧6调用的是2的右子树,2的右子树为NULL,就打印NULL,递归回上一层函数,此时最初的根节点1的左子树全部访问完成,进而将去访问1的右子树,这里栈帧2结束,被销毁

栈帧7:打印4后,分别调用4左子树的prevOrder,再调用4右子树prevOrder

栈帧8:打印5后,分别调用5左子树的prevOrder,再调用5右子树prevOrder

栈帧9:由于5的左子树为NULL,那么就打印NULL,递归回上一层函数,到5右子树prevOrder,同时栈帧销毁9

栈帧10:栈帧10调用的是5的右子树,5的右子树为NULL,就打印NULL,递归回上一层函数,这里5节点已经完全访问完成,销毁栈帧10,8

栈帧11:此时访问完成4的左子树,去访问4的右子树,即打印6,然后分别调用6左子树的prevOrder,再调用6右子树prevOrder

栈帧12:栈帧12调用的是6的左子树,6的右子树为NULL,就打印NULL,递归回上一层函数,销毁栈帧12

栈帧13:此时访问完成6的左子树,去访问6的右子树,6的右子树为NULL,即打印NULL,至此已经将二叉树全部访问完成,依次返回结果,然后逐步将栈帧销毁。

下面是函数递归展开图能够更好的理解

2. 实现中序遍历

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreeInOrder(root->_left);
	printf("%d ", root->_data);
	BinaryTreeInOrder(root->_right);
}

 递归展开图如下:

3.实现后序遍历 

void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	BinaryTreePostOrder(root->_left);
	BinaryTreePostOrder(root->_right);
	printf("%d ", root->_data);
}

 递归展开图如下: 

 

二叉树的基本函数接口

下面我们实现二叉树的基本函数

先来看一下二叉树的函数接口

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

二叉树函数实现

1.二叉树结构体

我们在之前已经介绍了二叉树节点的基本结构,包含三个元素:数据,左子树,右子树

这样我们可以清楚的访问到树的全部节点

结构体构建

typedef char BTDataType;

typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

2.通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树

这个函数的意思就是根据前序遍历的数组,来创建一个二叉树,我们既然知道了二叉树的前序遍历结果,同时空节点由#代替,我们就能利用递归来构建这个符合要求的二叉树、

进行遍历数组,由于这里是递归函数,那么我们就需要记住数组下标,而如果仅仅是用int类型的话,那么形参是会在递归过程中不断的销毁重建,是没法达到记住数组下标的目的的。

因此,我们传入int*,将地址传入,这样无论递归多少层,修改的变量一直是同一个变量,就能实现记录下标的目的

若数组元素为#则,返回NULL,同时下标+1

下面按照前序遍历进行递归构建二叉树

代码如下:

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->_data = a[(*pi)++];
	root->_left = BinaryTreeCreate(a, pi);
	root->_right = BinaryTreeCreate(a, pi);
	return root;

}

3. 二叉树销毁

二叉树销毁的过程,也是一个递归的过程,但是我们不能用前序遍历进行递归,如果我们用前序遍历进行递归的话,先将root释放,那我们就没法找到根的左右子树了,因此我们需要用后序遍历进行销毁。

我们用递归,找到根的左子树和右子树,从叶节点开始,从叶到根依次释放掉根节点的空间即可

不要忘记递归的终止条件,当root == NULL 时 返回

这里我们需要传入结构体二级指针,因为我们的根节点是一级指针,因此想要释放,就需要传入其地址,由二级指针接收

代码如下:

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
		return;
	BinaryTreeDestory((*root)->_left);
	BinaryTreeDestory((*root)->_right);
	free(*root);
}

4.二叉树节点个数

树的节点个数,也不难理解,还是利用递归,我们定义一个全局变量size,在左右子树进行递归,每次调用一次函数进行判断,若根节点不为NULL,就将size++,层层递归,最后size就是节点个数的结果

代码如下:

// 二叉树节点个数
int size = 0;
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	else
	{
		size++;
	}
	BinaryTreeSize(root->_left);
	BinaryTreeSize(root->_right);
	return size;
}

5.二叉树叶子节点个数

叶节点和节点个数略有差异,总体思想都是递归,但是叶结点需要进行判断,很显然叶结点的左右子树都为空,因此我们只要进行判断一下,符合条件就返回1,利用递归将左右子树的叶节点加起来就可以了

代码如下:

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root->_left == NULL && root->_right == NULL)
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

6. 二叉树第k层节点个数

统计k层的节点个数,还是利用递归,我们创建一个全局变量sizek,假设求第k层的节点,那么从第一层开始进行递归,每一次传入k = k -1,

当k==1 时sizek++

若k >1 则说明在k层之上,我们就需要继续递归,将 k -1,找到第k层

若k<1 说明已经找到了k层的元素,超过了第k层,直接返回0就可以了

注意,记住判断根节点是否为空,若为空就返回 0 ,不然会出现越界现象

逻辑图如下:

代码实现如下:

// 二叉树第k层节点个数
int sizek = 0;
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
	if (k < 1)
		return 0;
	else if (k > 1)
	{
		BinaryTreeLevelKSize(root->_left, k - 1);
		BinaryTreeLevelKSize(root->_right, k - 1);
	}
	else if (k == 1)
	{
		sizek++;
	}
	return sizek;
}

7.二叉树查找值为x的节点

查找对应值的节点,还是利用递归思想,由根->左子树->右子树的顺序即前序遍历的顺序进行查找,找到对应值就返回对应节点,若没有找到就返回NULL。

注意:在递归查找对应节点时,我们需要用指针进行接收结果,然后先判断左子树结果是否为NULL,若不为NULL,就直接返回对应节点,这样就不用再去遍历右子树,节省了时间和空间

代码如下:

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{

	if (root == NULL)
	{
		return NULL;
	}
	if (root->_data == x)
		return root;
	BTNode* ret1 = BinaryTreeFind(root->_left, x);
	if (ret1)
		return ret1;
	BTNode* ret2 = BinaryTreeFind(root->_right, x);
	if (ret2)
		return ret2;
	return NULL;
}

层序遍历(广度优先遍历BFS)

层序遍历比较特殊,我们单独分一部分来讲

这里我们需要用到队列的基本用法,如果忘记了可以去复习一下

层序遍历:除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根结点所在层数为1,层序遍历就是从所在二叉树的根结点出发,首先访问第一层的树根结点,然后从左到右访问第2层上的结点,接着是第三层的结点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

我们利用队列,上一层带下一层,将数据一层一层的入队列,根据队列的先进先出的特性,我们能将数据按照层序打印出来

逻辑图如下

我们建立队列q,此时队列q存的数据类型是结构体指针,这样才能记录根节点的地址,从而进行修改,然后判断根节点是否为空,若不为空就让根节点入队列,然后建立循环,当队列为空时说明数据已经遍历完成,此时终止循环

在循环中,我们记录队首数据,然后打印数据,删除队首数据,再用递归访问根节点的左右子树,从而实现层序遍历

代码如下:

void BinaryTreeLevelOrder(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    if (root)
    {
        QueuePush(&q, root);
    }
    while (!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
        QueuePop(&q);
        printf("%d ", front->_data);
        if (front->_left)
            QueuePush(&q, front->_left);
        if (front->_right)
            QueuePush(&q, front->_right);
    }
}

判断完全二叉树

判断完全二叉树,我们先来分析一下

完全二叉树:前n-1层 都是满的 第n层从左到右是连续的

我们仍然利用层序遍历的思想,不同的是,刚才我们的层序遍历不进空节点,这里我们也进入空节点 

1.进行层序遍历,空也进队列

2.遇到第一个空节点时,开始判断,后面全空就是完全二叉树,后面有非空就不是完全二叉树

 我们用逻辑图分析一下,即

下面我们根据思路来实现代码,总体思路还是层序遍历,但是不用打印数据,直接将左右子树数据入队,然后若出队数据为NULL节点,那么就进行判断,判断队列内是否全为NULL节点,若全为NULL节点,那么就是完全二叉树,若有非空数据,就不是完全二叉树。

代码实现如下:

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
	{
		QueuePush(&q, root);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q, front->_left);
		QueuePush(&q, front->_right);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

总结

二叉树的基本应用,还是比较复杂的,大多要使用递归实现,个别还需要用到队列的知识,因此我们需要掌握递归的原理,若不理解递归过程,可以通过画递归展开图的方式去理解,一定要将每一个函数接口给理解清楚,此类问题需要我们不断的练习和复习,才能将二叉树的知识巩固住,希望本篇文章能够帮助到你。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1710926.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NDIS协议驱动(三)

协议驱动程序源自发送请求并处理基础驱动程序的接收指示。 在单个函数调用中&#xff0c;NDIS 协议驱动程序可以在每个 NET_BUFFER_LIST 结构上发送具有多个 NET_BUFFER 结构的多个NET_BUFFER_LIST结构。 在接收路径中&#xff0c;协议驱动程序可以接收NET_BUFFER_LIST结构的列…

宝塔部署Java+Vue前后端分离项目

1. 服务器 服务器选择Linux的CentOS7的版本 2. 宝塔Linux面板 2.1 百度搜索宝塔 2.2 进去之后点击立即免费安装 2.3 选择Linux在线安装&#xff0c;输入服务器信息进行安装(也可以选择其他方式) 安装完成之后会弹一个宝塔的应用面板&#xff0c;并附带有登录名称和密码&…

Hugging face win使用教程(二)

1. 将环境迁移到win&#xff0c;可以直接下载修改好的cuda环境https://download.csdn.net/download/liangjiubujiu/89368302?spm1001.2014.3001.5503 2. 修改hugging face的缓存路径 3. 重启pycharm 4. 编写测试代码&#xff0c;注意可能由于网络链接问题&#xff0c;需要关…

OSINT 与心理学:通过开源情报进行剖析和行为分析

在不断发展的心理学领域&#xff0c;人们越来越认识到通过应用开源情报 (OSINT) 方法取得进步的潜力。OSINT 主要以其在安全和情报领域的应用而闻名&#xff0c;并且越来越多地展示其在心理分析和行为分析方面的潜力。本文探讨了 OSINT 和心理学的迷人交叉点&#xff0c;研究如…

基于BP神经网络和小波变换特征提取的烟草香型分类算法matlab仿真,分为浓香型,清香型和中间香型

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ...................................................................................... …

AURIX TC3xx单片机介绍-启动过程介绍3

如下的内容是英文为主,对于TC3xx芯片启动原理不清楚的,可以给我留言,我来解答你们的问题! 3.2.1 Reset类型识别 Reset类型的识别是用来判断上次的复位是Application Reset还是System Reset还是CPU0 Reset。基于复位的原因,启动软件会运行不同的分支逻辑。复位原因可以通…

每日两题 / 131. 分割回文串 42. 接雨水(LeetCode热题100)

131. 分割回文串 - 力扣&#xff08;LeetCode&#xff09; 数据量较小&#xff0c;考虑直接暴力&#xff0c;每次dfs&#xff1a;以bg作为左区间&#xff0c;往右遍历&#xff0c;找到一段回文串区间后&#xff0c;将回文串插入vector<string>&#xff0c;并以下一个下标…

【最优化方法】实验一 熟悉MATLAB基本功能

实验一  熟悉MATLAB基本功能 实验的目的和要求&#xff1a;在本次实验中&#xff0c;通过亲临使用MATLAB&#xff0c;对该软件做一全面了解并掌握重点内容。 实验内容&#xff1a; &#xff11;、全面了解MATLAB系统 &#xff12;、实验常用工具的具体操作和功能 学习建…

PHP:phpmyadmin 将查询数据导出csv

1、输入你的SQL查询出结果 2、查出数据以后拖到最下方【导出】 3、导出CSV

cs61B-sp21 | lab6

cs61B-sp21 | lab6 TODO 1 在 CapersRepository.java 中 static final File CAPERS_FOLDER null; // TODO Hint: look at the join // function in Utils在 Utils.java 我们找到 join 函数&#xff0c;第一个 join 的作用是将 first 和 others 连接起来形成一个路径…

部署LAMP环境

红帽9搭建LAMP 安装Apache 2.安装数据库服务 3.安装php (1)使用IP访问/phpinfo.php 4.安装phpMyAdmin &#xff08;1&#xff09;数据库端口改为学号后五位 &#xff08;2&#xff09;登录phpmyadmin 5.SSH增加一个端口10022&#xff0c;fttp增加两个端口10080和8080 &#xf…

使用python绘制一个五颜六色的爱心

使用python绘制一个五颜六色的爱心 介绍效果代码 介绍 使用numpy与matplotlib绘制一个七彩爱心&#xff01; 效果 代码 import numpy as np import matplotlib.pyplot as plt# Heart shape function def heart_shape(t):x 16 * np.sin(t)**3y 13 * np.cos(t) - 5 * np.cos…

HNU-人工智能-作业3

人工智能-作业3 计科210X 甘晴void 202108010XXX 1.贝叶斯网络 根据图所给出的贝叶斯网络&#xff0c;其中&#xff1a;P(A)0.5&#xff0c;P(B|A)1&#xff0c; P(B|A)0.5&#xff0c; P(C|A)1&#xff0c; P(C|A)0.5&#xff0c;P(D|BC)1&#xff0c;P(D|B, C)0.5&#xff…

链式法则:神经网络前向与反向传播的基石

在深度学习的浪潮中&#xff0c;神经网络以其强大的学习和预测能力&#xff0c;成为解决复杂问题的有力工具。而神经网络之所以能够不断学习和优化&#xff0c;离不开两个核心过程&#xff1a;前向传播和反向传播。其中&#xff0c;链式法则作为微积分学中的一个基本概念&#…

闲鱼电商运营高级课程,一部手机学会闲鱼开店赚钱

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89360471 更多资源下载&#xff1a;关注我。 课程内容&#xff1a; 10-9、怎么寻找优质的货源店铺.mp4 11-10、怎么去选择商品图片.mp4 12-11、商品图片的注意避免事项.mp4 13-12、怎么写标题.mp4 …

C++ 基于vs2019创建并使用动态链接库(dll)

库的基本认识 静态库&#xff08;Static Library&#xff09; 基本概念&#xff1a;静态库是在编译时链接到目标程序中的库文件。它包含了程序运行所需的所有函数和数据&#xff0c;这些函数和数据会被直接嵌入到最终生成的可执行文件中。静态库通常以.a&#xff08;在Unix-l…

三步走,Halo DB 安装指引

前文介绍了国产数据库新星 Halo 数据库是什么&#xff0c; 哈喽&#xff0c;国产数据库&#xff01;Halo DB! ★ HaloDB是基于原生PG打造的新一代高性能安全自主可控全场景通用型统一数据库。 业内首次创造性的提出插件式内核架构设计&#xff0c;通过配置的方式&#xff0c;适…

美光拟投巨资在日本广岛建DRAM厂,目标2027年底投产

美光科技&#xff08;Micron Technology&#xff09;据日本媒体报道&#xff0c;计划在日本广岛县新建一座DRAM芯片生产工厂&#xff0c;目标最快于2027年底投入运营。这一举措标志着美光在增强其内存芯片生产能力方面的又一重大步伐。 报道称&#xff0c;新工厂的总投资规模预…

【CALayer-CALayer的基本属性 Objective-C语言】

一、接下来,我们来说这个Layer啊, 1.首先,Layer能接触到的,就是我们之前说截图啊,就是我们self.view里面,有一个layer属性, [self.view.layer renderInContext:(CGContextRef t)]; 那个里面,有一个layer属性,然后呢,是CALayer类型的, 接下来,我们就来学习一…

union all 以及标量子查询执行计划

SELECT 1, (SELECT ID1 FROM TE WHERE IDA.ID2) FROM .TA A WHERE COLA X UNION ALL SELECT 1, (SELECT ID2 FROM TD WHERE IDA.ID1) FROM .TB A WHERE COLA X UNION ALL SELECT 1,COL2 AS PARENT_UUID FROM .TC a WHERE COLA X 三个union all 看着像是5个table joi…