数据结构篇之二叉树(binary tree)的介绍和应用

news2024/11/16 3:24:49

欢迎光临:

                                   

男神

目录

一·树的介绍和表示:

二·二叉树的介绍及性质:

三·堆的介绍及创建:

1·堆的创建:

2·堆的应用:

四·二叉树的创建:

①// 通过前序遍历的数组"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):

⑤int BinaryTreeLevelKSize(BTNode* root, int k):

⑥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):

⑫int maxDepth(struct TreeNode* root):

五·二叉树的oj刷题:

1·对称二叉树: 

2·另一棵树的子树:

3·平衡二叉树:

4·翻转二叉树:


一·树的介绍和表示:

首先,要知道树是一种非线性结构,然后第一个是它的根节点,类似于树一样往下面展开。

大致长这个样子。

那这样子的树要怎表示呢?

我们用一种叫孩子兄弟表示法:

也就是我们在结构体里面创建的分别为孩子,兄弟,数据这几个项,然后把它们连接起来

    typedef int DataType;
    struct treenode
    {
         struct Node* Child; 
         struct Node* Brother; 
         DataType data; 
    };

当然,这种研究起来比较复杂,接下来介绍一下二叉树。 

 

二·二叉树的介绍及性质:

二叉树就是一个根引出两个支,然后延续下去,成为二叉树的左右子树,然后这就给了它一个限制,也就是每个节点只能引出两条边,也就是度最大为2。

然后二叉树又分为:

1·不完全二叉树:不完全的话就是完全的反面如上面的这棵树就是不完全二叉树。

2·完全二叉树:完全的话就是每一行从左到右应该是持续排列的,即不能左支无而右支有。如:

3·满二叉树: 这就是完全二叉树的一种类型,它是每个节点都连接了两个分支,也就是除了叶子层每个节点度都是2.如:

性质:简单说比较重要的就是任何一个二叉树n(度为2)+1=n(度为1) ;下面还有一些性质可做参考:

 

三·堆的介绍及创建:

堆其实也可以看做一颗完全二叉树,它是非线性结构。然后又可以分为大堆和小堆。

1·小堆:树中父亲节点的值小于或等于孩子节点值。

2·大堆:树中父亲节点的值大于会等于孩子节点的值。

因此我们创建堆可以分这两类。

那这个有什么用途呢?我们可以用它topk(在很多数据中找到前k个数据)以及排序问题。

1·堆的创建:

那么下面我们先来实现一下堆:

首先我们先创建这样的结构体,利用数组来实现。

typedef struct Heap {
	int capacity;
	int size;
	headpdata* a;
}Hp;

下面介绍一下,向上调整和向下调整。首先对于堆来说要知道孩子还父亲在数组里对应的关系。

int parent = (child - 1) / 2;
int child = parent * 2 + 1;

上调:就是从孩子节点往上调整,也就是如果我们要建立小堆的话,如果孩子比父亲小那么就让两者互换得到新的孩子一直循环下去,直到孩子的下标大于0即完成,大堆变动一下即可。

下调:还是拿小堆为例子,拿到父亲节点值与两个孩子比较,得到两个孩子中最小的交换,再次得到新的父亲节点,再求下一个孩子节点,直到孩子节点到达数组最大长度n终止。 

然后这里我们在每一次上调或者下调的时候要确保这个数组构成的堆是有序的。

然后我们每次删除堆顶元素,需要让它和尾部元素交换,然后得到一个除了堆顶元素外剩下都有序的堆然后下调,同理插入的时候,每次插入都进行上调。

下面展示一下堆的代码:

heap.h


#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int headpdata;
typedef struct Heap {
	int capacity;
	int size;
	headpdata* a;
}Hp;
void Hpinit(Hp* p);//初始化
void Hpdestroy(Hp* p);//销毁
void Hppush(Hp* p, headpdata x);//数据入堆
void Hpupadjust(headpdata* a, int child);//数据在堆中上调
void Hpdownadjust(headpdata* a, int parent, int n);//下调
bool Hpempty(Hp* p);//判空
headpdata  Hptop(Hp* p);//返回堆顶元素;
void Hppop(Hp* p);//删除堆顶元素


