数据结构--二叉树

news/2025/2/27 17:32:12/

目录

  • 1.树概念及结构
    • 1.1数的概念
    • 1.2数的表示
  • 2.二叉树概念及结构
    • 2.1二叉树的概念
    • 2.2数据结构中的二叉树
    • 2.3特殊的二叉树
    • 2.4二叉树的存储结构
      • 2.4.1顺序存储
      • 2.4.2链式存储
    • 2.5二叉树的性质
  • 3.堆的概念及结构
    • 3.1堆的实现
      • 3.1.1堆的创建
      • 3.1.2堆的插入
      • 3.1.3堆顶的删除
      • 3.1.4堆的代码实现
      • 3.1.5建堆时间复杂度
    • 3.2堆的应用
      • 3.2.1堆排序
      • 3.2.2TOP-K问题
  • 4.二叉树链式结构的实现
    • 4.1二叉树的定义
    • 4.2二叉树的遍历
      • 4.2.1二叉树的前序递归遍历
      • 4.2.2二叉树的中序递归遍历
      • 4.2.3二叉树的后序递归遍历
      • 4.2.4二叉树的层序遍历
    • 4.3二叉树节点数
    • 4.4二叉树叶子节点数
    • 4.5二叉树第K层节点数
    • 4.6二叉树查找值为X的节点
    • 4.7二叉树的深度
    • 4.8判断二叉树是否是完全二叉树
    • 4.9二叉树完整代码

1.树概念及结构

1.1数的概念

数是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

  1. 根结点:根节点没有前驱结点。
  2. 除根节点外,其余结点被分成是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。
  3. 因此,树是递归定义的。
    在这里插入图片描述

• 节点的度:一个节点含有的子树的个数称为该节点的度;如上图:A的为2

• 叶节点:度为0的节点称为叶子节点;如上图:D、E、F节点为叶子节点

• 非终端节点或分支节点:度不为0的节点; 如上图:B、C为分支节点

• 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点

• 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点

• 兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点

• 树的度:一棵树中,最大的节点的度称为树的度;如上图:树的度为2

• 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

• 树的高度或深度:树中节点的最大层次;如上图:树的高度为3

• 堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图:B、F互为堂兄弟节点

• 节点的祖先:从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先

• 以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙

• 由m棵互不相交的树的集合称为森林

1.2数的表示

树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法,孩子表示法、孩子兄弟表示法等等。我们这里就简单的了解其中最常用的孩子兄弟表示法。

typedef int DataType;
struct Node
{struct Node* firstChild1; struct Node* pNextBrother; DataType data; 
};

在这里插入图片描述

2.二叉树概念及结构

2.1二叉树的概念

一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
二叉树的特点:

  1. 每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
  2. 二叉树的子树有左右之分,其子树的次序不能颠倒。

2.2数据结构中的二叉树

在这里插入图片描述

2.3特殊的二叉树

  1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。### 3.2.1堆排序也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。
    在这里插入图片描述

2.4二叉树的存储结构

二叉树一般可以使用两种存储结构,一种顺序结构,一种链式结构。

2.4.1顺序存储

顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
在这里插入图片描述

2.4.2链式存储

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前介绍的是二叉链,后面课程学到高阶数据结构如红黑树等会用到三叉链。

   // 二叉链struct BinaryTreeNode{struct BinTreeNode* _pLeft; // 指向当前节点左孩子struct BinTreeNode* _pRight; // 指向当前节点右孩子BTDataType _data; // 当前节点值域};

在这里插入图片描述

2.5二叉树的性质

  1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1) 个结点.
  2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h- 1.
  3. 对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有n0=n2+1
  4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=Log2(n+1). (ps:Log2(n+1)是log以2为底,n+1为对数)
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:
  1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点
  2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子
  3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

3.堆的概念及结构

