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

数据结构 二叉树(一篇基本掌握)


绪论

        雄关漫道真如铁,而今迈步从头越。 本章将开始学习二叉树(全文共一万两千字),二叉树相较于前面的数据结构来说难度会有许多的攀升,但只要跟着本篇博客深入的学习也可以基本的掌握基础二叉树。

 话不多说安全带系好,发车啦(建议电脑观看)


附:红色,部分为重点部分;蓝颜色为需要记忆的部分(不是死记硬背哈,多敲);黑色加粗或者其余颜色为次重点;黑色为描述需要

目录

1.树的概念

2.二叉树

2.1二叉树的概念

2.2二叉树的顺序结构

2.2.1堆:

堆的实现(所要实现的功能):

向上、下调整的应用:

1.向上、向下调整直接建堆的方法:

2.通过大小堆来实现排升、降序

堆的TopK问题:

2.3二叉树的链式结构

2.3.1前序、中序、后序

2.3.1.1当给定前序 / 后序 + 中序就能确定唯一的二叉树(考试常考点)

2.3.2求二叉树中的节点个数:

2.3.3求二叉树的高度

2.3.4求二叉树中第K层的节点

2.3.5在二叉树中查找节点

进阶练习:

2.3.6层序遍历

练习:判断一个二叉树是否为完全二叉树


1.树的概念

树是一种非线性的数据结构,因为其结构和现实中的树的分叉形式倒过来的样子非常相似故被称为树,其中每个数据被称为一个个 节点,在一棵树中最上面的节点又被称为根节点(根节点没有前驱节点),对于每个节点来说都能称为一个树(或称为子树),每棵树都是由多颗子树构成,并且每个节点都会有且只有一个前驱节点而且由多个或零个后继节点。在树中的各个子树是不能有交集的(互不相交的),一棵有n个节点的树有n-1个父节点(在树中常习惯用亲缘关系来描述)

  • 父节点(双亲节点):一个节点的前驱节点(上图A就是BCD的父节点)
  • 子节点:一个节点的后继节点(A的子节点有BCD)
  • :每个节点有的子节点个数(A的度就为3)
  • 树的度:在一个树中的所有节点中的最大的度(上面的树其中就是A的度最大故树的度为3)
  • 节点的层次:从根开始算,根的层次为1依次往后....(故A的层次为1 、 j 的层次为4)
  • 高度(深度):一般来说也是从1开始的,上图的深度/高度就是4(会有争议有些是从0开始)
  • 叶(子)节点(终端节点):度为0的节点 或者 可以看成没有子节点的节点就是叶节点(如J、K、L、H、I、F)
  • 分支节点(非终端节点):不是叶子节点的节点都算分支节点 或者 度不为0的节点(上图除了叶子节点的其余节点都能看成分支节点)
  • 兄弟节点:有相同父节点的节点(如上图的BCD他们都互为兄弟节点)
  • 堂兄弟节点:父节点在同一层的节点(如F与G)
  • 祖先节点:从根到该节点所经过的所有节点
  • 子孙:其节点往后的所有节点都能看成该节点的子孙(上图所有节点都是A)
  • 森林:多颗互不相交的树

应用:目录树(由左孩子右兄弟法结构表示)

2.二叉树

2.1二叉树的概念

二叉树是树中的特殊的一种树,其中二叉树的度最大为2,此时的每个节点的子节点称为左孩子、右孩子(左子树、右子树)

  1. 满二叉树:除了叶子结点度为0,其余的节点的度都为2,每一层都是满的        
    1. 一个h层的满二叉树,有(2 ^ h) -  1 个节点
  2. 完全二叉树:高度为h的完全二叉树,其前h-1层都是满的,最后一层可以不满,但最后一层必须从左往右是连续的
    1. 一个h层的完全二叉树,有   2 ^( h - 1)~ (2 ^ h) - 1
  3. 在二叉树中 第 h 层的最大节点个数为:2 ^( h - 1)(若是满二叉树那就直接等于)
  4. 一个有h层的二叉树其节点个数最大( 2 ^ h )- 1
  5. 对于非空二叉树来说度为0的节点 n0 = 度为2的节点n2 + 1:n0 = n2 + 1
  6. 在完全二叉树中度为1的节点只有可能为 1 / 0 ;树的节点个数为偶数时度为1的节点个数是1、奇数时为0

2.2二叉树的顺序结构

二叉树的结构可以由顺序、链表结构实现,对于一般的二叉树来说不适合用顺序结构来存储,因为对于一些没有节点的地方会有许多空间的浪费,完全二叉树很适合用顺序结构,现实中通常把堆(一种二叉树)使用顺序结构的数组存储