heap.c

#define _CRT_SECURE_NO_WARNINGS
#include"heap.h"
void swap(headpdata* a, headpdata* b) {
	headpdata tmp = *a;
	*a = *b;
	*b = tmp;
}
void Hpinit(Hp* p) {
	assert(p);
	p->capacity = p->size = 0;
	p->a = NULL;
}

void Hppush(Hp* p, headpdata x) {
	assert(p);

	if (p->capacity == p->size) {
		int newcapacity = p->capacity == 0 ? 4 : 2 * p->capacity;
		headpdata* m = (headpdata*)realloc(p->a, sizeof(headpdata) * newcapacity);
		if (m != NULL) {

			p->a = m;
			p->capacity = newcapacity;

		}
		else {
			perror(realloc);

		}
	}
	p->a[p->size] = x;
	p->size++;
	Hpupadjust(p->a, p->size - 1);
}
void Hpupadjust(headpdata* a, int child) {
	int parent = (child - 1) / 2;
	while (child > 0) {
		//小堆(<)
		//大堆(>)
		if (a[child] < a[parent]) {
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else {
			return;
		}
	}
}
void Hpdownadjust(headpdata* a, int parent, int n) {
	int child = parent * 2 + 1;


	while (child < n) {
		//假设孩子为左孩子
		if (child + 1 < n && a[child] > a[child + 1]) {
			child++;
		}
		//小堆:child>child+1
		//此时的child一定是最小的一个
		if (a[child] < a[parent]) /*小堆(<)*/
		{
			swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			return;
		}
	}
}
bool Hpempty(Hp* p) {
	assert(p);
	return p->size == 0;
}
headpdata  Hptop(Hp* p) {
	assert(p);
	return p->a[0];
}
void Hppop(Hp* p) {
	assert(p);
	swap(&p->a[0], &p->a[p->size - 1]);
	p->size--;
	Hpdownadjust(p->a, 0, p->size);
}
void Hpdestroy(Hp* p) {
	assert(p);
	free(p->a);
	p->a = NULL;
	p->capacity = p->size = 0;

}


test.c


#define _CRT_SECURE_NO_WARNINGS
#include"heap.h"
void test1() {
	Hp hp;
	Hpinit(&hp);
	Hppush(&hp, 2);
	Hppush(&hp, 11);
	Hppush(&hp, 9);
	Hppush(&hp, 8);
	Hppush(&hp, 5);
	Hppush(&hp, 2);
	Hppush(&hp, 4);
	for (int i = 0; i < 7; i++)
	{

		printf("%d ", Hptop(&hp));
		Hppop(&hp);

	}
	Hpdestroy(&hp);
}
int main() {
	test1();
	return 0;
}

这样就建好堆了,至于每次上下调传的是数组的指针而不是结构体指针方便下面我们可以直接调用数组进行堆排序。

2·堆的应用:

堆排序:以小堆为例介绍,其实就是我们创建好数组然后给它传递过去,上调的话就是从第一个开始遍历这个数组每次都调用一次上调,最后这个数组就会是一个小堆,接着每次让堆顶元素跟尾部元素交换然后尾指针--;然后对这个堆(由于除了堆顶元素其他都有序,故下调,无论建大堆还是小堆均这样)最后就得到了降序的数组。

然后由于时间复杂度,通常选用下调建堆。

堆排序也通常要记住:升序建大堆,降序建小堆。

代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//数组内部堆排序:
//数组升序则大堆排列,数组降序则小堆排列

//数据往上下调时候,原堆应该是有序的
void swap(int* a, int* b) {
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void Hpupadjust(int* a, int child) {
	int parent = (child - 1) / 2;
	while (child > 0) {
		//小堆(<)
		if (a[child] < a[parent]) {
			swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else {
			return;
		}

	}
}
void Hpdownadjust(int * a, int parent, int n) {
	int child = parent * 2 + 1;
	//小堆(<)
	while (child < n) {
		//假设孩子为左孩子
		if (child + 1 < n && a[child] > a[child + 1]) {
			child++;
		}
	//大堆child<child+1
		if (a[child] <a[parent])/*小堆(<) */{
			swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			return;
		}
	}
}
/升序大堆,降序小堆
void sort_upadjust(int* a, int n) {
	for (int i = 1; i < n; i++) {
		Hpupadjust(a, i);//上调
	}
	/*for (int i = 0; i < n; i++) {
		printf("%d ", a[i]);
	}
	printf("\n");*/
	int end = n - 1;
	while (end > 0) {
		swap(&a[0], &a[end]);
		Hpdownadjust(a, 0,end);
		end--;
		
		
		

	}
	for (int i = 0; i < n; i++) {
		printf("%d ", a[i]);
	}
}

void sort_downadjust(int* a, int n) {
	for (int i = (n-2)/2; i >=0; i--) {
		Hpdownadjust(a, 0, i);//下调
	}
	int end = n - 1;
	while (end > 0) {
		swap(&a[0], &a[end]);
		Hpdownadjust(a, 0, end);
		end--;
	}

		for (int i = 0; i < n; i++) {
			printf("%d ", a[i]);
		}
}
int main() {
	int a[] = { 4,6,28,99,34,66,1,1,2,377,4 };
	int r=sizeof(a)/sizeof(a[0]);
	sort_upadjust(a,r);//上调生成堆的排序
	sort_downadjust(a, r);//下调生成堆的排序


	return 0;
}

还有一个应用就是在大量数据中找出前k个:

这里我们在文件内生成十万个数据模拟一下,然后为了节省空间选择建立一个k个数的小堆(假设k=5);然后把它建成小堆,先从文件中读取5个数建好,然后接下来每次读取一个跟堆顶元素比较比它大就覆盖它重新下调,这样每次我们都能选出6个数中最大的5个并且淘汰一个已知不合要求的数字,然后循环下去得到最终的前五。

 

如果我们要得到有序的k个最大的数,可以再调用一下上面的堆排序:

代码如下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
void swap(int* a, int* b) {
	int tmp = *a;
	*a = *b;
	*b = tmp;
}
void Hpdownadjust(int* a, int parent, int n) {
	int child = parent * 2 + 1;
	//小堆(<)
	while (child < n) {
		//假设孩子为左孩子
		if (child + 1 < n && a[child] > a[child + 1]) {
			child++;
		}
		//大堆child<child+1
		if (a[child] < a[parent])/*小堆(<) */ {
			swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else {
			return;
		}
	}
}
void createdata() {
	FILE* file = fopen("data.txt", "w");
	if (file == NULL) {
		perror(fopen);
		return;
	}
	int j = 0;
	srand((unsigned int)time(NULL));
	for (int i = 0; i < 100000; i++) {
		j = (rand() + i) % 100000;//srand产生的随机数会有重复,给他+i可以减少重复数据
		fprintf(file, "%d\n", j);
	}
	fclose(file);
}
int main() {
	/*createdata();*///创作10w个数据
	//读入数组内
	printf("请输入要找的最大的前k个数>: ");
	int k = 0;
	scanf("%d", &k);
	int* a = (int*)malloc(sizeof(int) * k);
	FILE* fout = fopen("data.txt", "r");
	if (fout == NULL) {
		perror(fopen);
		return;
	}
	for (int i = 0; i < k; i++) {
		fscanf(fout, "%d", &a[i]);
	}
	
	/*for (int i = 0; i < k; i++) {
		printf("%d ", a[i]);
	}*/
	//向下调整建立小堆:
	for (int i = (k - 2) / 2; i >=0; i--) {
		Hpdownadjust(a, i, k);
	}
	//一个个把剩下的n-k个数据读出来与a[0]比大小,大则替换:
	// 可以确保小堆里面的数据一定比替换下去的数据大
	//这样就可以一次舍去不合要求数据
	int x = 0;
	while (fscanf(fout, "%d", &x)!=EOF) {//fscanf输出异常为EOF否则
		if (x > a[0]) {
			a[0] = x;
			Hpdownadjust(a, 0, k);
		}
		/*printf("%d ", fscanf(fout, "%d", &x));*///返回的为实际读取数据的个数:1
	}
	fclose(fout);
	//逆序输出最大前k个数:
	int end = k - 1;
	while (end) {
		swap(&a[0], &a[end]);
		end--;
		Hpdownadjust(a, 0, end+1);
	}
	for (int i = 0; i < k; i++) {
		printf("%d ", a[i]);
	}
	return 0;
}

以上就是堆的一些应用。 

 

四·二叉树的创建:

首先创建一个存放字符数据的树,先定义结构体:

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):

这里首先创建一个返回可放入数据的一个节点,然后每次判断只要是#就返回NULL,否则放入数据,然后再左子树右子树走下去,每当判断完或者放完数据就让指针++;然后函数不断调用自己,直到归到返回的最开始的根节点。(由于函数调用自己要想改变下标的值必须传地址用指针接收)

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi) {
	
		if (a[*pi] == '#') {
			

			return NULL;
		}

		BTNode* node = createnode(a[*pi]);
		

		(*pi)++;
		node->left = BinaryTreeCreate(a, n, pi);
			(*pi)++;

		node->right = BinaryTreeCreate(a, n,pi);
	

			return node;
	
}

 ②void BinaryTreeDestory(BTNode** root):

这里为了销毁的完整选择的是只要不为空左右子树遍历下去,然后当左右子树都为空时,把这个根释放掉。

void BinaryTreeDestory(BTNode** root) {
	if (*root != NULL) {
		BinaryTreeDestory(&((*root)->left));
		BinaryTreeDestory(&((*root)->right));
		free(*root);
		*root=NULL;
	}
}

 

③int BinaryTreeSize(BTNode* root):

就是节点的个数,可以从根(不为NULL)开始左右子树遍历,每遍历一次就加1,故就是1+遍历的左子树+遍历的右子树,为NULL就返回0。

int BinaryTreeSize(BTNode* root) {
	if (root == NULL) {
		return 0;
	}
	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

④int BinaryTreeLeafSize(BTNode* root):

首先要知道只有当它的左右子树都为NULL,那么就是叶子,故可以加一,否则在不为空的情况下就递归返回它的左子树加右子树,直到为空就返回0。

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);
}

 

⑤int BinaryTreeLevelKSize(BTNode* root, int k):

可以通过每次遍历左右子树就让k的值-1直到达到1,也就是递归的时候这一层就是一开始要找的第k层就返回1。

 

int BinaryTreeLevelKSize(BTNode* root, int k) {
	if (root == NULL) {
		return 0;
	}
	if (k == 1) {
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) 
		+ BinaryTreeLevelKSize(root->right, k - 1);
}

 

⑥BTNode* BinaryTreeFind(BTNode* root, BTDataType x):

法一:这里,可以考虑把它按照层序查找入栈,然后一个个去访问栈中的元素,一旦发现就返回这个指针,到最后也没发现就返回NULL;思路同

代码:

BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
	queue q;
	Queueinit(&q);
	Queuebackpush(&q, root);
	while (!QueueEmpty(&q)) {
		BTNode* ret = Queuefrontdata(&q);
		Queuefrontpop(&q);
		if (ret->data == x) {
			Queuedestroy(&q);
			return ret;
		}
		if (ret->left != NULL) {

			Queuebackpush(&q, ret->left);
		}
		if (ret->right != NULL) {

			Queuebackpush(&q, ret->right);
		}
	}
	
	Queuedestroy(&q);
	return NULL;


}

