【数据结构】 二叉树相关操作(详解)

概念及结构

二叉树基本概念

二叉树是结点的一个有限集合,该集合:
1.为空
2.由一个根节点和它的左子树与右子树构成
在这里插入图片描述
由上图可以得知:
1.二叉树不存在度大于2的结点
2.二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

特殊的二叉树

满二叉树

1.是一棵空树
2.非空,则该二叉树的每一层节点数达到该层若能容纳结点的最大值
也即:层数为k时,总结点数为2^k -1 个结点
在这里插入图片描述

完全二叉树

完全二叉树是由满二叉树引出的~~

1.是一棵空树
2.若不空,对于层数为k的二叉树来说:
k-1 层是一个 满二叉树
k从左到右依次按序排列剩余节点数
在这里插入图片描述

二叉树的性质

1.若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点
2.若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h -1 个
3.对任何一棵二叉树,若 度为 0 的结点个数 为 n0, 度为 2 的结点个数 为 n2, 则有关系 n0 = n2+1成立

对于3的证明如下:
在这里插入图片描述

4.若规定根节点的层数为1,具有n个结点的满二叉树的深度为h = log(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 否则无左孩子
3.若2i+2 < n,右孩子序号:2i+2 否则无右孩子

对于5的简单说明
在这里插入图片描述

二叉树的存储结构

对于二叉树来说,一般有两种存储结构

顺序存储

数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中只有堆才会使用数组来存储。
二叉树顺序存储在物理上是一个数组,在逻辑上是一棵二叉树

完全二叉树的顺序存储
在这里插入图片描述

非完全二叉树的顺序存储

在这里插入图片描述

链式存储

用链表来表示一棵二叉树
链表中的每个结点由3部分组成,数据域和左右指针域,其中左右指针域分别指向该结点的左右孩子所在结点,目前采用二叉链来存储。
在这里插入图片描述
对应的数据结构类型为

typedef char BTDataType;

typedef struct BinaryTree
{
	BTDataType data;//数据域
	struct BinaryTree* left;//指向当前结点的左孩子
	struct BinaryTree* right;//指向当前结点的右孩子
}BTNode;

二叉树的顺序结构及实现

前面提到,顺序存储对于完全二叉树比较实用,顺序存储主要是用来对堆进行存储。下面介绍堆的相关概念

堆的概念及结构

概念:有一组数据将其按照完全二叉树的顺序存储方式存储在数组中。这一组数据需要满足以下条件:
该二叉树的父节点始终大于它的孩子结点,称它为大堆
在这里插入图片描述

该二叉树的父节点始终小于于它的孩子结点,称它为小堆
在这里插入图片描述

堆的性质

堆中某个结点的值总是不大于或不小于其父节点的值;
堆总是一棵完全二叉树。

堆的实现

问题引入(以小堆为例)

在这里插入图片描述
如上图所示:红色部分都已经符合小堆的特性,那么我们应该如何调整绿色部分的结点,使整个二叉树是一个小堆呢?

我们只需要进行以下步骤:
1.找到该结点左右孩子中较小的结点
2.交换这两个结点
3.对已交换的部分进行同样的操作(1.2)
4.直到该结点没有孩子结点时停止

那么对于上述情况的操作步骤如下图所示:
在这里插入图片描述

调整算法

针对于上述情况的执行过程,大佬们总结出一个算法来实现这样的功能,那就是向下调整算法

前提:左右子树必须是一个堆时,才能调整

所以执行步骤为:
1.找到倒数第一个非叶子结点,从该结点开始调整
2.找到该结点左右孩子中较小的结点
3.交换这两个结点
4.对已交换的部分进行同样的操作(2.3)
5.直到该结点没有孩子结点时表示一轮调整完成
6.紧接着调整前一个非叶子结点,直到调整到根节点停止
7.此时构建的堆为小堆

若要构建大堆,只需要每次交换时将上述的较小换为较大即可

堆的创建

对于给定的一个任意数据的数组,起初它是不符合堆的特性的。
由于根节点的左右子树并不是堆,所以我们从倒数第一个非叶子结点开始调整,一直调整到根节点,就可以调整为堆

e.g int a[] = {1,5,3,8,7,6};
调整成一个大堆
对于该数组的调整过程如下图所示:
在这里插入图片描述
在每一次调换元素的时候,有可能破坏其子堆的结构,因此在每一次发生元素交换的时候,都需要递归调用重新构造堆的结构。

建堆的时间复杂度

由于堆是一个完全二叉树,而满二叉树也是一种特殊的完全二叉树,因此使用满二叉树来求解时间复杂度(时间复杂度本里就是一个近似值,多几个结点不影响最终结果)
设树的高度为h
第1层, 2^0个结点,需要向下移动h-1层
第2层, 2^1个结点,需要向下移动h-2层
第3层, 2^2个结点,需要向下移动h-3层
第4层, 2^3个结点,需要向下移动h-4层

第h-1层, 2^(h-2)个结点,需要向下移动1层

因此需要移动结点总的移动步数为:
在这里插入图片描述

堆的插入

将要插入的数据放在数组的末尾,再进行向上调整算法直到满足堆为止
e.g 向建堆时的例子中插入数据100
则新数组为{8,7,6,5,1,3,100}
对应的调整过程见下图
在这里插入图片描述

堆的删除

删除堆是删除堆顶的数据,将堆顶的数据与最后一个数据交换,然后删除最后一个元素,再进行向下调整算法
e.g 删除刚刚建好的大堆中的堆顶元素100
在这里插入图片描述

堆的代码实现

heap.h
申明了一些堆的基本操作

#pragma once

#include <stdio.h>
#include <windows.h>


typedef int HPDataType;

//定义一个返回值为int且有两个int型参数的函数指针类型
typedef int(*Com) (HPDataType, HPDataType);

typedef struct Heap
{
	HPDataType* arr;
	int size;
	int capacity;
	Com Compare;
}Heap;

// 堆的构建
extern void HeapCreat(Heap* hp, HPDataType* a, int n,Com com);
// 堆的销毁
extern void HeapDestory(Heap* hp);

// 取堆顶的数据
extern HPDataType HeapTop(Heap* hp);
//堆的数据个数
extern int HeapsSize(Heap* hp);
// 堆的判空
extern int HeapEmpty(Heap* hp);
// 堆的插入
extern void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
extern void HeapPop(Heap* hp);

extern void HeapTest();

heap.c

#include "heap.h"
#include <malloc.h>
#include <assert.h>

static void Swap(HPDataType* xp, HPDataType* yp)
{
	HPDataType temp = *xp;
	*xp = *yp;
	*yp = temp;
}

static void AdjustDown(Heap* hp, int parent)
{
	assert(hp);
	//1.child标记左右孩子较小的,默认父节点的左孩子为左右孩子中较小的一个
	int child = 2 * parent + 1;
	while (child < hp->size)
	{
		//2.确定实际较小的孩子结点
		if (child + 1 < hp->size && hp->Compare(hp->arr[child+1],hp->arr[child]))
		{
			child += 1;
		}
		//3.将较小的孩子结点与父节点比较,判断是否需要交换
		if (hp->Compare(hp->arr[child] , hp->arr[parent]))
		{
			Swap(&hp->arr[child], &hp->arr[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			return;
		}
	}

}

// 堆的构建
void HeapCreat(Heap* hp, HPDataType* a, int n,Com com)
{
	assert(a);
	assert(hp);

	//1.开辟空间
	hp->arr = (HPDataType*)malloc(sizeof(HPDataType)*n);
	if (NULL == hp->arr)
	{
		assert(0);
		return;
	}
	//2.空间开辟成功,对新空间初始化
	for (int i = 0; i < n; i++)
	{
		hp->arr[i] = a[i];
	}
	hp->capacity = n;
	hp->size = n;
	hp->Compare = com;
	//3.使用向下调整方法进行堆的构建
	int parent = (n - 2) / 2;
	while (parent >= 0)
	{
		AdjustDown(hp, parent);
		parent--;
	}
}


// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	if (hp->arr)
	{
		free(hp->arr);
		hp->arr = NULL;
		hp->capacity = hp->size = 0;
	}
}

// 堆的判空  返回值:1  空的   0 不空
int HeapEmpty(Heap* hp)
{
	assert(hp);
	return hp->size == 0? 1:0;
}

// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	if (!HeapEmpty(hp))
	{
		return hp->arr[0];
	}
	else
	{
		printf("堆为空!返回值为-1\n");
	}
	return -1;
}

//或取堆的元素个数
int HeapsSize(Heap* hp)
{
	assert(hp);
	return hp->size;
}

// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	//1.将堆顶元素与堆的最后一个元素交换
	Swap(&hp->arr[0], &hp->arr[hp->size - 1]);
	//2.对hp的size-1,控制有效元素个数
	hp->size--;
	if (hp->size == 0)
	{
		free(hp->arr);
		hp->arr = NULL;
		hp->capacity = 0;
	}
	//3.对新的堆顶元素进行向下调整操作
	AdjustDown(hp,0);
}