2.2.1堆:

  1. 为一棵完全二叉树
  2. 大堆:树中的父节点都大于子节点
  3. 小堆:树中的父节点都小于子节点
  4. 父节点与子节点的关系
    1. 父节点到左孩子:leftchild = parent * 2 + 1
    2. 父节点到左孩子:leftchild = parent * 2 + 2
    3. 左、右孩子到父节点:parent = (child  - 1) /  2
  5. 对于堆结构来说,我们要理解到它的逻辑结构和物理结构
    1. 逻辑结构来说他是一颗完全二叉树
    2. 物理结构来说他的底层是数组来存储的
    3. 我们需要记住他的底层数组来描述这棵完全二叉树

堆的实现(所要实现的功能):

  1. 初始化(主要针对顺序结构所需要的空间)
  2. 销毁
  3. 插入数据
    1. 堆的向上调整算法
      1. 大概的算法原理:给定孩子的位置和父节点进行比较如果大于/小于则交换(大堆/小堆)循环判断若孩子到了堆顶了停止
      2. 时间复杂度为:O(N*logN)   
  4. 删除数据
    1. 堆的向下调整算法
      1. 大概的算法原理:给定父节点和树中的个数,然后让父节点和子节点进行比较如果大于/小于则交换(小堆/大堆)循环判断若孩子节点不存在时停止
      2. 时间复杂度为:O(N) 
  5. 取堆顶的数据、查看堆有几个数据、对堆判空