法二:在前提首根不为NULL情况下遍历它的左右子树,如果发现了x就返回这个根,否则接着

左右子树接着调用找,用指针记录它们的返回值,并保存,等到归回去的时候,如果不为空就把它返回最终得到x所在节点的指针。

BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
  
if(root==NULL){
return NULL;
 if(root->val==x){
return root;
    
       }
    BTNode*ret1=BinaryTreeFind(root->left);
if(ret1){
      return ret1;}
     BTNode*ret2=BinaryTreeFind(root->right);
if(ret2){
      return ret2;}
}

 

 

 

⑦void BinaryTreePrevOrder(BTNode* root):

也就是根左右方式去遍历,打印的话就是遇到根就把它打印,然后再分别去遍历左右两支。

void BinaryTreePrevOrder(BTNode* root) {
	if (root == NULL) {
		printf("# ");
		return;
	}
	printf("%c ", root->data);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);


}

 

⑧void BinaryTreeInOrder(BTNode* root):

也就是左根右方式去遍历,一直遍历左树后打印然后再调用右树。

与前序相类似:

void BinaryTreeInOrder(BTNode* root) {
	if (root == NULL) {
		printf("# ");
         return;
 
	}
	BinaryTreeInOrder(root->left);
	printf("%c ", root->data);
	BinaryTreeInOrder(root->right);
}