//扩容函数
static void HeapExpand(Heap* hp)
{
	int newCapacity = hp->capacity * 2;
	//1.开辟新空间
	HPDataType *tmp = (HPDataType*)malloc(sizeof(HPDataType)*newCapacity);
	if (NULL == tmp)
	{
		assert(0);
		return;
	}
	//2.将旧空间的内容拷贝至新空间
	memcpy(tmp, hp->arr, sizeof(HPDataType)*hp->capacity);
	//3.释放旧空间
	free(hp->arr);
	hp->arr = tmp;
	hp->capacity = newCapacity;
}

//向上调整
static void AdjustUp(Heap* hp)
{
	int child = hp->size - 1;
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (hp->Compare(hp->arr[child] , hp->arr[parent]))
		{
			Swap(&hp->arr[parent], &hp->arr[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			return;
		}
	}
}

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	//1.判断能否进行插入
	if (hp->size == hp->capacity)
	{
		//不能插入,先扩容
		HeapExpand(hp);
	}
	//2.执行插入操作
	hp->arr[hp->size] = x;
	hp->size++;
	AdjustUp(hp);
}

/

static int Less(HPDataType left,HPDataType right)
{
	return left < right;
}

static int Greater(HPDataType left, HPDataType right)
{
	return left > right;
}