在上面2.4.1中提到了堆,那么什么是堆呢?
堆从分类来分,分为大堆和小堆。从性质来看,堆其实是一个特别的完全二叉树,这个特别的完全二叉树中所有的父亲节点的值要么都大于等于子节点值(大堆),要么小于等于子节点值(小堆)。
在这里插入图片描述
堆的性质:
1.堆中某个节点的值总是不大于或不小于其父节点的值;

2.堆总是一棵完全二叉树。

3.1堆的实现

3.1.1堆的创建

首先先定义一个堆
.h文件

#include<assert.h>
#include<stdbool.h>
#include<time.h>
typedef int HeapDateType;
typedef struct Heap
{HeapDateType* arr;int size;int capacity;
}HP;
//初始化
void HeapInit(HP* hp);
//销毁
void HeapDestory(HP* hp);

.c文件

#include"Heap.h"
//初始化
void HeapInit(HP* hp)
{hp->arr = NULL;hp->capacity = hp->size = 0;
}
//销毁
void HeapDestory(HP* hp)
{assert(hp);free(hp->arr);hp->capacity = hp->size = 0;
}

3.1.2堆的插入

堆的插入思路:
在这里插入图片描述
堆的插入代码:
.h文件

//插入数据
void HeapPush(HP* hp, HeapDateType x);
//向上调整
void AjustUp(HeapDateType* arr, int child);

.c文件