⑨void BinaryTreePostOrder(BTNode* root):

也就是左右根方式去遍历,一直遍历右树后打印然后再调用左树,与中序打印换一下位置。

void BinaryTreePostOrder(BTNode* root) {
	if (root == NULL) {
		printf("# ");
         return;
 
	}
	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->data);
}

⑩void BinaryTreeLevelOrder(BTNode* root):

首先建立一个队列然后入队列的数据类型是这个树节点的类型,然后先把树的头根入进去,之后把出队列,然后保存并打印,只要它的左右子树不为空就入进去,循环下去,为空就退,最后就能都打印出。

void BinaryTreeLevelOrder(BTNode* root) {
	queue q;
	Queueinit(&q);
	Queuebackpush(&q, root);
	while (!QueueEmpty(&q)) {
		BTNode*ret=Queuefrontdata(&q);
		Queuefrontpop(&q);
		printf("%c ", ret->data);
		if (ret->left!= NULL) {
			
			Queuebackpush(&q, ret->left);
		}
		if (ret->right!= NULL) {
			
			Queuebackpush(&q, ret->right);
		}
	}
	Queuedestroy(&q);

}

 

这里会不会一个疑问,就是队列为空时候可不可能还有树的节点指针没有进去,然而要知道只要父亲被移除了队列后那么他的孩子一定能进去。

