二叉树相关知识

二叉树相关知识

前言 树的相关知识

1.树的结构

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

image-20220814223534002

2.树的相关概念 百度百科 树的概念

image-20220814223705324

3.树的常用表示

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

typedef int DataType;
struct Node
{
 struct Node* _firstChild1; // 第一个孩子结点
 struct Node* _pNextBrother; // 指向其下一个兄弟结点
 DataType _data; // 结点中的数据域
};

image-20220814224328355

1.二叉树性质概念

1.二叉树的概念与结构

  1. 二叉树不存在度大于2的结点

  2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

    特殊的二叉树
  3. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是 说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。

    1. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对 应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

image-20220814224646750

注意:对于任意的二叉树都是由以下几种情况复合而成的:

image-20220814224800875

2.二叉树的性质

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

3.二叉树的存储结构

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

  1. 顺序存储

    顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空 间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺 序存储在物理上是一个数组,在逻辑上是一颗二叉树。

  2. 链式存储

    二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是 链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所 在的链结点的存储地址 。

接下来开始堆(二叉树的顺序结构)和二叉树(二叉树的链式结构)的实现

2.堆的实现

1.堆的相关知识

堆是由数组实现,物理层面我们在对数组操作,逻辑上在对一棵二叉树操作.

左右孩子与父节点计算公式

leftchild=parent*2+1;左孩子

rightchild=parent*2+2;右孩子

parent=(child-1)/2;父节点

2.堆的函数接口实现

//函数接口实现
#define _CRT_SECURE_NO_WARNINGS 1
#include"heap.h"
//交换函数
void swap(int *a, int *b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
//检查内存是否满
void checkheap(Heap* hp)
{
	if (hp->_capacity == hp->_size)
	{
		int newcapacity = hp->_capacity == 0 ? 4 : hp->_capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity);
		if (tmp == NULL)
		{
			exit(-1);
		}
		hp->_capacity = newcapacity;
		hp->_a = tmp;
	}
}
// 堆的构建
void HeapCreate(Heap* hp, HPDataType* a, int n)
{
	assert(hp);
	hp->_a = NULL;
	hp->_capacity = hp->_size = 0;
	for (int i = 0; i < n; i++)
	{
		HeapPush(hp, a[i]);
	}
}
// 堆的销毁
void HeapDestory(Heap* hp)
{
	assert(hp);
	free(hp->_a);
	hp->_a = NULL;
	hp->_capacity = hp->_size = 0;
}
// 堆的插入
void HeapPush(Heap* hp, HPDataType x)
{
	assert(hp);
	checkheap(hp);
	hp->_a[hp->_size++] = x;
	//HeapUp(hp, hp->_size - 1);
	
	heapdown(hp, hp->_size,(hp->_size - 2)/2);
}
//小堆向上调整
void HeapUp(Heap* hp, int child)
{
	assert(hp);
	int parent = (child - 1) / 2;
	while (hp->_a[parent] > hp->_a[child])
	{
		swap(&hp->_a[parent], &hp->_a[child]);
		child = parent;
		parent = (child - 1) / 2;
	}
}
//小堆向下调整(有bug版)
//void heapdown(Heap *hp,int n)
//{
//	int parent = 0;
//	int child = 0;
//	parent = child;
//	child = parent * 2 + 1;
//	if (hp->_a[child + 1] < hp->_a[child]&&child + 1 < n)
//	{
//		child++;
//	}
//	while (child < n)
//	{
//		if (hp->_a[parent] > hp->_a[child])
//		{
//			swap(&hp->_a[parent], &hp->_a[child]);
//		}
//		parent = child;
//		child = parent * 2 + 1;
//		if (hp->_a[child + 1] < hp->_a[child]&&child+1<n)
//		{
//			child++;
//		}
//	}
//}
//小堆向下调整
void heapdown(Heap* hp, int n,int root)
{
	while (root >= 0)
	{
		int parent = root;
		int child = 0;
		child = parent * 2 + 1;
		if (child + 1 < n &&hp->_a[child + 1] < hp->_a[child] )
		{
			child++;
		}
		while (child < n)
		{
			if (hp->_a[parent] > hp->_a[child])
			{
				swap(&hp->_a[parent], &hp->_a[child]);
			}
			parent = child;
			child = parent * 2 + 1;
			if (hp->_a[child + 1] < hp->_a[child] && child + 1 < n)
			{
				child++;
			}
		}
		root--;
	}
	
}
// 堆的删除
void HeapPop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	hp->_a[0] = hp->_a[hp->_size - 1];
	hp->_size--;
	heapdown(hp, hp->_size,0);
}
// 取堆顶的数据
HPDataType HeapTop(Heap* hp)
{
	assert(hp);
	assert(!HeapEmpty(hp));
	return hp->_a[0];
}
// 堆的数据个数
int HeapSize(Heap* hp)
{
	assert(hp);
	return hp->_size;
}
// 堆的判空
int HeapEmpty(Heap* hp)
{
	assert(hp);
	if (hp->_size == 0)
	{
		return 1;
	}
	return 0;
}

