• 二叉树基本操作-1



    节点设置

    既然是链式二叉树,那必须得有自己的结点类型,以下是链式二叉树结点类型的定义,为了避免过多重复的代码,下面的问题都统一使用该结点类型。

    typedef int BTDataType;
    typedef struct BinaryTreeNode
    {
    	BTDataType data;
    	struct BinaryTreeNode* left;
    	struct BinaryTreeNode* right;
    }BTNode;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    BTNode* CreateTree()
    {
    	BTNode* n1 = (BTNode*)malloc(sizeof(BTNode));
    	assert(n1);
    	BTNode* n2 = (BTNode*)malloc(sizeof(BTNode));
    	assert(n2);
    	BTNode* n3 = (BTNode*)malloc(sizeof(BTNode));
    	assert(n3);
    	BTNode* n4 = (BTNode*)malloc(sizeof(BTNode));
    	assert(n4);
    	BTNode* n5 = (BTNode*)malloc(sizeof(BTNode));
    	assert(n5);
    	BTNode* n6 = (BTNode*)malloc(sizeof(BTNode));
    	assert(n6);
    
    	n1->data = 1;
    	n2->data = 2;
    	n3->data = 3;
    	n4->data = 4;
    	n5->data = 5;
    	n6->data = 6;
    
    	n1->left = n2;
    	n1->right = n4;
    
    	n2->left = n3;
    	n2->right = NULL;
    
    	n3->left = NULL;
    	n3->right = NULL;
    
    	n4->left = n5;
    	n4->right = n6;
    
    	n5->left = NULL;
    	n5->right = NULL;
    
    	n6->left = NULL;
    	n6->right = NULL;
    
    	return n1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    二叉树的深度优先遍历

    在这里插入图片描述

    前序遍历

    前序遍历,又叫先根遍历。
    遍历顺序:根 -> 左子树 -> 右子树

    //二叉数前序遍历
    void PreOrder(BTNode* root)
    {
    	if (NULL == root)
    	{
    		printf("NULL ");
    		return;
    	}
    	printf("%d ", root->data);
    	PreOrder(root->left);
    	PreOrder(root->right);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    中序遍历

    中序遍历,又叫中根遍历。
    遍历顺序:左子树 -> 根 -> 右子树

    //二叉数中序遍历
    void InOrder(BTNode* root)
    {
    	if (NULL == root)
    	{
    		printf("NULL ");
    		return;
    	}
    	InOrder(root->left);
    	printf("%d ", root->data);
    	InOrder(root->data);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    后序遍历

    后序遍历,又叫后根遍历。
    遍历顺序:左子树 -> 右子树 -> 根

    //二叉数后序遍历
    void PostOrder(BTNode* root)
    {
    	if (NULL == root)
    	{
    		printf("NULL ");
    		return;
    	}
    	InOrder(root->left);
    	InOrder(root->right);
    	printf("%d ", root->data);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    二叉树结点个数

    求解树的结点总数时,可以将问题拆解成子问题:
    1.若为空,则结点个数为0。
    2.若不为空,则结点个数 = 左子树结点个数 + 右子树结点个数 + 1(自己)。

    int TreeSize(BTNode* root)
    {
    	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
    }
    
    • 1
    • 2
    • 3
    • 4

    叶子结点个数

    子问题拆解:
    1.若为空,则叶子结点个数为0。
    2.若结点的左指针和右指针均为空,则叶子结点个数为1。
    3.除上述两种情况外,说明该树存在子树,其叶子结点个数 = 左子树的叶子结点个数 + 右子树的叶子结点个数。

    int TreeLeafSize(BTNode* root)
    {
    	if (root == NULL)
    	{
    		return 0;
    	}
    
    	if (root->left == NULL && root->right == NULL)
    	{
    		return 1;
    	}
    
    	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    树的最大深度

    子问题:
    1.若为空,则深度为0。
    2.若不为空,则树的最大深度 = 左右子树中深度较大的值 + 1。

    int TreeHeight(BTNode* root)
    {
    	if (root == NULL)
    	{
    		return 0;
    	}
    
    	int lh = TreeHeight(root->left);
    	int rh = TreeHeight(root->right);
    
    	return lh > rh ? lh + 1 : rh + 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    第K层结点的个数

    思路:
    相对于根结点的第k层结点的个数 = 相对于以其左孩子为根的第k-1层结点的个数 + 相对于以其右孩子为根的第k-1层结点的个数。

    int TreeKLevel(BTNode* root, int k)
    {
    	assert(k > 0);
    	if (root == NULL)
    	{
    		return 0;
    	}
    	if (k == 1)
    	{
    		return 1;
    	}
    	//转换成求子树的k-1层
    	return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    判断单值二叉树

    思路:
    1.如果为空,返回true
    2.如果左或者右不为空,且val不相等,直接返回false
    3.分解子问题,分为左子树和右子树,对结果进行&操作

    bool isUnivalTree(struct TreeNode* root){
        if(root == NULL)
        {
            return true;
        }
    
        if(root->left && root->left->val != root->val)
        {
            return false;
        }
    
        if(root->right && root->right->val != root->val)
        {
            return false;
        }
    
        return isUnivalTree(root->left) && isUnivalTree(root->right);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    值为X的结点

    子问题:
    1.先判断根结点是否是目标结点。
    2.再去左子树中寻找。
    3.最后去右子树中寻找。

    BTNode* TreeFind(BTNode* root, BTDataType x)
    {
    	//这种方法只会寻找左树 不会寻找右树
    	//if (root == NULL)
    	//{
    	//	return NULL;
    	//}
    	//if (root->data == x)
    	//{
    	//	return root;
    	//}
    	//return TreeFind(root->left, x);
    	//return TreeFind(root->right, x);
    
    	if (root == NULL)
    	{
    		return NULL;
    	}
    
    	if (root->data == x)
    	{
    		return root;
    	}
    
    	//先去找左树
    	BTNode* lret = TreeFind(root->left, x);
    	if (lret)
    	{
    		return lret;
    	}
    	BTNode* rret = TreeFind(root->right, x);
    	if (rret)
    	{
    		return rret;
    	}
    
    	return NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    判断两棵树是否相同

    判断两棵二叉树是否相同,也可以将其分解为子问题:
    1.比较两棵树的根是否相同。
    2.比较两根的左子树是否相同。
    3.比较两根的右子树是否相同。

    bool isSameTree(struct TreeNode* p, struct TreeNode* q){
        if((p == NULL && q != NULL) || (p != NULL && q == NULL))
        {
            return false;
        }
    
        if(p == NULL && q == NULL)
        {
            return true;
        }
    
        if(p->val != q->val)
        {
            return false;
        }
    
        return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    判断一棵树是否是另一棵树的子树

    判断 subRoot 是否是二叉树 root 的子树,即检验 root 中是否包含和 subRoot 具有相同结构和结点值的子树,其中 root 和 subRoot 均为非空二叉树。
    如下图中,subRoot就是root的一棵子树:
    在这里插入图片描述
    思路:
     依次判断以 root 中某一个结点为根的子树是否与subRoot相同。
    在这里插入图片描述
     实际上,当发现 root 中的某一个子树与 subRoot 相匹配时,便不再继续比较其他子树,所以图中只会比较到序号2就结束比较了。

    bool isSameTree(struct TreeNode* p, struct TreeNode* q){
        if((p == NULL && q != NULL) || (p != NULL && q == NULL))
        {
            return false;
        }
    
        if(p == NULL && q == NULL)
        {
            return true;
        }
    
        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);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    前序遍历->接口型题目

    注意,接下来所要说的深度遍历与前面有所不同,前面说到的深度遍历是将一棵二叉树遍历,并将遍历结果打印屏幕上(较简单)。而下面说到的深度遍历是将一棵二叉树进行遍历,并将遍历结果存储到一个动态开辟的数组中,将数组作为函数返回值进行返回。

    思路:
    1.首先计算二叉树中结点的个数,便于确定动态开辟的数组的大小。
    2.遍历二叉树,将遍历结果存储到数组中。
    3.返回数组。

    int TreeSize(struct TreeNode* root)
    {
        if(root == NULL)
        {
            return 0;
        }    
        return 1 + TreeSize(root->left) + TreeSize(root->right);
    }
    
    void preorder(struct TreeNode* root, int* a, int* pi)
    {
        if(root == NULL)
        {
            return;
        }
        a[*pi] = root->val;
        (*pi)++;
        preorder(root->left, a, pi);
        preorder(root->right, a, pi); 
    }
    
    int* preorderTraversal(struct TreeNode* root, int* returnSize){
        int i = 0;
        int Num = TreeSize(root);
        int* a = (int *)malloc(sizeof(int) * Num);
    
        preorder(root, a, &i);
        
        *returnSize = Num;
        return a;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

  • 相关阅读:
    Android数据存储及Room数据库的使用和原理分析
    STM32F103标准库开发---SPI实验---W25Qxx系列外部Flash芯片
    荣耀笔试(8.16)
    【结构型】桥接模式(Bridge)
    jdk工具之jstack
    非零基础自学Java (老师:韩顺平) 第2章 Java概述 2.18 Java代码规范 && 2.19 DOS命令
    学习c语言中的几道习题(小有难度)!
    Doceker-compose——容器群集编排管理工具
    基于阈值预分割的区域生长分割法研究-含Matlab代码
    [React]useEffect中return函数执行时机
  • 原文地址:https://blog.csdn.net/jiejiezuishuai/article/details/126295874