⑪int BinaryTreeComplete(BTNode* root):

还是把它放入队列,然后有进有出,当我们把空节点的指针放进去,那么只要我们在出队列时候读取数据,只要读到了空,就停止,时刻记录队列内数据的个数,然后依次去出队列剩下的数据,如果读到了不是空的指针就证明不是完全二叉树直接返回,如果读到size为0还为读到有数据,即完全二叉树。

int BinaryTreeComplete(BTNode* root) {
	int is = 1;
	int size = 0;
	queue q;
	Queueinit(&q);
	Queuebackpush(&q, root);
	size = 1;
	BTNode* ret = NULL;
	while (!QueueEmpty(&q)) {
		ret = Queuefrontdata(&q);
		Queuefrontpop(&q);
		size--;
		if (ret != NULL) {
			Queuebackpush(&q, ret->left);
			Queuebackpush(&q, ret->right);
			size += 2;
		}
		else {
			break;
		}
		
	}
	while (size--) {
		ret = Queuefrontdata(&q);
		Queuefrontpop(&q);
		if (ret != NULL) {
			 is = 0;
			break;
		}

	}
	Queuedestroy(&q);

	return is;
}

 

⑫int maxDepth(struct TreeNode* root):

即树的高度,就是1+左右子树最长的树,然后一次递归下去(在前提首根不为NULL情况下)。

注:这里由于多次递归复杂问题,可以把之前递归得到的值保存,在进行比较。