//堆排序 降序
void heapsort(int* a, int n)
{
	Heap hp;
	hp._a = a;
	hp._capacity = hp._size = n;
	for (int i = 0; i < n; i++)
	{
		heapdown(&hp, i+1,(i-1)/2);//小堆
	}
	int i = 1;
	while (i<n)
	{
		swap(&hp._a[0], &hp._a[n - i]);
		heapdown(&hp, n-i,0);
		i++;
	}

}

// TopK问题:找出N个数里面最大/最小的前K个问题。
// 比如:未央区排名前10的泡馍,西安交通大学王者荣耀排名前10的韩信,全国排名前10的李白。等等问题都是Topk问题,
// 需要注意:
// 找最大的前K个,建立K个数的小堆
// 找最小的前K个,建立K个数的大堆
void PrintTopK(int* a, int n, int k)
{
	Heap hp;
	hp._a = NULL;
	hp._capacity = hp._size = 0;
	for (int i = 0; i < k; i++)
	{
		HeapPush(&hp,a[i]);
	}
	for (int i = k; i < n; i++)
	{
		if (a[i] > HeapTop(&hp))
		{
			hp._a[0] = a[i];
			heapdown(&hp, hp._size,0);
		}
	}
	for (int i = 0; i < k; i++)
	{
		printf("%d ",hp._a[i]);
	}
}
void TestTopk()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10,34,124,235,445,33,25,5,23,4,2,2,2,7,35,67,45,56,222,367,990};
	PrintTopK(arr,sizeof(arr)/sizeof(arr[0]),6);
}

3.堆的函数部分讲解

1.堆的向下调整

自顶向下:主要用于堆删除节点,删除结点之后把最后一个节点替换到,当前根节点位置,对此节点进行自顶向下操作

image-20220815093520213

2.堆的向上调整

自底向上:主要用于节点的插入,当一个节点插入到这个堆之后,向上调整堆,堆还能符合大顶堆或者小顶堆的定义。

image-20220815093809860

3.堆的删除

删除堆的节点,删除堆节点的操作,只能够删除根节点,把根节点和最后一个节点交换位置之后,然后弹出最后一个节点,并且对当前堆进行自顶向下的操作

image-20220815093959144

3.二叉树实现

1.二叉树的函数接口实现

#define _CRT_SECURE_NO_WARNINGS 1
#include"tree.h"
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
	BTNode* newBTnode = (BTNode*)malloc(sizeof(BTNode));
	if (newBTnode == NULL)
	{
		exit(-1);
	}
	newBTnode->_data = a[*pi];
	(*pi)++;
	newBTnode->_left = BinaryTreeCreate(a, n, pi);
	newBTnode->_right = BinaryTreeCreate(a, n, pi);
	return newBTnode;
}
// 二叉树销毁
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;
	}
	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;
	}
	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
	{
		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* tmp=BinaryTreeFind(root->_left, x);
	if (tmp)
	{
		return tmp;
	}
	tmp=BinaryTreeFind(root->_right, x);
	if (tmp)
	{
		return tmp;
	}
	return NULL;
}
// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->_data);
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BinaryTreeInOrder(root->_left);
	printf("%c ", root->_data);
	BinaryTreeInOrder(root->_right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	BinaryTreePostOrder(root->_left);
	BinaryTreePostOrder(root->_right);
	printf("%c ", root->_data);
}
//MAX函数
int MAX(int a,int b)
{
	return a > b ? a : b;
}
//二叉树的高度 
int treehight(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	
	int left = treehight(root->_left);
	int right = treehight(root->_right);
	return 1 + MAX(left, right);
	//return 1+(right>left?right:left);
}
//接下来的两个函数使用了之前队列的函数接口,需要源代码请到我的队列文章获取
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q,root);

	while (QueueEmpty(&q))
	{
		BTNode *tmp = QueueFront(&q);
		QueuePop(&q);
		printf("%c", tmp->_data);
		if (tmp->_left)
		{
			QueuePush(&q,tmp->_left);
		}
		if (tmp->_right)
		{
			QueuePush(&q, tmp->_right);
		}
	}
	printf("\n");
	QueueDestroy(&q);
	
}
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (QueueEmpty(&q))
	{
		BTNode* tmp = QueueFront(&q);
		QueuePop(&q);
		if (tmp == NULL)
		{
			break;
		}
		QueuePush(&q, tmp->_left);
		QueuePush(&q, tmp->_right);
	}
	while (QueueEmpty(&q))
	{
		BTNode* tmp = QueueFront(&q);
		QueuePop(&q);
		if (tmp != NULL)
		{
			QueueDestroy(&q);
			return 0;
		}
	}
	return 1;
	QueueDestroy(&q);
}

2.二叉树的函数部分讲解

四种遍历

  1. 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
  2. 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
  3. 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
  4. 层序遍历 设二叉树的根节点所在 层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层 上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

4.二叉树相关oj题

1.144. 二叉树的前序遍历

image-20220815094535534

解题思路:

直接前序遍历即可

解题代码:

void BinaryTreePrevOrder(struct TreeNode* root,int *a,int *arr)
{
	if (root == NULL)
	{
		return;
	}
    arr[*a]=root->val;
    (*a)++;
	BinaryTreePrevOrder(root->left,a,arr);
	BinaryTreePrevOrder(root->right,a,arr);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize){
int *arr=(int *)malloc(sizeof(int)*100);
*returnSize=0;
BinaryTreePrevOrder(root,returnSize,arr);
return arr;
}

2.965. 单值二叉树

image-20220815094835122

解题思路:

判断是否为单值二叉树,及判断每一个节点与左右节点的值是否即可,通过分析我们可以使用递归来实现判断,

从子节点开始判断,逐渐往上判断.

解题代码:

bool isUnivalTree(struct TreeNode* root){
if(root==NULL)
{
    return true;
}
//都为空
if(root->left==NULL&&root->right==NULL)
{
    return true;
}
//其中一个为空
if(root->left==NULL||root->right==NULL)
{
    if(root->left)
    {
    if((root->val!=root->left->val))
{
    return false;
}

    }
    else
    {
if((root->val!=root->right->val))
{
    return false;
}

    }
}
if((root->left!=NULL&&root->val!=root->left->val)||(root->right!=NULL&&root->val!=root->right->val))
{
    return false;
}
bool tmp=isUnivalTree(root->left);
if(!tmp)
{
    return false;
}
tmp =isUnivalTree(root->right);
return tmp;
}

3.104. 二叉树的最大深度

image-20220815095401969

解题思路:

直接求树的高度即可

解题代码:

int maxDepth(struct TreeNode* root){
if(root==NULL)
{
    return 0;
}
int l=maxDepth(root->left);
int r=maxDepth(root->right);
return l>r?l+1:r+1;
}

4.226. 翻转二叉树

image-20220815095527761

解题思路:

按照题意直接翻转即可

解题代码:

struct TreeNode* invertTree(struct TreeNode* root){
if(root==NULL)
{
    return NULL;
}
struct TreeNode*tmp=root->left;
root->left= invertTree(root->right);
root->right=invertTree(tmp);
return root;
}

5.100. 相同的树

image-20220815095654985

解题思路:

直接判断各个节点是否相同

解题代码:

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
if(p==NULL&&q==NULL)
{
    return true;
}
if(p==NULL||q==NULL)
{
    return false;
}
if(p->val!=q->val)
{
    return false;
}
bool tmp=isSameTree(p->left,q->left);
if(tmp==false)
{
    return false;
}
tmp=isSameTree(p->right,q->right);
return tmp;
}

6.101. 对称二叉树

image-20220815095832354

解题思路:

跟上道题类似,直接将这棵树的左子树,右子树放入,判断是否为对称树.在判断时一个节点放左子树,另一个要放右子树.

解题代码:

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
if(p==NULL&&q==NULL)
{
    return true;
}
if(p==NULL||q==NULL)
{
    return false;
}
if(p->val!=q->val)
{
    return false;
}
bool tmp=isSameTree(p->left,q->right);
if(tmp==false)
{
    return false;
}
tmp=isSameTree(p->right,q->left);
return tmp;
}

bool isSymmetric(struct TreeNode* root){
if(root==NULL)
{
    return true;
}
return isSameTree(root->left,root->right);
}

7.572. 另一棵树的子树

image-20220815100203203

解题思路:

直接递归判断是否有相同子树.

解题代码:

bool isSameTree(struct TreeNode* p, struct TreeNode* q){
if(p==NULL&&q==NULL)
{
    return true;
}
if(p==NULL||q==NULL)
{
    return false;
}
if(p->val!=q->val)
{
    return false;
}
return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right);
}

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root==NULL)
{
    return false;
}
if(isSameTree(root,subRoot))
{
    return true;
}
return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}

8.110. 平衡二叉树

image-20220815100409153

解题思路:

暴力,求出每个节点左右子树高度,判断是否符合条件

解题代码:

int treehight(struct TreeNode* root)
{
    if(root==NULL)
    {
        return 0;
    }
    int l=treehight(root->left);
    int r=treehight(root->right);
    return l>r?l+1:r+1;
}

bool isBalanced(struct TreeNode* root){
if(root==NULL)
{
    return true;
}
return fabs(treehight(root->left) - treehight(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}

ULL)
{
return false;
}
if(isSameTree(root,subRoot))
{
return true;
}
return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}


### 8.[110. 平衡二叉树](https://leetcode.cn/problems/balanced-binary-tree/)

![image-20220815100409153](https://i-blog.csdnimg.cn/blog_migrate/897471a97799644caa39210f56e8ecac.png)

解题思路:

暴力,求出每个节点左右子树高度,判断是否符合条件

解题代码:

```c
int treehight(struct TreeNode* root)
{
    if(root==NULL)
    {
        return 0;
    }
    int l=treehight(root->left);
    int r=treehight(root->right);
    return l>r?l+1:r+1;
}

bool isBalanced(struct TreeNode* root){
if(root==NULL)
{
    return true;
}
return fabs(treehight(root->left) - treehight(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sleepymonstergod

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

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

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

打赏作者

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

抵扣说明:

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

余额充值