数据结构-二叉树(深度剖析)

一、树的概念

1,什么是树

日常中的树想必大家都知道吧,那么在数据结构中,树又是什么样子的呢?

像图片上的这样一个结构,就可以定义为树了,树是由多个节点结合起来的,像A,B,C,D,E等等,都是树的一个个节点。下面我们来说说树都有哪些概念又由哪些部分组成。

树的高度/深度:叶子节点到根节点的高度成为树的高度,如图中树的高度为3.

根节点:树的第一个节点,如图中A节点。

节点的度:一个节点拥有多少个子节点,就有多少度,如图中A的度为4.

叶节点:没有孩子节点的节点或度为0的节点,如图中F,G节点都没有孩子节点,则为叶节点。

分支节点:(除了根节点外 )度不为0的节点,如B,C,D,E节点。

数的度:最大节点的度称为树的度,如图中A的节点最大,此时度为4,则树的度为4。

在树的概念中,有父亲节点,孩子节点和兄弟节点的说法,此时我们可以看到,A是在第一排第一个节点,下面有一条线连接了B,C,D,E等节点,那我们就可以说A是他们的父亲节点,他们这些节点都是A节点的孩子,有了A才有了他们。而因为他们的父亲都是A节点,所以此时我们就可以说他们彼此是兄弟节点。

父亲节点:若含有一个或多个子节点,则称该节点为父亲节点。

孩子节点:一个节点含有且只含有一个子树的根节点,则称这个节点为该节点的子节点。

兄弟节点:两个节点的父节点相同,则称这两个节点为兄弟节点。

堂兄弟节点:两个节点的父亲节点为兄弟节点,那么这两个节点便成为堂兄弟节点,如图F,G节点。

祖先:这里可以理解为根节点,如图中A是所有节点的祖先。如果将B看作根节点,那么我们就说B是F的祖先。

子孙:所有的节点都是根节点的子孙。如果我们将B看作根节点,那么我们就说F是B的子孙。

2,树和森林

森林:由m(m>0)棵互不相交的树的集合组成。

如下面两棵树我们就可以说是一个森林,因为他们没有相连的节点。

二、二叉树的概念

1,什么是二叉树

二叉树的概念基本和树相同,唯一不同的是每个节点都只有一个或两个子节点,或者干脆没有子节点。

在任意一棵二叉树中,若终端结点(叶子节点)的个数为n0,度为2(如A,B,C节点)的结点数为n2,则n0=n2+1

叶子节点4=A+B+C+1。

2,满二叉树

满二叉树除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。

3,完全二叉树

叶子节点只会出现在最后2层,且最后1层的叶子节点都靠左对齐。

完全二叉树从根节点至倒数第2层是一颗满二叉树。

度为1的节点只有左子树。

一颗有n个节点的完全二叉树(n>0),从上到下,从左到右节点从1开始进行编号,对任意第i个节点

  1. 如果 i = 1 它是根节点

  2. 如果 i > 1 他的父节点编号 floor(i/2)

  3. 如果2i<=n, 他的左子节点编号为2i

  4. 如果2i>n,它无左子节点

  5. 如果 2i+1 <= n,它的右子节点编号为 2i+1

  6. 如果 2i+1 >n,它无右子节点。

注意:满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树 

完全二叉树的每一层(除最后一层)都满足2的次方,那么我们就可以用2的次方和完全二叉树的性质来求出该树的节点数。

三、基于二叉树的堆排序

1,什么是堆

我们给定一个数组,随机放入几个值,比如我写的这个数组int arr[]={12,8,9,4,6,5};

堆是一种满足以下条件的树:堆中的每一个节点值都大于等于(或小于等于)子树中所有节点的值。或者说,任意一个节点的值都大于等于(或小于等于)所有子节点的值。

我们把刚刚写的数组按二叉树进行排序,可以发现,刚好满足每一个节点值都大于等于子树中所有节点的值。那我们就说这个数组是一个堆排序的数组。

2,大堆小堆

大堆:堆中的每一个节点值都大于等于子树中所有节点的值。或者说,任意一个节点的值都大于等于所有子节点的值。