void HeapTest()
{
	Heap hp;
	HPDataType array[] = { 16, 72, 31, 23, 94, 53 };

	printf("初始化堆\n");
	HeapCreat(&hp, array, sizeof(array) / sizeof(array[0]), Greater);
	printf("堆顶元素:%d\n", HeapTop(&hp));
	printf("堆的有效元素个数:%d\n",HeapsSize(&hp));
	

	printf("插入新元素后\n");
	HeapPush(&hp, 1000);
	printf("堆顶元素:%d\n", HeapTop(&hp));
	printf("堆的有效元素个数:%d\n", HeapsSize(&hp));

	printf("删除堆顶元素后\n");
	HeapPop(&hp);
	printf("堆顶元素:%d\n", HeapTop(&hp));
	printf("堆的有效元素个数:%d\n", HeapsSize(&hp));

	HeapDestory(&hp);
}

}

main.c

#include "heap.h"

int main()
{
	HeapTest();
	system("pause");
	return 0;

}

在这里插入图片描述
堆的相关操作见源码 堆!

堆的应用

堆排序

顾名思义:就是利用堆的思想来对数据进行排序
主要分成两个步骤:
1.建堆
升序:建大堆
降序:建小堆
2.利用堆删除的思想来进行排序
利用向下调整算法实现

e.g 对数组{8,7,6,5,1,3}进行升序排列
首先,升序需要建立大堆
在这里插入图片描述
其次:对该对结构进行删除的操作
主要过程如下
在这里插入图片描述
实现代码

#include "heap.h"