int maxDepth(struct TreeNode* root) {
    if (root == NULL) {
        return  0;
    }
    int lefthigh = maxDepth(root->left);
    int righthigh = maxDepth(root->right);//这里把计算的子树的深度保存防止下一次再次递归
    return lefthigh > righthigh ? (lefthigh + 1) : (righthigh + 1);

}

 

五·二叉树的oj刷题:

1·对称二叉树: 

链接:. - 力扣(LeetCode)

思路:如果数都为null那么肯定不对称,不是null那么就可以类比判断两颗树是否相同,即每次比较左右子树,但是是左子树左支和右子树右支比,左子树右支和右子树左支比,然后递归下去,只有每次判断都是true才成立,用&&

bool issimilar(struct TreeNode* p, struct TreeNode* q){
   if(p==NULL&&q==NULL){
        return true;//判断位置合适也是终止条件(true)
    }
    if(p==NULL||q==NULL){
        return false;//上面已经判断了q p不可能同时为空,那么其中至少一个为空就false
    }
    if(q->val!=p->val){
        return false;//判断数值是否合适
    }
    return  issimilar( p->left,q->right)&&issimilar( p->right,q->left);

 }
bool isSymmetric(struct TreeNode* root) {
    if(root==NULL){
        return false;
    }//判断一开始就不为空

  return issimilar( root->left,root->right);
}

 

 

2·另一棵树的子树:

链接:. - 力扣(LeetCode) 

思路:首先root如果为NULL肯定不成立,如果root不为空,就遍历root这棵树,然后每次可以得到他的一个节点,就调用比较两棵树是否相同的函数,每到root的一个节点就用一下 isSameTree,然后每次调用遍历函数 只要发现了相同就是出现了true即成立,故这里用||

 

 bool isSameTree(struct TreeNode* p, struct TreeNode* q) {
    if(q==NULL&&p==NULL){
        return true;//判断位置合适也是终止条件(true)
    }
    // if(p==NULL&&q!=NULL){
    //     return false;
    // }
    // if(p!=NULL&&q==NULL){
    //     return false;
    // }
    if(p==NULL||q==NULL){
        return false;//上面已经判断了q p不可能同时为空,那么其中至少一个为空就false
    }
    if(q->val!=p->val){
        return false;//判断数值是否合适
    }

    return  isSameTree(p->left,q->left)&& isSameTree(p->right,q->right);//由于为bool类型,只有遍历的两棵树
    // 左右都返回的true才能证明完全相等,故yong&&
}
 bool prefind_istree(struct TreeNode*p,struct TreeNode*q){
   if(p==NULL){
    return false;
   }
      
      if( isSameTree(p,q)==true){
        return true;
      }//证明找到子树
   return  prefind_istree(p->left,q)||prefind_istree(p->right,q);//找到了就可以返回存在一个就可

 }

bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
    if(root==NULL){
        return false;
    }
    return prefind_istree(root,subRoot);
}

 

3·平衡二叉树:

链接:. - 力扣(LeetCode) 

思路:先建立一个比较树高度的函数,然后让整颗树以及它的左右子树互相比较left与right如果发现小于等于一就返回false否则继续下去直到为空

 

int maxDepth(struct TreeNode* root) {
    if (root == NULL) {
        return  0;
    }
    int lefthigh = maxDepth(root->left);
    int righthigh = maxDepth(root->right);//这里把计算的子树的深度保存防止下一次再次递归
    return lefthigh > righthigh ? (lefthigh + 1) : (righthigh + 1);

}

bool isBalanced(struct TreeNode* root) {
    if(root==NULL){
        return true;
    }//只能判断首个根是否为NULL
    
    if(abs(maxDepth(root->left)-maxDepth(root->right))>1){
        return false;//这里判断>1好写一些
    }
    return isBalanced(root->left)&&isBalanced(root->right);
   
   
}

 

 

4·翻转二叉树:

链接:. - 力扣(LeetCode) 

 思路:依次交换树的左右两支,不断递归直到得到的为叶子,然后用本次递归调用函数的root->左右交换,最后返回的root就是第一次调用函数的root。

 

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

    return root;

}

 

 ☺剧终☺

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1698211.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt 对话框或者QMainWindow等类中调用自定义QWidget继承组件