小堆:堆中的每一个节点值都小于等于子树中所有节点的值。或者说,任意一个节点的值都小于等于所有子节点的值。

简而言之,大堆越来越小,小堆越来越大。

 

大堆

小堆

3,堆实现

堆是一种比较优的数据结构,不过限制也比较大,因为它的实现是基于二叉树的,所以实际使用也是在二叉树的相关问题中。

堆的结构体创建

我们下面讲到的都是数组方式存储的堆的实现,实现起来也比较方便,后面讲二叉树的实现会使用链式结构访问。当然用链式结构创建堆也是可以的,只是我这里用的数组实现。

初始化堆

销毁堆

打印堆

堆的插入

这里我用的是大堆的思想,小堆与大堆只是符号上有区别,更换一下符号就是小堆了。

每插入一个数据都进行一次向上调整,始终保持最大的数在最上面。

堆的删除

这里我们要删除堆的第一个元素,如果直接删除,后续元素逐个覆盖可以实现吗?当然是可以的,但是我们会遇到一个问题,就是覆盖之后的数组还是堆吗,显然不是,下图可以看到,这里已经没有大堆的思想了 。

那么最好的办法是什么呢,我们可以先将第一个元素和最后一个元素调换,然后删除最后一个元素。这里肯定有人说了,4在第一个元素不也不是大堆了吗,是的,所以这只是我们的第一步,第二步我们把这个数组再向下调整一次,就变成我们的大堆了。

这里我们就实现了一个删除后数组仍然是堆的一个办法。

堆的顶部元素

因为我们是用的数组存储,已经经过堆处理了,所以只需要访问数组第一个元素即可。

堆的元素个数

检查堆是否为空

堆的创建(已有数组数据)

这是建立在已经有一个数组,需要我们进行堆排序的时候,比如我这里的数组是

int arr[] = {15,23,73,56,12,94};

memcpy是一个复制函数,我们这里将arr的数组拷贝到结构体中,再进行一次向下调整,就可以得到排好的数组了。

heap.h

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<time.h>
#include <string.h>

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}Heap;

// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的销毁
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的删除
void HeapPop(Heap* hp);
// 取堆顶的数据
HPDataType HeapTop(Heap* hp);
// 堆的数据个数
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);

 heap.c

#define _CRT_SECURE_NO_WARNINGS 

#include"heap.h"
void HeapUp(HPDataType* a, int child);
void HeapDown(HPDataType* a, int n, int parent);
void Swap(HPDataType* a, HPDataType* b)
{
	
	HPDataType* tmp = *a;
	*a = *b;
	*b = tmp;
}
void HeapInit(Heap* hp)
{
	assert(hp);
	hp->a = NULL;
	hp->capacity = hp->size = 0;
}
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)//长度6
{
	assert(hp);
	hp->a = (HPDataType*)malloc(sizeof(HPDataType)*n);

	memcpy(hp->a, a, sizeof(HPDataType) * n);
	hp->capacity = hp->size = n;

	for (int i = (n-1-1)/2; i>=0; --i)//2
	{
		HeapDown(hp->a, n, i);//6,2
	}
}

void HeapUp( HPDataType* a,int child)
{	
	int parent = (child - 1) / 2;
	while (child > 0)//=0
	{
		if (a[parent] < a[child])    //大堆
		{   //a[parent] > a[child])  //小堆
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
			//HeapDown(); //向下排序
			//HeapUp();  //向上排序
		}
		else
		{
			break;
		}
	}
}

// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);

	if (hp->size == hp->capacity)//扩容
	{
		int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;
		hp->capacity = newcapacity;
		HPDataType* tmp = (HPDataType*)realloc(hp->a, newcapacity * sizeof(HPDataType));
		hp->a = tmp;
	}
	hp->a[hp->size] = x;  //0
	hp->size++;   //+1
	/*printf("%d ", hp->a[hp->size-1]);*/

	HeapUp(hp->a, hp->size - 1);//向上调整
}
void Print(Heap* hp)
{
	assert(hp);
	for (int i = 0; i < hp->size ; ++i)
	{
		printf("%d ", hp->a[i]);
	}
	printf("\n");
}
//void HeapDown(HPDataType* a, int n, int parent)//大堆,越来越xiao
//{
//	int child = parent * 2 + 1;
//	while (child < n)
//	{
//		if (a[child] < a[child + 1] && child + 1 < n)
//		{
//			child++;
//		}
//		if (a[parent] < a[child])
//		{
//			Swap( &a[child], &a[parent]);
//			parent = child;//
//			child = parent *2 +1;//
//			//HeapDown(); //向下排序
//			//HeapUp();  //向上排序
//		}
//		else
//		{
//			break;
//		}
//	}
//}

void HeapDown(HPDataType* a, int n, int parent)//小堆,越来越da
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (a[child] > a[child + 1] && child + 1 < n)
		{
			child++;
		}
		if (a[parent] > a[child])
		{
			Swap(&a[child], &a[parent]);
			parent = child;//
			child = parent * 2 + 1;//
			/*HeapDown();*/ //向下排序
			/*HeapUp(); */ //向上排序
		}
		else
		{
			break;
		}
	}
}
// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp->size > 0);
	//第一个跟最后一个换
	//重新排序
	Swap(&hp->a[0], &hp->a[hp->size-1]);
	hp->size--;

	HeapDown(hp->a, hp->size , 0);

}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	return hp->a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	
	return hp->size;
}
// 堆的判空
int HeapEmpty(Heap* hp)
{
	assert(hp);

	return hp->size==0 ;
}
// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	


	free(hp->a);
	hp->a = NULL;

	hp->capacity = hp->size = 0;
	/*free(hp->a);*/
}
void heapsort(int* a, int size)
{
	for (int i = (size - 1 - 1) / 2; i >= 0; --i)
	{
		HeapDown(a, size, i);
		//这一步其实已经实现了降序排序
	}
	int count = size - 1;
	while (count > 0)
	{
		//加上这一步就是升序
		Swap(&a[0], &a[count]);
		HeapDown(a, count, 0);
		count--;
	}	
}

void topk(int* a, int size)
{
	//小堆是第一位最小,大堆是第一位最大
	int k = 5;
	int minheap[5];
	for (int i = 0; i < k/*sizeof(minheap) / sizeof(int)*/; i++)
	{
		minheap[i] = a[i];//先复制前k个,假设k为5
	}
	for (int i = ( k - 1 - 1) / 2; i >= 0; --i)
	{
		HeapDown(minheap, k, i);
		
		//这一步其实已经实现了降序排序
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", minheap[i]);
	}
	printf("\n");
	for (int j = k; j < size; j++)
	{
		if (a[j] > minheap[0])
		{
			minheap[0] = a[j];
			HeapDown(minheap, k, 0);
		}
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ", minheap[i]);
	}
	printf("\n");
}

test.c

#define _CRT_SECURE_NO_WARNINGS 

#include"heap.h"