具体细节会在实现里有详细的注释(注释在我们日后工作中是非常重要的,所以可以写成一种习惯)

 #define _CRT_SECURE_NO_WARNINGS 1#include"Heap.h"void If_Add_Capacity(HP* php)
{if (php->_size == php->_capacity)//判断已有成员个数是否等于容量,若等则进去{HPDataType* ptr = (HPDataType*)realloc(php->_a, sizeof(HPDataType) * php->_capacity * 2);//进来后就说明空间不够了,需要开空间//一般多直接开辟比容量大两倍的空间 即 对a开辟结构体大小为原capacity两倍的空间if (ptr == NULL){perror("realloc");return;}php->_a = ptr;//因为可能是异地扩容所以还要将ptr赋值给数组aphp->_capacity *= 2;//容量 乘于 2ptr = NULL;//无用的指针置为NULL(好习惯)}
}//对于堆的初始化和销毁就不过多赘述了相信通过我前面的几篇文章已经能很好的了解其原理了!
void HeapInit(HP* php)
{assert(php);php->_capacity = 4;php->_a = (HPDataType*)malloc(sizeof(HPDataType)* (php->_capacity));if (php->_a == NULL){perror("php::malloc");return;}php->_size = 0;}
void HeapDestroy(HP* php)
{assert(php);free(php->_a);php->_capacity = php->_size = 0;php->_a = NULL;}//----------------------------------------------------------------
void swap(HPDataType* t1, HPDataType* t2)
{HPDataType tmp = *t1;*t1 = *t2;*t2 = tmp;
}
//对于向上调整来说,他能形成大堆/小堆
//此处我们先实现一个小堆
// 
//小堆:树中任意一个位置的父节点都要比子节点小
//父子节点的关系: leftchild = parent * 2 + 1 、rightchild = parent * 2 + 2 、parent = (child - 1)/ 2 
void AdjustUp(HPDataType* a, int child) {while (child > 0)//循环来进行调整,从数组最后一直要调整到堆顶,顶部时child为0 所以条件是要大于0{int parent = (child - 1) / 2;//找到父节点if (a[parent] > a[child])//判断自己是否小于父节点{swap(&a[parent], &a[child]);//若小于则进行交换}else {break;//反之只要不小于就退出}child = parent;//修改子节点的下标,让其不断往上走}
}//对于堆的插入我们要知道的是其实他是把数据插入到了一个数组中
//但是要注意的是,如果需要实现一个堆的话 , 那就必须是大堆 / 小堆
//所以我们不仅仅只是把数据插入数组中,而且还需要对数组中的数据进行排序
//通过排序后让他变成大、小堆
//此处就需要用到  向上调整算法
//向上调整算法的前提是在调整的树(除去刚刚插入的数据外)已经满足大堆/小堆
//而此处的数据是一个个插入的(向上调整后就形成了大堆/小堆)所以就能很好的满足这个前提条件
void HeapPush(HP* php, HPDataType x)
{assert(php);If_Add_Capacity(php);//判断容量是不是满了//首先将数据插入顺序表的尾部php->_a[php->_size] = x;AdjustUp(php->_a, php->_size);//就行向上排序让新插入的数据也成功的融入到这个堆中php->_size++;//一定不要忘记要增加一下size}//--------------------------------------------------------------------
//向下调整成小堆
//向下调整的原理和向上调整差不多
//只不过反了过来void AdjustDown(int* a, int n, int parent)
{//找到小的那个孩子//建小堆的话需要父节点小于子节点//为什么要找小的孩子呢,因为我们要找到子节点中小的那个孩子,才能避免大的孩子如果大于父节点而小的孩子却小于父节点的情况//此处用了一种特殊的方法//先将左孩子看成小的,再判断如果左孩子小于右孩子的话再改变child即可int child = parent * 2 + 1;while (child < n)//要判断一下孩子节点是否在size范围内{if (child+1 < n && a[child + 1] < a[child])//细节判断一下child+1这个节点是否存在{child++;}if (a[child] < a[parent]){swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}	else{break;}}
}
//堆的删除数据是将堆顶的数据删除
//而这个删除并不是像顺序表一样的进行覆盖,而是
//先将堆顶的数据和最后的数据进行交换
//交换后size--,这样就表示成把数据删除了,因为访问时是在size范围内进行的
//然后对交换到堆顶的数据进行向下调整(让他保持还原成一个堆,满足堆的条件)
void HeapPop(HP* php)
{assert(php);assert(!HeapEmpty(php));swap(&php->_a[0], &php->_a[php->_size - 1]);php->_size--;AdjustDown(php->_a, php->_size ,0);
}HPDataType HeapTop(HP* php){assert(php);assert(!HeapEmpty(php));return php->_a[0];
}
bool HeapEmpty(HP* php){assert(php);return php->_size == 0;
}
int HeapSize(HP* php){assert(php);return php->_size;
}

向上、下调整的应用:

1.向上、向下调整直接建堆的方法:

向上调整:他是在一个堆中进行的,我们从数组的第二个数据开始调整这样就不用管这个前提条件了(因为向上看的话单独的节点可以看成一个堆)

向下调整:他左右节点是堆中进行的,我们可以从最后一个叶节点的父节点开始调整这样就不用再考虑前提条件(因为向下看的话单独的节点可以看成一个堆)

这样给定一个数组让他变成大、小堆的话,就能通过向上或者向下调整循环来构造出一个堆。

具体如下:

建小堆:(若要建大堆的话在向上下调整函数中改变一下大小与关系即可换成建大堆)

void HeapSort(int* a, int n)
{//原理和在堆中插入数据差不多//向上调整建小堆//从第二个数据开始然后不断往后for (int i = 1; i < n; i++){AdjustUp(a, i);}//向下调整建堆//从最后的叶子节点的开始然后不断往前for (int i = (n - 2) / 2; i >= 0; i--) // 最后一个叶子节点 n - 1 的父节点(( n - 1 ) - 1 )/ 2 {AdjustDown(a,n,i);}
}
2.通过大小堆来实现排升、降序

升序建大堆、降序建小堆

因为我们建大堆时父节w点一定大于子节点,所以此时根节点也就是最大的节点,可以把根节点和最后节点交换,这样最大的就发到了 , 然后size--把这个节点先排除再进行向下调整就能还原堆 , 最后循环上面过程最终就能实现升序。

小堆的方法一样就不赘述了。通过代码来看

//通过向下调整建小堆
for (int i = (n - 2) / 2; i >= 0; i--) // 最后一个叶子节点 n - 1 的父节点(( n - 1 ) - 1 )/ 2 
{AdjustDown(a,n,i);
}int end = n - 1;//找到最后一个节点
while (end > 0)
{swap(&a[0], &a[end]);//将最后的节点和堆顶元素交换下AdjustDown(a, end, 0);//再还原一下堆--end;//改变尾
}

此处的时间复杂度为:O(N + N*logN)==O(N*logN)

堆的TopK问题:

从堆中取出最大/最小的前K个,建大堆找最大的前K个、建小堆找最小的前K个

方法1:在大堆中 pop k次 就能找到最大的前K个、在小堆中 pop k次找最小的前k个

但这方法有点弊端:需要先建堆,而数据量非常大的时候就需要非常多的空间,这样就会导致内存的不够用,(适用于K比较小的情况)

方法2:若查找最大的前K个,我们可以创建一个K大小的小堆来存放这些数据(这样就避免将所有的数据都申请空间),让后让他们逐一比较当比堆顶的大就能进堆,建小堆的原因:因为是要找最大的前K个,小堆的根节点是最小的数据,只有这样才能把要找的大的数据放进去(大堆的根节点是最大的)

实现:

void TopK(int K)
{FILE* p = fopen("TopK.txt", "r");if (p == NULL){perror("p");return;}//建K大小的小堆HPDataType* heap_arr = (HPDataType*)malloc(sizeof(HPDataType) * K);if (heap_arr == NULL){perror("heap_arr::malloc");return;}int i = 0;for ( i = 0;i < K; i++){fscanf(p, "%d", &heap_arr[i]);}for (int i = (K - 2) / 2; i >= 0; i--) // 最后一个叶子节点 n - 1 的父节点(( n - 1 ) - 1 )/ 2 {AdjustDown(heap_arr, K, i);//对数组进行下下调整建堆}//查看剩下的数据HPDataType t = 0;while(!feof(p))//fscanf将文件中的数据读完后会设置feof的置为非0的值,故对feof取反{fscanf(p, "%d", &t);if (heap_arr[0] < t)//查看文件中的值是否大于堆顶的数据{heap_arr[0] = t;//大于的话这进堆AdjustDown(heap_arr, K, 0);//然后向下调整,将堆中最小的放到堆顶}}//打印堆的数据for (i = 0; i < K; i++){printf("%d\n", heap_arr[i]);}fclose(p);}void testtopk()
{//生成数据放进文件中FILE* p = fopen("TopK.txt", "w"); srand((unsigned int)time(0));//生成一百万个随机数据放进文件中for (int i = 1; i <= 1000000; i++){int r = rand() % 10000;fprintf(p, "%d\n", r);}fclose(p);TopK(10);//从小堆中找最大的前k(10)个
}

2.3二叉树的链式结构

链式二叉树的结构:int val、int* right、int* left,当其左右子树都为NULL时就代表到了最后,即对于链式的二叉树来说主要去查看他的左右子树来进行。

2.3.1前序、中序、后序

前序:根、左子树、右子树、中序:左子树、根、右子树、后序:左子树、右子树、根

这里的根表示访问根节点的数据,而左子树、右子树则表示通过结构去访问左右子树的节点。

像上图当左右子树为NULL时就开始返回

前中后序都是在递归的基础上进行的

  1. 前序遍历为:1 2 3 NULL NULL  NULL  4  5   NULL  NULL  6 N N 
    1. 分析: 前序遍历是先打印所到节点的数据后再往左右子树走遇到NULL则返回
  2. 中序遍历为:N 3 N 2 N  1  N  5  N  4  N  6  N
    1. 分析: 中序遍历是先往左子树走遇到NULL则返回  然后再打印所到节点的数据  然后才往右子树走遇到NULL则返回
  3. 后序遍历为:N N 3 N 2 N N 5 N N 6 4 1
    1. 分析: 后序遍历是先往左子树走遇到NULL则返回  然后再往右子树走遇到NULL则返回  然后才打印所到节点的数据 

用代码实现 :

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType _data;struct BinaryTreeNode* _left;struct BinaryTreeNode* _right;
}BTNode;BTNode* BuyNode(int x)
{BTNode* t = (BTNode*)malloc(sizeof(BTNode));if (t == NULL){perror("malloc");return NULL;}t->_data = x;t->_left = NULL;t->_right = NULL;return t;
}void Preorder(BTNode* node)
{if (node == NULL){printf("N ");return;}printf("%d ", node->_data);Preorder(node->_left);Preorder(node->_right);
}void Inorder(BTNode* node)
{if (node == NULL){printf("N ");return;}Inorder(node->_left);printf("%d ", node->_data);Inorder(node->_right);
}void Postorder(BTNode* node)
{if (node == NULL){printf("N ");return;}Postorder(node->_left);Postorder(node->_right);printf("%d ", node->_data);
}BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->_left = node2;node1->_right = node4;node2->_left = node3;node4->_left = node5;node4->_right = node6;return node1;
}int main()
{Preorder(CreatBinaryTree());printf("\n");Inorder(CreatBinaryTree());	printf("\n");Postorder(CreatBinaryTree());return 0;
}