//向上调整
void AjustUp(HeapDateType* arr, int child)
{assert(arr);int parent = (child - 1) / 2;while (child > 0){//大于号是大堆,小于号是小堆if (arr[child] > arr[parent]){Swap(&arr[parent], &arr[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}//插入数据
void HeapPush(HP* hp, HeapDateType x)
{//检查一下是否需要扩容if (hp->capacity == hp->size){hp->capacity = hp->capacity == 0 ? 4 : hp->capacity * 2;}HeapDateType* ptr = (HeapDateType*)realloc(hp->arr, hp->capacity * sizeof(HeapDateType));if (ptr == NULL){perror("HeapPushBack::realloc");}hp->arr = ptr;hp->arr[hp->size++] = x;AjustUp(hp->arr, hp->size - 1);
}

3.1.3堆顶的删除

堆顶的删除思路:
在这里插入图片描述
堆顶删除的代码:
.h文件

//删除堆顶数据
void HeapPop(HP* hp);
//向下调整
void AjustDown(HeapDateType* arr, int len, int parent);

.c文件

//向下调整
void AjustDown(HeapDateType* arr, int len, int parent)
{assert(arr);int child = parent * 2 + 1;while (child < len){//大于号是大堆,小于号是小堆if (child + 1 < len && arr[child + 1] > arr[child]){child++;}//大于号是大堆,小于号是小堆if (arr[child] > arr[parent]){Swap(&arr[parent], &arr[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}//删除堆顶数据
void HeapPop(HP* hp)
{assert(hp);assert(!HeapIsEmpty(hp));//交换Swap(&hp->arr[0], &hp->arr[hp->size - 1]);hp->size--;//向下调整AjustDown(hp->arr, hp->size, 0);}

3.1.4堆的代码实现

.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include<time.h>
typedef int HeapDateType;
typedef struct Heap
{HeapDateType* arr;int size;int capacity;
}HP;
//初始化
void HeapInit(HP* hp);
//销毁
void HeapDestory(HP* hp);
//插入数据
void HeapPush(HP* hp, HeapDateType x);
//删除堆顶数据
void HeapPop(HP* hp);
//打印
void HeapPrint(HP* hp);
//向上调整
void AjustUp(HeapDateType* arr, int child);
//向下调整
void AjustDown(HeapDateType* arr, int len, int parent);
//断空
bool HeapIsEmpty(HP* hp);
//堆大小
int HeapSize(HP* hp);
//堆顶
HeapDateType HeapTop(HP* hp);
//交换
void Swap(HeapDateType* px, HeapDateType* py);

.c文件

#include"Heap.h"
//交换
void Swap(HeapDateType* px, HeapDateType* py)
{HeapDateType tmp = *px;*px = *py;*py = tmp;
}//初始化
void HeapInit(HP* hp)
{hp->arr = NULL;hp->capacity = hp->size = 0;
}//销毁
void HeapDestory(HP* hp)
{assert(hp);free(hp->arr);hp->capacity = hp->size = 0;
}//向上调整
void AjustUp(HeapDateType* arr, int child)
{assert(arr);int parent = (child - 1) / 2;while (child > 0){//大于号是大堆,小于号是小堆if (arr[child] > arr[parent]){Swap(&arr[parent], &arr[child]);child = parent;parent = (child - 1) / 2;}else{break;}}
}//向下调整
void AjustDown(HeapDateType* arr, int len, int parent)
{assert(arr);int child = parent * 2 + 1;while (child < len){//大于号是大堆,小于号是小堆if (child + 1 < len && arr[child + 1] > arr[child]){child++;}//大于号是大堆,小于号是小堆if (arr[child] > arr[parent]){Swap(&arr[parent], &arr[child]);parent = child;child = parent * 2 + 1;}else{break;}}}//插入数据
void HeapPush(HP* hp, HeapDateType x)
{//检查一下是否需要扩容if (hp->capacity == hp->size){hp->capacity = hp->capacity == 0 ? 4 : hp->capacity * 2;}HeapDateType* ptr = (HeapDateType*)realloc(hp->arr, hp->capacity * sizeof(HeapDateType));if (ptr == NULL){perror("HeapPushBack::realloc");}hp->arr = ptr;hp->arr[hp->size++] = x;AjustUp(hp->arr, hp->size - 1);}//删除堆顶数据
void HeapPop(HP* hp)
{assert(hp);assert(!HeapIsEmpty(hp));//交换Swap(&hp->arr[0], &hp->arr[hp->size - 1]);hp->size--;//向下调整AjustDown(hp->arr, hp->size, 0);}//断空
bool HeapIsEmpty(HP* hp)
{return hp->size == 0;
}//堆大小
int HeapSize(HP* hp)
{return hp->size;
}//堆顶
HeapDateType HeapTop(HP* hp)
{assert(!HeapIsEmpty(hp));return hp->arr[0];
}//打印
void HeapPrint(HP* hp)
{for (int i = 0; i < hp->size; i++){printf("%d ", hp->arr[i]);}printf("\n");
}

3.1.5建堆时间复杂度

说到时间复杂度,就要考虑到最坏情况。那么假设完全二叉树的每一个节点都需要调整需要多少时间复杂度呢?
因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来看的就是近似值,多几个节点不影响最终结果):
在这里插入图片描述

3.2堆的应用

3.2.1堆排序

堆排序的思路:
在这里插入图片描述
建堆代码(方法1):

//构建大堆
//方法1
for (int i = 1; i < n; i++)
{AjustUp(a, i);
}

建堆代码(方法2):

//方法2
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{AjustDown(a, n, i);
}

堆排序代码:

void HeapSort(int* a, int n)
{assert(a);//构建大堆//方法1//for (int i = 1; i < n; i++)//{//	AjustUp(a, i);//}	//方法2for (int i = (n - 1 - 1) / 2; i >= 0; i--){AjustDown(a, n, i);}for (int end = n - 1; end > 0; end--){Swap(&a[end], &a[0]);AjustDown(a, end, 0);}
}

3.2.2TOP-K问题

TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素,一般情况下数据量都比较大。
比如:专业前10名、世界500强、富豪榜、游戏中前100的活跃玩家等。
对于Top-K问题,能想到的最简单直接的方式就是排序,但是:如果数据量非常大,排序就不太可取了(可能数据都不能一下子全部加载到内存中)。最佳的方式就是用堆来解决,基本思路如下:

  1. 用数据集合中前K个元素来建堆
    ·前k个最大的元素,则建小堆
    ·前k个最小的元素,则建大堆
  2. 用剩余的N-K个元素依次与堆顶元素来比较,不满足则替换堆顶元素
    TOP-K代码:
//在n个数中找出最大的前K个
void PrintTopK(int* arr, int n, int k)
{HP hp;HeapInit(&hp);//先建立一个小堆for (int i = 0; i < k; i++){HeapPush(&hp, arr[i]);}for (int i = k; i < n; i++){if (arr[i] > hp.arr[0]){HeapPop(&hp);HeapPush(&hp, arr[i]);}}for (int i = 0; i < k; i++){printf("%d ", hp.arr[i]);}printf("\n");
}void test02()
{int arr[10000] = { 0 };for (int i = 0; i < 10000; i++){int t = rand() % 10000;arr[i] = t;}arr[131] = 10001;arr[141] = 10002;arr[151] = 10003;arr[161] = 10004;arr[171] = 10005;arr[181] = 10006;arr[191] = 10007;arr[1311] = 10008;arr[885] = 10009;arr[240] = 10010;PrintTopK(arr, 10000, 10);
}

4.二叉树链式结构的实现

4.1二叉树的定义

typedef char BTDataType;
typedef struct BinaryTree
{struct BinaryTree* left;struct BinaryTree* right;BTDataType val;
}BTNode;

4.2二叉树的遍历

4.2.1二叉树的前序递归遍历

前序遍历可以想象为,一个小人从一棵二叉树根节点为起点,沿着二叉树外沿,逆时针走一圈回到根节点,路上遇到的元素顺序,就是先序遍历的结果
在这里插入图片描述

//二叉树前序遍历  根  左子树  右子树
void PreOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}else{printf("%c ", root->val);PreOrder(root->left);PreOrder(root->right);}
}

4.2.2二叉树的中序递归遍历

中序遍历也可以看成,二叉树每个节点,垂直方向投影下来(可以理解为每个节点从最左边开始垂直掉到地上),然后从左往右数,得出的结果便是中序遍历的结果。
在这里插入图片描述

//二叉树中序遍历  左子树  根  右子树
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}else{InOrder(root->left);printf("%c ", root->val);InOrder(root->right);}
}

4.2.3二叉树的后序递归遍历

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。

后序遍历就像是剪葡萄,我们要把一串葡萄剪成一颗一颗的。
就是围着树的外围绕一圈,如果发现一剪刀就能剪下的葡萄(必须是一颗葡萄)(也就是葡萄要一个一个掉下来,不能一口气掉超过1个这样),就把它剪下来,组成的就是后序遍历了。
在这里插入图片描述

//二叉树后序遍历   左子树 右子树  根
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}else{PostOrder(root->left);PostOrder(root->right);printf("%c ", root->val);}
}

口诀

前序遍历: 先根 再左 再右

中序遍历: 先左 再根 再右

后序遍历: 先左 再右 再根

4.2.4二叉树的层序遍历

层次遍历很好理解,就是从根节点开始,一层一层,从上到下,每层从左到右,依次写值就可以了。
在这里插入图片描述
在这里插入图片描述

//层序遍历
void LevelOrder(BTNode* root)
{Queue q;Init_Queue(&q);//先入根节点Push_Queue(&q, root);while (!Empty(&q)){BTNode* front = Front_Queue(&q);//出当前节点Pop_Queue(&q);printf("%c ", front->val);if (front->left != NULL){Push_Queue(&q,front->left);}if (front->right != NULL){Push_Queue(&q, front->right);}}printf("\n");
}

4.3二叉树节点数

//二叉树结点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}else{return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);}
}

4.4二叉树叶子节点数

当一个节点的左孩子和右孩子都为空时,该节点就是叶子节点。

//二叉数叶子结点个数
int BinaryTreeLeafSize(BTNode* root)
{//当左孩子和右孩子都是空的就是叶子结点if (root == NULL){return 0;}if (root->left = NULL && root->right == NULL){return 1;}else{return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);}
}

4.5二叉树第K层节点数

求第K层节点数的问题可以转换成左子树第K-1层节点数+右子树第K-1层节点数,由此递归下去。

//二叉树第K层节点数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k==1){return 1;}int LeftKSize = BinaryTreeLevelKSize(root->left, k - 1);int RightKSize = BinaryTreeLevelKSize(root->right, k - 1);return LeftKSize + RightKSize;
}

4.6二叉树查找值为X的节点

//二叉树查找值为X的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->val = x){return root;}BTNode* LeftNode = BinaryTreeFind(root->left, x);if (LeftNode != NULL){return LeftNode;}BTNode* RightNode = BinaryTreeFind(root->right, x);	if (RightNode != NULL){return RightNode;}return NULL;
}