static void Swap(int* xp,int* yp)
{
	int temp = *xp;
	*xp = *yp;
	*yp = temp;
}
static void AdjustHeap(int* array, int parent, int size)
{
	//使用child来标记parent的较小孩子结点
	//由于堆是一个完全二叉树,所以对于任意一个父节点来说,
	//它一定会有左孩子,但不一定有右孩子,
	//所以先让child标记它的左孩子
	int child = parent * 2 + 1;
	//左孩子存在
	if (child < size)
	{
		//右孩子存在,并且右孩子小于左孩子
		if (child + 1 < size && array[child] > array[child + 1])
		{
			//child重新标记右孩子
			child += 1;
		}
		if (array[parent]>array[child])
		{
			Swap(&array[parent], &array[child]);
		}
		else
		{
			return;
		}
	}
}

void  HeapSort(int *array, int size)
{
	//1.找到第一个非叶节点
	int parent = (size - 1 - 1) / 2;
	//2.从该结点开始,采用向下调整的方式进行排列
	//3.直到数组的第一个元素被排完-后结束
	while (parent >= 0)
	{
		AdjustHeap(array, parent, size);
		parent--;
	}
}

void testHeap()
{
	int array[] = { 16, 72, 31, 23, 94, 53 };
	int size = sizeof(array) / sizeof(array[0]);
	printf("Before:\n");
	for (int i = 0; i < size; i++)
	{
		printf("%d ",array[i]);
	}
	printf("\n");

	HeapSort(array, size);

	printf("After:\n");
	for (int i = 0; i < size; i++)
	{
		printf("%d ", array[i]);
	}
	printf("\n");
}

源码见 堆排序

Top-K问题

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

  1. 用数据集合中前k个元素来建堆
    前k个最大的元素 建小堆
    前k个最小的元素 建大堆
  2. 用剩余的N-k个元素依次与堆顶元素比较,不满足则替换堆顶元素,然后再重新调整为堆
  3. 将剩余N-k个元素依次与堆顶元素比完之后,堆中剩余的k个元素就是所求的前k个最小的或者最大的元素。

参考代码如下:

void PrintTopK(int* a, int n, int k)
{
	Heap hp;
	//找前k个最小的,建立大堆
	//HeapCreat(&hp, a, k, Greater);

	//找前k个最大的,建立小堆
	HeapCreat(&hp, a, k, Less);

	for (int i = k; i < n; i++)
	{
		if (a[i]>HeapTop(&hp))
		{
			//删除堆顶元素
			HeapPop(&hp);
			//将元素a[i]插入堆中
			HeapPush(&hp, a[i]);
		}
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ",HeapTop(&hp));
		HeapPop(&hp);
	}
	printf("\n");
}

void TestTopk()
{
	int n = 10000;
	int* a = (int*)malloc(sizeof(int)*n);
	srand(time(0));
	//随机生成10000个数存入数组,保证元素都小于1000000
	for (int i = 0; i < n; ++i)
	{
		a[i] = rand() % 1000000;
	}
	//确定10个最大的数
	a[5] = 1000000 + 1;
	a[1231] = 1000000 + 2;
	a[531] = 1000000 + 3;
	a[5121] = 1000000 + 4;
	a[115] = 1000000 + 5;
	a[2335] = 1000000 + 6;
	a[9999] = 1000000 + 7;
	a[76] = 1000000 + 8;
	a[423] = 1000000 + 9;
	a[3144] = 1000000 + 10;

	PrintTopK(a, n, 10);
}

在这里插入图片描述
源码见 Top-K

二叉树的链式结构及实现

二叉树的四种遍历方式

前序

先访问根节点,再访问左子树再访问右子树
即 根左右

e.g 对于下图的二叉树访问序列为:ABDECFG
在这里插入图片描述

中序

先访问左子树再访问根节点再访问右子树
即 左根右
e.g 对于下图的二叉树访问序列为:DBEAFCG
在这里插入图片描述

后序

先访问左子树再访问又子树,最后访问根节点
即 左右根
e.g 对于下图的二叉树访问序列为:DEBFGCA
在这里插入图片描述

层序