递归展开图(通过这个仔细的了解其原理):

前序:

 中序、后序就不画了,建议初学者一定要画画

2.3.1.1当给定前序 / 后序 + 中序就能确定唯一的二叉树(考试常考点)

而当给前序+后序的话是不一定能确定唯一二叉树的

例:前序:EFHIGJK、中序:HFIEJKG

因为前序是:根、左、右 ;中序是:左 、 根 、 右

  1. 那么前序就能先判断根、而中序能判断左右子树
  2. 此时前序先为E
  3. 那么中序E的左子树就为HFI 、 E的右子树就是JKG 
  4. 再通过前序看根:F他是在中序E的左子树的并且前序先是F故为左子树的根
  5. 再通过中序查看F的左右子树为:左:H ; 右:I (因为其内部只有一个节点就不用再排了故就完成了)
  6. 此时树的左子树已经完成那就排除掉左边的(EHFI)再继续看右子树(JKG)
  7. 同样的方法:通过前序从前往后看根、通过中序看左右子树、然后不断往下直到没有节点
  8. 右子树:前序最开始时G故意G为子树的根节点、再看中序只有左节点那么都在左边 再看J为前序的开始那么以J为根、再看中序K在右边那么K为右子树
  9. 最终得:

例:后序:bdeca 、中序:badce

此时后序和前序略有不同需要我们从后往前来看