4.7二叉树的深度

//二叉树深度
int BinaryDepth(BTNode* root)
{if (root == NULL){return 0;}int LeftDepth = BinaryDepth(root->left);int RightDepth = BinaryDepth(root->right);return LeftDepth > RightDepth ? LeftDepth + 1 : RightDepth + 1;
}

4.8判断二叉树是否是完全二叉树

判断的思路:
在这里插入图片描述

//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue q;Init_Queue(&q);Push_Queue(&q, root);while (Empty(&q)){BTNode* front = Front_Queue(&q);Pop_Queue(&q);if (front){Push_Queue(&q, front->left);Push_Queue(&q, front->right);}else{break;}}while (!Empty(&q)){BTNode* front = Front_Queue(&q);if (front != NULL){Destory_Queue(&q);return false;}Pop_Queue(&q);}Destory_Queue(&q);return true;
}

4.9二叉树完整代码

.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"Queue.h"typedef char BTDataType;
typedef struct BinaryTree
{struct BinaryTree* left;struct BinaryTree* right;BTDataType val;
}BTNode;//二叉树前序遍历
void PreOrder(BTNode* root);//二叉树中序遍历
void InOrder(BTNode* root);//二叉树后序遍历
void PostOrder(BTNode* root);//层序遍历
void LevelOrder(BTNode * root);//二叉树结点个数
int BinaryTreeSize(BTNode* root);//二叉数叶子结点个数
int BinaryTreeLeafSize(BTNode* root);//二叉树第K层节点数
int BinaryTreeLevelKSize(BTNode* root, int k);//二叉树查找值为X的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode * root);//二叉树深度
int BinaryDepth(BTNode* root);//二叉树销毁
void BinaryTreeDestory(BTNode *root);