简单的方法如下所示 1、创建一个ui文件&#xff0c;界面布局放入QVBoxLayout或者QHBoxLayout 使用他来放入自定义组件&#xff0c;类似如下 2、代码如下&#xff1a; ui.setupUi(this); { //自定义组价如下 KwTable *Table new KwTable(this); ui.vertical…

Firefox国际版

Firefox国际版官方网址&#xff1a; Download the Firefox Browser in English (US) and more than 90 other languagesEveryone deserves access to the internet — your language should never be a barrier. That’s why — with the help of dedicated volunteers around…

从用法到源码再到应用场景:全方位了解CompletableFuture及其线程池

文章目录 文章导图什么是CompletableFutureCompletableFuture用法总结API总结 为什么使用CompletableFuture场景总结 CompletableFuture默认线程池解析&#xff1a;ForkJoinPool or ThreadPerTaskExecutor&#xff1f;ForkJoinPool 线程池ThreadPerTaskExecutor线程池Completab…

【网络协议】应用层协议--HTTP

文章目录 一、HTTP是什么&#xff1f;二、HTTP协议工作过程三、HTTP协议1. fiddler2. Fiddler抓包的原理3. 代理服务器是什么?4. HTTP协议格式1.1 请求1.2 响应 四、认识HTTP的请求1.认识HTTP请求的方法2.认识请求头&#xff08;header&#xff09;3.认识URL3.1 URL是什么&…

嵌入式C语言中结构体使用详解