因为后序:左 、右 、根 ; 中序是:左 、 根 、 右

  1. 后序中从后往前看树的根为a
  2. 然后看中序只有b在左子树,那么在左子树就b一个节点、然后看右子树有(dce)
  3. 确定左右子树的节点后再看后序从后往前的倒数第二个为c,那么就表示c为右子树的根(此处要注意此时不同于前序要先看右树的根也就是c
  4. 在看中序就能看出d为右子树的左节点、e为右节点
  5. 最终得:

2.3.2求二叉树中的节点个数:

方法1的原理和前中后序一样

int size = 0;//用全局变量来记录节点个数
void BTreeSize1(BTNode* node)
{if (node == NULL)//当遇到NULL则返回且不记录return;++size;BTreeSize1(node->_left);BTreeSize1(node->_right);
}

方法2原理:返回 自身+左树的所有的节点+右树所有的节点,这样不断递归下去当到NULL时就返回0

int BTreeSize2(BTNode* node)
{if (node == NULL)//到尾了就返回return 0;return 1 + BTreeSize2(node->_left) + BTreeSize2(node->_right);
}

 初学建议一定要画画递归图这样就能更清楚的了解!下面就不画了看不懂可以画画图或者评论区可以提问我都会看!

2.3.3求二叉树的高度

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

实现:

int BTreeHight(BTNode* node)
{if (node == NULL)return 0;//记录左右子树返回的值int left = BTreeHight(node->_left);int right = BTreeHight(node->_right);//返回大的一边,+1是为了算自身return left > right ? left + 1 : right + 1;
}

2.3.4求二叉树中第K层的节点

思路:用一个变量记录如果到了第K层就返回1,否则的返回0

实现:

int BTreeLevelKSize(BTNode* node,int k)
{assert(k > 0);//防止K不符合实际if (node == NULL) {//同样的当遇NULL就要返回return 0;}//到达第K层时,k为1if (k == 1) {return 1;}//左右子树都要加上return BTreeLevelKSize(node->_left, k - 1) + BTreeLevelKSize(node->_right, k - 1);
}

2.3.5在二叉树中查找节点

思路:于上面查看二叉树的高度类似,先不断递归再在递归过程中判断其节点的值是否和所要查找的相等,若相等则返回该节点的地址,反之只有当遇空了才返回NULL , 最后通过记录左右子树的返回情况若为NULL则不用若不为NULL则表示返回来了一个节点指针

实现:

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{//查看节点是否为NULL若为则返回if (root == NULL){return NULL;}//判断节点数据是否等于要查找的数据if (root->_data == x){return root;}//记录左右子树的返回BTNode* left = BinaryTreeFind(root->_left, x);BTNode* right = BinaryTreeFind(root->_right, x);//若不为NULL则表示找到了节点返回节点指针即可if (left)return left;
//用了三目、原理一样return right != NULL ? right : NULL;
}

进阶练习:

对称二叉树:

思路:分析题目,为证明树的对称那么就能发现:要判断左子树的左节点和右子树的右节点是否相等、左子树的右节点和右子树的左节点是否相等。

那么首先要解决的难题是要抛弃固有思路只从一个节点来看,此时就可以在写一个函数,让其可以同时从左右子树往下查询是否相等

并且就能有递归思路:return   _isSymmetric(Leftroot->left ,  Rightroot->right ) && _isSymmetric(  Leftroot->right  ,  Rightroot->left);   左树左子--右树右子、左树右子--右树左子

然后就是限制递归的思路:

同时遇NULL表示相等(前面都相等到空树了)那么就表示是相等的返回真
若当只有一方为NULL时是不等的故返回false
再然后 就是判断其值是否相等了

最终代码:
 

//关键的如何可以同时在一棵树中往左右走
bool _isSymmetric(struct TreeNode* Leftroot  ,struct TreeNode* Rightroot){//两个都为NULLif(Leftroot == NULL &&  Rightroot == NULL){return true;}//其中一个为NULLif(Leftroot == NULL ||  Rightroot == NULL){return false;}if(Leftroot->val != Rightroot->val){return false;}return _isSymmetric(Leftroot->left,Rightroot->right )&& _isSymmetric(Leftroot->right,Rightroot->left);
}bool isSymmetric(struct TreeNode* root){return _isSymmetric(root->left , root->right);
}

2.3.6层序遍历

层序遍历的方法:通过队列的形式将节点存进队列中,每当有节点出队列时就将其自身的左右节点带进来,这样就能实现对二叉树的层序遍历。

代码实现:

void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);
//若为NULL就不进去了if(root)QueuePush(&q, root);
//判断条件队列不为NULLwhile (!QueueEmpty(&q)){
//记录在队列最前面的节点BTNode* front = QueueFront(&q);
//出队列QueuePop(&q);
//打印该节点的值printf("%d ", front->_data);//front的左右节点不是NULL则将节点带入if (front->_left)QueuePush(&q, front->_left);if(front->_right)QueuePush(&q, front->_right);}
}

练习:判断一个二叉树是否为完全二叉树

用层序的思路来查看该二叉树