void test1()
{
	Heap hp;
	HeapInit(&hp);
	
	HeapPush(&hp, 99);
	HeapPush(&hp, 4);
	HeapPush(&hp, 5);
	HeapPush(&hp, 6);
	HeapPush(&hp, 12);
	HeapPush(&hp, 9);
	HeapPush(&hp, 13);
	HeapPush(&hp, 8);
	Print(&hp);
	HeapPop(&hp);
	Print(&hp);
	HeapPop(&hp);
	Print(&hp);
	HeapDestory(&hp);
}
void test2()
{
	Heap op;
	int arr[] = {15,23,73,56,12,94};
	HeapCreate(&op,arr, 6);
	Print(&op);
	HeapDestory(&op);
}
void test3()
{
	int arr[] = { 15,23,73,56,12,94,500,123,321,741,156 };
	heapsort(&arr, sizeof(arr) / sizeof(int));

	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
void test4()
{
	int arr[] = { 15,23,73,56,94,500,123,321,741,156,2001,312,99,880,12,6 };
	topk(&arr, sizeof(arr) / sizeof(arr[0]));

	//int k = 5;
	//int minheap[5];
	//for (int i = 0; i < k/*sizeof(minheap) / sizeof(int)*/; i++)
	//{
	//	minheap[i] = arr[i];//先复制前k个,假设k为5
	//}
	//for (int i = (k - 1 - 1) / 2; i >= 0; --i)
	//{
	//	HeapDown(minheap, k, i);
	//	//这一步其实已经实现了降序排序
	//}
	//for (int j = k; j < sizeof(arr) / sizeof(arr[0]); j++)
	//{
	//	if (arr[j] > minheap[0])
	//	{
	//		minheap[0] = arr[j];
	//		HeapDown(minheap, k, 0);
	//	}
	//}
	//for (int i = 0; i < k; i++)
	//{
	//	printf("%d ", minheap[i]);
	//}
	//printf("\n");
	/*for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");*/
}
int main()
{
	
	/*test1();*/
	/*test2();*/
	/*test3();*/
	test4();

	return 0;
}
4,堆排实现及topk问题

堆排序这里要说明的一点是,我们的数据不多的时候,我们可以将数组中的数据拷贝到新数组里面去排序,如果我们是数据很大呢,这里所占据的时间就会相当大,当然现在的电脑运行效率都很高,可能感觉不是很明显,如果是100万亿的数据呢,40G以上的数据呢,我们就要用堆排序,因为堆的时间复杂度是n*logn,相对于逐个对比的n^2的效率自然要高得多得多

我们这里是用的数组排序,所以最后一位的下标是size-1,再-1 ,/2访问到右子树的节点进行向下调整,就得到右子树排序后的数据。

我们如果是要变成小堆的话,其实只需要将我们的向下调整改成小堆的符号就可以了,当然像我这样再调整一边也能得到想要的数据。

这里每次都会再调整一次,最后一次只有一个数据也会调整,经过交换再调整后,就会变成小堆的思想,这里看着像两次大堆,实际上每一次count都会减少一位,直到count=0才停止向下调整。

topk问题

这里要改变最小的几个值,只需要改变a[j]<minheap[0],同时把向下调整改成小堆就能能解决这个事情。

 

 如果是磁盘上的数据文件访问找出topk个数的话,原理也是一样的,只是涉及到几个读取写入数据的函数而已。具体实现这里就不细说了,感兴趣的大佬可以后台私信也可以自己试着实现一下。


四、二叉树的链式结构(递归访问)

前面的堆排序我们是用数组的存储方式来进行存储的,那么链式结构在链表当中如何实现,其优点又在哪里呢?

1、遍历方式

在二叉树中,有很多种遍历的形式,一层层遍历,先父亲后孩子遍历......我下面会讲到四种遍历方式,在后期的实际问题中大家可以自己选择喜欢的方式去解决存在的问题。

前序遍历:首先访问根节点,然后递归地按照前序遍历的方式访问左子树,最后递归地按照前序遍历的方式访问右子树。顺序为根节点 - 左子树 - 右子树,可以理解成:根左右。

中序遍历:首先递归地按照中序遍历的方式访问左子树,然后访问根节点,最后递归地按照中序遍历的方式访问右子树。顺序为左子树 - 根节点 - 右子树,可以理解成:左根右。

后序遍历:首先递归地按照后序遍历的方式访问左子树,然后递归地按照后序遍历的方式访问右子树,最后访问根节点。顺序为左子树 - 右子树 - 根节点,可以理解成:左右根。

层序遍历:层序遍历是一种二叉树的遍历方式,也叫广度优先遍历。它按照树的层次顺序,从上到下逐层地访问每个节点。在同一层中,按照从左到右的顺序访问每个节点。可以理解成:从上到下从左到右。

前序遍历

我们先随便取一个数组array,数组里面的值就用"ABD##E#H##CF##G##"。

# 表示NULL指针。

然后我们用先序遍历构建二叉树

这是先序遍历的图活动过程,从A开始,然后一直不断获取下一个节点,知道遇到null,表示左子树这一个节点已经走到尽头,然后再走右子树这个方向。d遇到#表示左子树走完了,然后就再找d的右子树,此时右子树也是#。那就再往上找b的右子树,b的右子树是e。e去到#,在访问e的右节点,h,h的左右子树都是#。这个时候我们就再往上访问A的右子树c,c的左子树是f,f的左右子树是#,再往上访问c的右子树g,g的左右子树都是#。此时前序遍历就访问完毕,服从根->左子树->右子树这一原则,就可以构建出正确的前序遍历的链式访问结构。

后面几个遍历就不再一一说明了。

 中序遍历

左子树->根->右子树

中序遍历可以看成,二叉树每个节点,从左到右从下到上一个个排序(也可以理解为每个节点从最左边开始一个个掉到地上排成一排),然后从左往右数,得出的结果便是中序遍历的结果


我是看这个大佬的文章瞬间明白的,http://t.csdnimg.cn/5lw1g,感兴趣的小伙伴可以看看他的动图讲解

后序遍历

左子树->右子树->根

 大佬的思路是把二叉树想象成一串葡萄,就是围着树的外围绕一圈,如果发现一剪刀就能剪下的葡萄(必须是一颗葡萄)(也就是葡萄要一个一个掉下来,不能一口气掉超过1个这样),就把它剪下来,组成的就是后序遍历了。

层序遍历

按照树的层次顺序,从上到下逐层地访问每个节点。在同一层中,按照从左到右的顺序访问每个节点。可以理解成:从上到下从左到右。第一层完了就第二层,一层一层去遍历。

这里空默认不打印出来,直接返回。

当rear为0 时存放root的值,此时queue的0下标就是root,然后挨个的去赋值去打印出来就可以了。

二叉树销毁

遇到null返回,我这里用的是先序销毁,遇到值释放,置空,遇到null返回,层层递归。

 

 

二叉树节点个数 

这里的话思想和销毁的思想是一样的,就是不断地递归然后+1,最后得到的就是节点个数。

这里我的版图有点小,不太好画出来展示。

二叉树叶子节点个数 

 访问到根的左右子树都是null,那么这个节点就是叶子节点。如果一个为空,另一个不为空,就不是叶子节点。

 

二叉树第k层节点个数

此时k为3 ,

1+1+1+1=4.

根节点时,k=3,到第二层时,k=2,到第三层时,k=1.

这个时候我们只需要将所有k=1加起来,就是当前要 求的第三层的节点个数。

二叉树查找值为x的节点

当我们查找时,我们可以根据前面的经验,遇到null我们就返回,如果我们找到这个值,我们就返回找到了或者返回这个值的地址。在这一层没有找到的话,我们就递归到下一层去找,所有节点都没有的话,我们就返回null。

按照我们的思路,我们写出了这个代码,那它符不符合我们的要求,我们通过调试来看一下,假设我们要查找的值是f。

奇怪,我们的思路是没有问题的,这一点可以确定,那么我们来看一下究竟是哪里出了问题。

经过我们的调式,我们看到,这里已经找到了f的值,但是我们在返回的时候,发现没有东西来存储f的值,导致我们辛辛苦苦找到的值,最后一直遍历,直到遍历结束,到一开始执行的函数时,最后返回一个null。

解决这个问题的话,我们就添加一个结构体变量,让它存储我们找到的值,这样就可以解决这个问题了。

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

叶子节点只会出现在最后2层,且最后1层的叶子节点都靠左对齐。

 注意:满二叉树一定是完全二叉树,完全二叉树不一定是满二叉树 

我们可以借助队列来存放二叉树的节点,如果前面的left节点是null,此时访问right节点还有值且不为空的话,那么这个二叉树基本上就不是完全二叉树。

因为我们的完全二叉树要满足最后1层的叶子节点都靠左对齐,左边为空右边还有值时,那么就没有靠左对齐,就不是完全二叉树了。


Treelist.h
#define _CRT_SECURE_NO_WARNINGS 

#include<stdio.h>
#include<string.h>
#include <stdlib.h>
#include <stdbool.h>
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);
Treelist.c
#define _CRT_SECURE_NO_WARNINGS 
#include"Treelist.h"



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

    BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
    newNode->_data = a[*pi];
    (*pi)++;

    newNode->_left = BinaryTreeCreate(a, n, pi);
    newNode->_right = BinaryTreeCreate(a, n, pi);

    return newNode;
}

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
    if (*root == NULL)
        return;

    BinaryTreeDestory(&(*root)->_left);
    BinaryTreeDestory(&(*root)->_right);

    free(*root);
    *root = NULL;
}

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
    if (root == NULL)
        return 0;

    return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

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

    return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
    if (root == NULL || 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)
{
    if (root == NULL)
        return NULL;
    if (root->_data == x)
        return root;
    
    BTNode* ret = BinaryTreeFind(root->_left, x);
    if (ret != NULL)
        return ret;

    ret = BinaryTreeFind(root->_right, x);
    if (ret != NULL)
        return ret;

    return NULL;
}




// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
    if (root == NULL)
        return;

    printf("%c ", root->_data);
    BinaryTreePrevOrder(root->_left);
    BinaryTreePrevOrder(root->_right);
}

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
    if (root == NULL)
        return;

    BinaryTreeInOrder(root->_left);
    printf("%c ", root->_data);
    BinaryTreeInOrder(root->_right);
}

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
    if (root == NULL)
        return;

    BinaryTreePostOrder(root->_left);
    BinaryTreePostOrder(root->_right);
    printf("%c ", root->_data);
}

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
    if (root == NULL)
        return;

    BTNode* queue[100]; // 使用队列来辅助层序遍历
    int front = 0;
    int rear = 0;
    queue[rear++] = root;

    while (front != rear) {
        BTNode* node = queue[front++];
        printf("%c ", node->_data);

        if (node->_left != NULL)
            queue[rear++] = node->_left;
        if (node->_right != NULL)
            queue[rear++] = node->_right;
    }
}

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
    if (root == NULL)
        return true;
    BTNode* queue[100]; // 使用队列来辅助判断完全二叉树
    int front = 0;
    int rear = 0;
    queue[rear++] = root;
    bool flag = false; // 标记是否出现过NULL节点
    while (front != rear) {
        BTNode* node = queue[front++];
        if (node == NULL)
            flag = true;
        else {
            if (flag) // 如果之前出现过NULL节点,当前节点不为空,则不是完全二叉树
                return false;
            queue[rear++] = node->_left;
            queue[rear++] = node->_right;
        }
    }
    return true;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 