各位开发者大家好,今天给大家分享一下,嵌入式C语言中结构体的使用方法。 第一个:内存对齐 内存对齐是指一个数据类型在内存中存放时,对其地址的要求。简单来说内存对齐就是使得其内存地址是该类型大小的整数倍,例如 double 类型的变量,其内存地址需要是8的倍数(double大…

驱动开发执行应用层时报ELF: not found,syntax error: unexpected “(“错误

问题&#xff1a; 原因&#xff1a;在跨平台的时候注意我们使用的编译器&#xff0c;我是因为没有没有交叉编译导致的。 出问题之前使用的是gcc test_01_normal.c -o test_01_normal生成的文件&#xff0c;导致&#xff0c;执行时报ELF这种问题。 解决办法&#xff1a;arm-li…

Hybrid Block Storage for Efficient Cloud Volume Service——论文泛读

TOS 2023 Paper 论文阅读笔记整理 问题 传统桌面和服务器应用程序向云的迁移给底层云存储带来了高性能、高可靠性和低成本的挑战。由于这些传统应用程序的I/O模式和一致性要求&#xff0c;与采用特定编程模型和范式&#xff08;如MapReduce[22]和RDD[52]&#xff09;的云原生…

MybatisPlus静态工具Db

前言&#xff1a;在开发的时候&#xff0c;有时Service之间也会相互调用&#xff0c;会出现循环依赖问题&#xff0c;MybatisPlus提供一个静态工具类:Db&#xff0c;其中的一些静态方法与IService中方法签名基本一致&#xff0c;也可以帮助我们实现CRUD功能。 一、Db工具类中常…

games 101 作业4

games 101 作业4 题目题解作业答案 题目 Bzier 曲线是一种用于计算机图形学的参数曲线。在本次作业中&#xff0c;你需要实 现 de Casteljau 算法来绘制由 4 个控制点表示的 Bzier 曲线 (当你正确实现该 算法时&#xff0c;你可以支持绘制由更多点来控制的 Bzier 曲线)。 你需…

vulnhub靶机De-ICE_S2.100_(de-ice.net-2.100-1.0)

下载地址&#xff1a;https://download.vulnhub.com/deice/De-ICE_S2.100_%28de-ice.net-2.100-1.0%29.iso 靶机搭建 注意下载下来的是iso文件接下来说明系统选择 linux的Debian 7.x就可以 然后注意一点我们需要创建一个192.168.2.0/24的网卡进行连接&#xff08;靶机ip地址…

ISCC2024个人挑战赛WP-DLLCode

&#xff08;非官方解&#xff0c;以下内容均互联网收集的信息和个人思路&#xff0c;仅供学习参考&#xff09; 注意到程序调用了Encode函数对明文进行加密&#xff0c;点进去发现是对外部DLL的调用 静态分析DLL中的Encode函数可以得到 写出对应的解密脚本如下 #include <…

SQL常用基础语句(一)-- ABCDE开头

AS 将列名从 count(*) 修改为 total select count(*) as total from users where status0 将列名 username 改为 uname&#xff0c; password 改为 upwd select username as uname, password as upwd from users BETWEEN AND 说明&#xff1a;BETWEEN 筛选的是 >value1且 &l…

澳大利亚.德国-门户媒体投放通稿:需要注意什么地方

概述 在现代社会&#xff0c;新闻媒体的投放成为企业和组织宣传推广的重要手段之一。澳大利亚和德国作为全球重要的经济和科技中心&#xff0c;其新闻媒体也备受关注。本文将介绍澳大利亚和德国的一些主要新闻媒体&#xff0c;并讨论发表新闻稿时需要注意的地方。 澳大利亚媒…

驱动开发之字符设备开发

1.概念 字符设备是 Linux 驱动中最基本的一类设备驱动&#xff0c;字符设备就是一个一个字节&#xff0c;按照字节 流进行读写操作的设备&#xff0c;读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI&#xff0c; LCD 等等都是字符设备&#xff0c;这些设备的驱…

OFDM通信中的部分内容

纠错编码&#xff1a;在无线通信过程中由于传输过程存在噪声等各种非理想因素&#xff0c;在接收端接收到的信息往往相对于发射信息存在误码&#xff0c;通过纠错编码方式可以对少数非连续的误码进行判断和纠正。举个简单的例子&#xff0c;发射端可能发射的信息为00,01,10,11,…

功率电感设计方法2:实例

文章目录 1&#xff1a;美磁的选项手册截图2&#xff1a;设计步骤2.1&#xff1a;设计需求2.2:选择磁芯材料2.3&#xff1a;选择磁芯2.4 查询 A L A_{L} AL​自感系数2.5 初算匝数2.6重新校准验算感量 3&#xff1a;后续 绕线因子4&#xff1a;日常壁纸分享 参考手册链接 1&…

HTML+CSS+JS(web前端大作业)~致敬鸟山明简略版

HTMLCSSJS【动漫网站】网页设计期末课程大作业 web前端开发技术 web课程设计 文章目录 一、网站题目 二、网站描述 三、网站介绍 四、网站效果 五、 网站代码 文章目录 一、 网站题目 动漫网站-鸟山明-龙珠超 二、 网站描述 页面分为页头、菜单导航栏&#xff08;最好可下拉&…

Denoising Diffusion Probabilistic Models 全过程概述 + 论文总结

标题&#xff1a;Denoising&#xff08;&#x1f31f;去噪&#xff09;Diffusion Probabilistic Models&#xff08;扩散概率模型&#xff09; 论文&#xff08;NeurIPS会议 CCF A 类&#xff09;&#xff1a;Denoising Diffusion Probabilistic Models 源码&#xff1a;hojona…

大数据智慧消防解决方案(24页PPT)

方案介绍&#xff1a; 大数据智慧消防解决方案是提升消防安全管理水平、保障人民群众生命财产安全的重要手段。通过集成物联网、云计算、大数据、人工智能等先进技术&#xff0c;构建集监测、预警、指挥、救援于一体的智慧消防系统&#xff0c;将为消防安全事业注入新的活力。…

ubuntu20.04安装cmake3.22.1

背景 由于第一套上位机windows clion22 嵌入式ubuntu20.04的开发环境中&#xff0c;ubuntu20.04上安装的是cmake3.22.1, 为了保持一致&#xff0c;需要指定安装的cmake版本。 下载指定版本的cmake 进入cmake官网的download页面&#xff0c;https://cmake.org/download/&…