对于完全二叉树的特性:节点是除了最后一层其余每层都是满的并且从左往右是连续的

则就表示在层序的队列中所有节点都是连续存放的其中不会有NULL插开的情况

bool BTreeCompare(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);//把节点逐一放进队列中while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);//只要第一次NULL就退出if (front == NULL)break;QueuePush(&q, front->_left);QueuePush(&q, front->_right);}//查看队列后面是否全部为NULL若不是就表示不是完全二叉树
//因为完全二叉树的节点是连续的while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front != NULL)return false;}return true;
}

如果有任何问题欢迎讨论哈!

如果觉得这篇文章对你有所帮助的话点点赞吧!

持续更新大量数据结构细致内容,早关注不迷路。

相关文章:

数据结构 二叉树(一篇基本掌握)

绪论 雄关漫道真如铁&#xff0c;而今迈步从头越。 本章将开始学习二叉树&#xff08;全文共一万两千字&#xff09;&#xff0c;二叉树相较于前面的数据结构来说难度会有许多的攀升&#xff0c;但只要跟着本篇博客深入的学习也可以基本的掌握基础二叉树。 话不多说安全带系好&…...

​可视化绘图技巧100篇高级篇(四)-南丁格尔玫瑰图(二)

目录 前言 适用场景 不适用场景 ​堆积式南丁格尔玫瑰图( Nightingale Rose Diagram)...

Stable Diffusion - Candy Land (糖果世界) LoRA 提示词配置与效果展示

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/132145248 糖果世界 (Candy Land) 是一个充满甜蜜和奇幻的地方&#xff0c;由各种各样的糖果和巧克力构成。在糖果世界&#xff0c;可以看到&…...

ES6学习-module语法

Module语法 CommonJS模块 let { readfile } require(fs) # 等同于 let _fs require(fs) let readfile _fs.readfile //这种加载称为“运行时加载”ES6模块 import { stat, exists, readFile } from fs;这种加载称为“编译时加载”或者静态加载 静态加载带来的各种好处 …...

Flutter 实现按位置大小比例布局的控件

文章目录 前言一、如何实现&#xff1f;1、数值转成分数2、RowFlexible布局横向3、ColumnFlexible布局纵向 二、完整代码三、使用示例1、基本用法2、四分屏3、六分屏4、八分屏5、九分屏6、414分屏 总结 前言 做视频监控项目时需要需要展示多分屏&#xff0c;比如2x2、3x3、414…...

ES6 - 对象新增的一些常用方法

文章目录 1&#xff0c;Object.is()2&#xff0c;Object.asign()3&#xff0c;Object.getOwnPropertyDescriptors()4&#xff0c;Object.setPrototypeOf()和getPrototypeOf()5&#xff0c;Object.keys()、values() 和 entries()6&#xff0c;Object.fromEntries()7&#xff0c;…...

半导体存储电路

存储电路 存储单元&#xff1a;只能存储一位数据 寄存器&#xff1a;存储一组数据 存储单元 静态存储单元&#xff1a;包含锁存器和触发器&#xff0c;只要不断电&#xff0c;静态存储单元的状态会一直保持下去。 动态存储单元&#xff1a;利用电容的电荷存储效应来存储数据。…...

web前端之CSS操作

文章目录 一、CSS操作1.1 html元素的style属性1.2 元素节点的style属性1.3 cssText属性 二、事件2.1 事件处理程序2.1.1 html事件2.1.2 DOM0事件&#xff08;适合单个事件&#xff09;2.1.3 DOM2事件&#xff08;适合多个事件&#xff09; 2.2 事件之鼠标事件2.3 事件之Event事…...

Python SQLAlchemy ( ORM )

From Python中强大的通用ORM框架&#xff1a;SQLAlchemy&#xff1a;https://zhuanlan.zhihu.com/p/444930067Python ORM之SQLAlchemy全面指南&#xff1a;https://zhuanlan.zhihu.com/p/387078089 SQLAlchemy 文档&#xff1a;https://www.sqlalchemy.org/ SQLAlchemy入门和…...

鉴源实验室丨汽车网络安全运营

作者 | 苏少博 上海控安可信软件创新研究院汽车网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 01 概 述 1.1 背景 随着车辆技术的不断进步和智能化水平的提升&#xff0c;车辆行业正经历着快速的变革和技术进步。智能化…...

分布式链路追踪之SkyWalking详解和实战

SkyWalking 文章目录 SkyWalking1.SkyWalking概述2.SkyWalking架构设计3.SkyWalking部署4.应用程序接入SkyWalking5.SkyWalking配置应用告警5.1.告警规则5.2.Webhook&#xff08;网络钩子&#xff09;5.3.邮件告警实践 6.项目自动化部署接入SkyWalking6.1 整体思路6.2 启动参数…...

【工程实践】使用EDA(Easy Data Augmentation)做数据增强