将二叉树看作是以层为单位的树,按照每一层的方式访问树的每一个结点
e.g 对于下图的二叉树访问序列为:ABCDEFG
在这里插入图片描述
看似最简单的层序遍历应该如何实现呢?
猛地一看,貌似没有什么思路,别急,我们慢慢捋捋思路
首先,它是要按层遍历,那么我们可以将它放在一个容器内,然后将它再从该容器中取出来
存放
放的时候是按照从上到下,从左到右的顺序放,那么取的时候就要将先放进去的先取出来

看到这里是不是就有点思路了,先不管它是如何放进去的,就对于取元素来说,这不就是队列的特性吗

所以,我们对于二叉树的层序遍历需要借助队列来实现

具体步骤:

  1. 将根结点入队
  2. 队列不空时:
    将队首元素的左右孩子结点分别入队,然后将对首元素输出并出队列
  3. 当队列为空时,表明层序遍历已经结束,停止操作

比如上图的层序遍历过程如下:
在这里插入图片描述

二叉树的创建(重点)

如何创建一个二叉树?
现在我们假设按照前序的方式进行二叉树的创建
用户需要给定一个数组,数组中存放的是每个结点的data域,在这个数组中还应该有一些无效元素用来表示结点的左右孩子不存在这一情况
具体建立步骤如下:
1.为根结点分配空间,将根节点创建出来
2.递归创建根结点的左子树和右子树
3.递归过程中遇到无效值时,说明该结点没有左子树或者是右子树,此时应该回退到上一层
5.遍历整个数组,直到数组遍历完成,二叉树也就创建完成

e.g: 用户输入数组为“ABD##E#H##CF##G##”,其中#是无效元素,则对应的二叉树创建过程如下图:
在这里插入图片描述


// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* array, int size, int* index)
{
	BTNode* root = NULL;
	if (*index < size && array[*index] != ' ')
	{
		//创建根节点
		root = BuyBTNode(array[*index]);
		//递归创建左子树与右子树
		++(*index);
		root->left = BinaryTreeCreate(array, size, index);
		++(*index);
		root->right = BinaryTreeCreate(array, size, index);
	}

	return root;
}

注意函数中的第三个参数
它表示的是数组中待操作元素的下标,问题是它为什么要用指针呢?直接传值不行吗?
原因如下:
首先,我们对于二叉树的创建是通过递归来实现的,递归是一个函数自己调用自己的过程。
既然是函数调用,那么形参就是实参的一份临时拷贝,在函数执行开始时被创建,函数执行完毕后自动被销毁。
所以如果我们使用下标值作为函数参数,那么在函数执行完毕后,并不能改变该变量在该函数外的值,进而我们的程序就无法达到预期的结果。
因此,我们要传入指针,目的是能够将函数内部对该变量修改后的结果拿出来。

二叉树基本操作的源码

BinaryTree.h

#pragma once

#include <stdio.h>
#include <windows.h>

typedef  char BTDataType;

typedef  struct BTNode
{
	BTDataType data;
	struct BTNode* left;
	struct BTNode* right;
}BTNode;


// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
extern BTNode* BinaryTreeCreate(BTDataType* array, int size, int* index);
// 二叉树销毁
extern void BinaryTreeDestory(BTNode** root);

// 二叉树前序遍历 
extern void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
extern void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
extern void BinaryTreePostOrder(BTNode* root);
// 层序遍历
extern void BinaryTreeLevelOrder(BTNode* root);

// 二叉树节点个数
extern int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
extern int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
extern int BinaryTreeLevelKSize(BTNode* root, int k);
// 二叉树查找值为x的节点
extern BTNode* BinaryTreeFind(BTNode* root, BTDataType x);

// 判断二叉树是否是完全二叉树
extern int BinaryTreeComplete(BTNode* root);



extern void BTtest();

BinaryTree.c

#include "Binarytree.h"
#include "queue.h"
#include <assert.h>