.c文件

#include"BinaryTree.h"//二叉树前序遍历  根  左子树  右子树
void PreOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}else{printf("%c ", root->val);PreOrder(root->left);PreOrder(root->right);}
}//二叉树中序遍历  左子树  根  右子树
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}else{InOrder(root->left);printf("%c ", root->val);InOrder(root->right);}
}//二叉树后序遍历   左子树 右子树  根
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}else{PostOrder(root->left);PostOrder(root->right);printf("%c ", root->val);}
}//层序遍历
void LevelOrder(BTNode* root)
{Queue q;Init_Queue(&q);Push_Queue(&q, root);while (!Empty(&q)){BTNode* front = Front_Queue(&q);Pop_Queue(&q);printf("%c ", front->val);if (front->left != NULL){Push_Queue(&q,front->left);}if (front->right != NULL){Push_Queue(&q, front->right);}}printf("\n");
}//二叉树结点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}else{return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);}
}//二叉数叶子结点个数
int BinaryTreeLeafSize(BTNode* root)
{//当左孩子和右孩子都是空的就是叶子结点if (root == NULL){return 0;}if (root->left = NULL && root->right == NULL){return 1;}else{return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);}
}//二叉树第K层节点数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k==1){return 1;}int LeftKSize = BinaryTreeLevelKSize(root->left, k - 1);int RightKSize = BinaryTreeLevelKSize(root->right, k - 1);return LeftKSize + RightKSize;
}//二叉树查找值为X的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->val = x){return root;}BTNode* LeftNode = BinaryTreeFind(root->left, x);if (LeftNode != NULL){return LeftNode;}BTNode* RightNode = BinaryTreeFind(root->right, x);	if (RightNode != NULL){return RightNode;}return NULL;
}//判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{Queue q;Init_Queue(&q);Push_Queue(&q, root);while (Empty(&q)){BTNode* front = Front_Queue(&q);Pop_Queue(&q);if (front){Push_Queue(&q, front->left);Push_Queue(&q, front->right);}else{break;}}while (!Empty(&q)){BTNode* front = Front_Queue(&q);if (front != NULL){Destory_Queue(&q);return false;}Pop_Queue(&q);}Destory_Queue(&q);return true;}//二叉树深度
int BinaryDepth(BTNode* root)
{if (root == NULL){return 0;}int LeftDepth = BinaryDepth(root->left);int RightDepth = BinaryDepth(root->right);return LeftDepth > RightDepth ? LeftDepth + 1 : RightDepth + 1;
}//二叉树销毁  使用递归  先释放左子树在释放右子树最后释放根
void BinaryTreeDestory(BTNode* root)
{if (root == NULL){return;}BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);
}