工程项目中&#xff0c;由于数据量不够&#xff0c;经常需要用到数据增强技术&#xff0c;尝试使用EDA进行数据增强。 1.EDA简介 EDA是一种简单但是非常有效的文本数据增强方法&#xff0c;是由美国Protago实验室发表于 EMNLP-IJCNLP 2019 会议。EDA来自论文《EDA: Easy Data…...

ClickHouse(十三):Clickhouse MergeTree系列表引擎 - ReplicingMergeTree

进入正文前&#xff0c;感谢宝子们订阅专题、点赞、评论、收藏&#xff01;关注IT贫道&#xff0c;获取高质量博客内容&#xff01; &#x1f3e1;个人主页&#xff1a;含各种IT体系技术&#xff0c;IT贫道_Apache Doris,大数据OLAP体系技术栈,Kerberos安全认证-CSDN博客 &…...

机器学习笔记之优化算法(十)梯度下降法铺垫:总体介绍

机器学习笔记之优化算法——梯度下降法铺垫&#xff1a;总体介绍 引言回顾&#xff1a;线搜索方法线搜索方法的方向 P k \mathcal P_k Pk​线搜索方法的步长 α k \alpha_k αk​ 梯度下降方法整体介绍 引言 从本节开始&#xff0c;将介绍梯度下降法 ( Gradient Descent,GD ) …...

Selenium 根据元素文本内容定位

使用xpath定位元素时&#xff0c;有时候担心元素位置会变&#xff0c;可以考虑使用文本内容来定位的方式。 例如图中的【股市】按钮&#xff0c;只有按钮文本没变&#xff0c;即使位置变化也可以定位到该元素。 xpath内容样例&#xff1a; # 文本内容完全匹配 //button[text(…...

第17章-Spring AOP经典应用场景

文章目录 一、日志处理二、事务控制三、参数校验四、自定义注解五、AOP 方法失效问题1. ApplicationContext2. AopContext3. 注入自身 六、附录1. 示例代码 AOP 提供了一种面向切面操作的扩展机制&#xff0c;通常这些操作是与业务无关的&#xff0c;在实际应用中&#xff0c;可…...

Leetcode周赛 | 2023-8-6

2023-8-6 题1体会我的代码 题2我的超时代码题目体会我的代码 题3体会我的代码 题1 体会 这道题完全就是唬人&#xff0c;只要想明白了&#xff0c;只要有两个连续的数的和&#xff0c;大于target&#xff0c;那么一定可以&#xff0c;两边一次切一个就好了。 我的代码 题2 我…...

ts中interface自定义结构约束和对类的约束

一、interface自定义结构约束对后端接口返回数据 // interface自定义结构 一般用于较复杂的结构数据类型限制 如后端返回的接口数据// 首字母大写;用分割号隔开 interface Iobj{a:number;b:string } let obj:Iobj {a:1,b:2 }// 复杂类型 模拟后端返回的接口数据 interface Il…...

Oracle单实例升级补丁

目录 1.当前DB环境2.下载补丁包和opatch的升级包3.检查OPatch的版本4.检查补丁是否冲突5.关闭数据库实例&#xff0c;关闭监听6.应用patch7.加载变化的SQL到数据库8.ORACLE升级补丁查询 oracle19.3升级补丁到19.18 1.当前DB环境 [oraclelocalhost ~]$ cat /etc/redhat-releas…...

力扣初级算法(二分查找)

力扣初级算法(二分法)&#xff1a; 每日一算法&#xff1a;二分法查找 学习内容&#xff1a; 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 2.二分查找流程&…...

探索未来:直播实时美颜SDK在增强现实(AR)直播中的前景

在AR直播中&#xff0c;观众可以与虚拟元素实时互动&#xff0c;为用户带来更加丰富、沉浸式的体验。那么&#xff0c;直播美颜SDK在AR中有哪些应用呢&#xff1f;下文小编将于大家一同探讨美颜SDK与AR有哪些关联。 一、AR直播与直播实时美颜SDK的结合 增强现实技术在直播中…...

SQL 单行子查询 、多行子查询、单行函数、聚合函数 IN 、ANY 、SOME 、ALL

单行子查询 子查询结果是 一个列一行记录 select a&#xff0c;b&#xff0c;c from table where a >(select avg(xx) from table ) 还支持这种写法,这种比较少见 select a&#xff0c;b&#xff0c;c from table where (a ,b)(select xx,xxx from table where col‘000’ )…...

【第一阶段】kotlin的range表达式

range:范围&#xff1a;从哪里到哪里的意思 in:表示在 !in&#xff1a;表示不在 … :表示range表达式 代码示例&#xff1a; fun main() {var num:Int20if(num in 0..9){println("差劲")}else if(num in 10..59){println("不及格")}else if(num in 60..89…...

网络防御(5)