#include"Treelist.h"




int main()
{
    // 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
    BTDataType a[] = { 'A', 'B', 'D', '#', '#', 'E', '#', 'H', '#', '#', 'C', 'F', '#', '#', 'G', '#', '#', '\0' };
    int pi = 0;
    BTNode* root = BinaryTreeCreate(a, sizeof(a) / sizeof(a[0]), &pi);

    printf("Binary Tree Preorder: ");
    BinaryTreePrevOrder(root);
    printf("\n");

    printf("Binary Tree Inorder: ");
    BinaryTreeInOrder(root);
    printf("\n");

    printf("Binary Tree Postorder: ");
    BinaryTreePostOrder(root);
    printf("\n");

    printf("Binary Tree Levelorder: ");
    BinaryTreeLevelOrder(root);
    printf("\n");

    printf("The size of Binary Tree: %d\n", BinaryTreeSize(root));
    printf("The number of leaf nodes in Binary Tree: %d\n", BinaryTreeLeafSize(root));

    int k = 3;
    printf("The number of nodes in level %d of Binary Tree: %d\n", k, BinaryTreeLevelKSize(root, k));

    BTDataType x = 'F';
    BTNode* findNode = BinaryTreeFind(root, x);
    if (findNode != NULL)
        printf("Found node with value %c\n", x);
    else
        printf("Node with value %c not found\n", x);

    printf("Is the Binary Tree complete? %s\n", BinaryTreeComplete(root) ? "Yes" : "No");

    BinaryTreeDestory(&root);

    return 0;
}

写的不好的地方还请见谅,学习为主,笔记为辅。诸位少爷,再会!

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值