http://www.ppmy.cn/news/33373.html

相关文章

【C语言】位段(详解)

目录1. 什么是位段2. 位段的内存分配1. 什么是位段 结构体中可以位为单位来指定其成员所占内存长度&#xff0c;这种以位为单位的成员称为“位段”或“位域”( bit field) 。利用位段能够用较少的位数存储数据。 位段的好处&#xff1a; 位段可以使数据单元节省储存空间位段可…

Spring Profiles and @Profile

1. Overview In this tutorial, we’ll focus on introducing Profiles in Spring. Profiles are a core feature of the framework — allowing us to map our beans to different profiles — for example, dev, test, and prod. We can then activate different profiles…

Keil5安装和使用小记

随着keil版本的更新&#xff0c;一些使用问题一随之产生。本文针对安装目前最新版本keil软件和使用问题做一些总结。 目录1 Keil5下载&安装1.1 官网下载链接1.2 软件安装1.2.1 安装说明1.2.2 关于 51 和 ARM 共存的问题1.3 软件破解2 pack包安装 & 破解2.1 下载2.2 安装…

Go语言必知必会100问题-02 减少代码的嵌套层数

减少代码的嵌套层数 软件开发中的“心智模型”用于描述开发人员在编码时心理活动&#xff0c;每段代码其实是人在编写这段代码时的心智模型投射&#xff0c;不能把代码看成是客观的存在&#xff0c;而是主观的产物&#xff0c;参合了当时心理活动或各种直觉感知。在编程时&…

特征建模之FiBiNet

FiBiNet: Combine Feature importance and Bilinear feature Interaction for Click-Through Rate Prediction https://arxiv.org/abs/1905.0943 一、特征建模的重要性 推荐领域的深度CTR模型中的参数主要由两部分构成&#xff1a;特征Embedding参数和MLP层参数&#xff0c;假…

【基于协同过滤算法的推荐系统项目实战-2】了解协同过滤推荐系统

本文目录1、推荐系统的关键元素1.1 数据1.2 算法1.3 业务领域1.4 展示信息2、推荐算法的主要分类2.1 基于关联规则的推荐算法基于Apriori的算法基于FP-Growth的算法2.2 基于内容的推荐算法2.3 基于协同过滤的推荐算法3、推荐系统常见的问题1、冷启动2、数据稀疏3、不断变化的用…

Spark SQL函数定义【博学谷学习记录】

1 如何使用窗口函数窗口函数格式:分析函数 over(partition by xxx order by xxx [asc|desc] [rows between xxx and xxx])学习的相关分析函数有那些? 第一类: row_number() rank() dense_rank() ntile()第二类: 和聚合函数组合使用 sum() avg() max() min() count()第三类: la…

博客系统实现自动化测试

目录 一、设计博客系统的测试用例 二、利用测试用例进行测试 测试登录页面 界面测试 功能测试 测试博客列表页 界面测试 功能测试 测试博客详情页 界面测试 功能测试 博客编辑页测试 界面测试 功能测试 一、设计博客系统的测试用例 二、利用测试用例进行测…