一、结合以下问题对当天内容进行总结 1. 什么是恶意软件&#xff1f; 2. 恶意软件有哪些特征&#xff1f; 3. 恶意软件的可分为那几类&#xff1f; 4. 恶意软件的免杀技术有哪些&#xff1f; 5. 反病毒技术有哪些&#xff1f; 6. 反病毒网关的工作原理是什么&#xff1f; 7. 反…...

gradle 命令行单元测试执行问题

文章目录 问题&#xff1a;命令行 执行失败最终解决方案&#xff08;1&#xff09;ADB命令&#xff08;2&#xff09;Java 环境配置 问题&#xff1a;命令行 执行失败 命令行 执行测试命令 无法使用&#xff08;之前还能用的。没有任何改动&#xff0c;又不能用了&#xff09; …...

剑指Offer12.矩阵中的路径 C++

1、题目描述 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。单词必须按照字母顺序&#xff0c;通过相邻的单元格内的字母构成&#xff0c;其中“相邻”单元格是那些水平…...

金鸣识别将无表格线的图片转为excel的几个常用方案

我们知道&#xff0c;金鸣识别要将横竖线齐全的表格图片转为excel非常简单&#xff0c;但要是表格线不齐全甚至没有表格线的图片呢&#xff1f;这就没那么容易了&#xff0c;在识别这类图片时&#xff0c;我们一般会使用以下的一种或多种方法进行处理&#xff1a; 1. 基于布局…...

刚刚更新win11,记事本消失怎么处理?你需要注意些什么?

记录window11的bug hello&#xff0c;我是小索奇 昨天索奇从window10更新到了window11&#xff0c;由于版本不兼容卸载了虚拟机&#xff0c;这是第一个令脑壳大的&#xff0c;算了&#xff0c;还是更新吧&#xff0c;了解了解win11的生态&#xff0c;后期重新装虚拟机 第一个可…...

【QT】 QTabWidgetQTabBar控件样式设计(QSS)

很高兴在雪易的CSDN遇见你 &#xff0c;给你糖糖 欢迎大家加入雪易社区-CSDN社区云 前言 本文分享QT控件QTabWidget&QTabBar的样式设计&#xff0c;介绍两者可以自定义的内容&#xff0c;以及如何定义&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴…...

【个人记录】CentOS7 编译安装最新版本Git

说明 使用yum install git安装的git版本是1.8&#xff0c;并不是最新版本&#xff0c;使用gitlab-runner托管时候会拉项目失败&#xff0c;这里使用编译源码方式安装最新版本的git。 基础环境安装 echo "nameserver 8.8.8.8" >> /etc/resolv.conf curl -o /…...

c 做网站 知乎/自己如何做一个网站

这个作业的要求来自于&#xff1a;https://edu.cnblogs.com/campus/gzcc/GZCC-16SE2/homework/2941。 由于存在多次请求&#xff0c;所以稍微将请求封装如下 def tranfrom_dom_tree(url):将获取的html文本转化为dom树response requests.get(url);response.encoding "utf…...

为什么要买wordpress会员/自己搭建网站需要什么

我正在使用相当普通的Ubuntu 10.04服务器安装,我正在尝试使用BTRFS.如何创建BTRFS RAID1安装&#xff1f;我有两(2)个1Gig驱动器,我在服务器中弹出,运行以下命令后,看起来我有一个2千兆分区,而不是我预期的1千兆.$sudo mkfs.btrfs -m raid1 -d raid1 /dev/sdb /dev/sdcWARNING!…...

请别人做网站需要注意什么/网站改版seo建议

vs2015编译报错&#xff1a; 错误 C4996 ‘inet_addr’: Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated API warnings libharmorobotservice 解决方式&#xff1a; SDL改为否...

网站建设刂搜金手指下拉贰伍/个人怎么接外贸订单

1.话不多说,直接搞起; (源码地址https://github.com/carryLess/mbtsstd-001) 2.去官网https://github.com/mybatis下载文件 下载完成后,解压,有一个核心jar包,还有lib目录下的依赖包,ok 3.创建数据库表,实体类,图如下 *请将属性名称与表字段名称写一样(不一样也行,不一样的时候…...

微软网站设计/余姚网站制作公司

php blog网站开发实例教程 本章介绍一个基于文本的简易BLOG系统,当然我们可以利用这款blog系统的开发&#xff0c;很好的理解php网站开发原理了&#xff0c;其实网站开发容易于博客开哦&#xff0c;下面来看功能模块。本章介绍一个基于文本的简易blog系统,当然我们可以利用这款…...

免费网站平台推荐/链接平台

方法一&#xff1a; 修改/root/.bash_profile文件&#xff0c;增加export LANGzh_CN.GB18030 该文件在用户目录底下 对于其他用户&#xff0c;也必须相应修改该文件 使用该方法时putty能显示中文&#xff0c;但桌面系统是英文&#xff0c;而且所有的网页中文显示还是乱码 方法二…...