static BTNode* BuyBTNode(BTDataType x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (NULL == node)
	{
		assert(0);
		return NULL;
	}
	node->data = x;
	node->left = node->right = NULL;
	return node;
}

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* array, int size, int* index)
{
	BTNode* root = NULL;
	if (*index < size && array[*index] != ' ')
	{
		//创建根节点
		root = BuyBTNode(array[*index]);
		//递归创建左子树与右子树
		++(*index);
		root->left = BinaryTreeCreate(array, size, index);
		++(*index);
		root->right = BinaryTreeCreate(array, size, index);
	}

	return root;
}

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	assert(root);
	if (*root)
	{
		BinaryTreeDestory(&(*root)->left);
		BinaryTreeDestory(&(*root)->right);
		free(*root);
		*root = NULL;
	}
}

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (NULL == root)
	{
		return;
	}
	printf("%c ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (NULL == root)
	{
		return;
	}
	BinaryTreeInOrder(root->left);
	printf("%c ", root->data);
	BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (NULL == root)
	{
		return;
	}

	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->data);
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	if (NULL == root)
	{
		return;
	}
	//借助队列实现
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* cur = QueueFront(&q);

		printf("%c ",cur->data );
		QueuePop(&q);
		if (cur->left)
		{
			QueuePush(&q, cur->left);
		}
		if (cur->right)
		{
			QueuePush(&q, cur->right);
		}
	}
	QueueDestroy(&q);
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (NULL == root)
	{
		return 0;
	}
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (NULL == root)
	{
		return 0;
	}
	if (NULL == root->left && NULL == root->right)
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (NULL == root || k < 1)
	{
		return 0;
	}

	if (k == 1)
	{
		return 1;
	}

	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

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

	if (NULL == root)
	{
		return NULL;
	}

	if (x == root->data)
	{
		return root;
	}

	if (ret = BinaryTreeFind(root->left, x))
	{
		return ret;
	}

	return BinaryTreeFind(root->right, x);
}

// 判断二叉树是否是完全二叉树    1---是完全二叉树   0----不是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	//利用层序遍历的思想,依靠队列实现判断
	//空树也是完全二叉树
	if (NULL == root)
	{
		return 1;
	}

	Queue q;
	QueueInit(&q);

	BTNode* cur = root;
	//1.将每一层的结点入队列(有空节点是按照NULL进行入队操作)
	QueuePush(&q, cur);
	while ((cur = QueueFront(&q)) != NULL)
	{
		QueuePop(&q);
		QueuePush(&q,cur->left);
		QueuePush(&q,cur->right);
	}
	//2.此时判断队列情况:若队列中结点全部为NULL则说明是完全二叉树,否则不是完全二叉树
	while (!QueueEmpty(&q))
	{
		cur = QueueFront(&q);
		
		if (cur != NULL)
		{
			return 0;
		}

		QueuePop(&q);
	}
	return 1;
}



void BTtest()
{
	BTNode* root = NULL;
	BTDataType array[] = "ABD  E H  CF  G  ";
	int index = 0;
	root = BinaryTreeCreate(array,sizeof(array)/sizeof(array[0]),&index);

	//前序
	BinaryTreePrevOrder(root);
	printf("\n");
	//中序
	BinaryTreeInOrder(root);
	printf("\n");
	//后序
	BinaryTreePostOrder(root);
	printf("\n");
	//层序
	BinaryTreeLevelOrder(root);
	printf("\n");

	printf("二叉树结点个数为:%d\n", BinaryTreeSize(root));

	printf("二叉树叶子结点个数为:%d\n", BinaryTreeLeafSize(root));

	int k = 4;
	printf("二叉树第 %d 层的结点个数为 %d\n",k, BinaryTreeLevelKSize(root,k));

	BTNode* cur = BinaryTreeFind(root,'D');
	if (cur)
	{
		printf("存在\n");
	}
	else
	{
		printf("不存在\n");
	}

	int flag = BinaryTreeComplete(root);
	if (flag)
	{
		printf("该二叉树是完全二叉树!\n");
	}
	else
	{
		printf("该二叉树不是完全二叉树!\n");
	}

}

具体源代码参考 BinaryTree

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Suk-god

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值