0
点赞
收藏
分享

微信扫一扫

在虚拟机上部署hadoop集群(全流程)

非凡兔 2023-06-02 阅读 84

目录

目录

一、二叉树的创建与遍历

1.创建二叉树

构建出来的树如图:

2.二叉树的销毁

3.二叉树的前序遍历[Leetcode144.力扣]

4.二叉树的中序遍历

5.二叉树的后序遍历

二、二叉树的实现

1.获取树中节点的个数

2.获取叶子节点的个数

3.获取第K层节点的个数

4.获取二叉树的高度[Leetcode104.]

5.判断是否为单值二叉树[leetcode965.]

6.检查两棵树是否相同[Leetcode100.]

7.判断是否为另一棵树的子树[Leetcode572.]

8.查找二叉树中值为x的节点

9.翻转二叉树[Leetcode226.]

10.对称二叉树[Leetcode101.]

11.二叉树的遍历[Nowcoder KY11.]

12.判断一棵二叉树是否为平衡二叉树[Leetcode110.]

13.二叉树的层序遍历

14.判断一棵树是否为完全二叉树

总结


一、二叉树的创建与遍历

1.创建二叉树

        从0创建一棵二叉树,首先需要定义树的节点结构,定义之后需要在内存中开辟空间,初始化节点,然后链接节点中的左右指针

typedef int TreeNodeDataType;

typedef struct TreeNode{
    TreeNodeDataType data;
    struct TreeNode * left;
    struct TreeNode * right;
}TDNode;

//在内存中开辟一个树的节点
TDNode * BuyTDNode(TreeNodeDataType x)
{
    TDNode * node = (TDNode *)malloc(sizeof(TDNode*));
    if(node == NULL)
        return;
    //创建好之后初始化
    node->data = x;
    node->left = NULL;
    node->right = NULL;
//创建一棵树
int main()
{
    TDNode* n1 = BuyTDNode(1);
	TDNode* n2 = BuyTDNode(2);
	TDNode* n3 = BuyTDNode(3);
	TDNode* n4 = BuyTDNode(4);
	TDNode* n5 = BuyTDNode(5);
	TDNode* n6 = BuyTDNode(6);


    n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n4->left = n5;
	n4->right = n6;
}

构建出来的树如图:

2.二叉树的销毁

        由于二叉树中的节点是动态开辟出来的内存空间,所以在不使用的时候需要动态销毁。

void DestoryTree(BTNode * root)
{
    free(root->left);
    free(root->right);
    root->val = 0;
    free(root);
}

//注意这里虽然free掉了root,但是这里只是形参,最后在主函数中应该把这个节点置空

 

3.二叉树的前序遍历[Leetcode144.力扣]

        给一棵树的根节点root,返回它节点值的前序遍历,前序遍历的顺序是:根节点-左孩子-右孩子,可以使用递归的方法,把每个子树都看成由左右孩子组成,访问根节点0,然后访问左节点1,此时把左节点1当作根节点0,若还有左节点1,继续访问,访问右节点2,返回到上一层的左节点1,再访问上一层的右节点,直到遍历完整棵树

//按照前序打印这棵树 
void PreOrder(struct TreeNode * root)
{
    if(root ==NULL)
        return;
    printf("%d ",root->data);
    PreOrder(root->left);
    PreOrder(root->right);
}

//打印结果: 1 2 3 4 5 6 

力扣 题解,首先这道题核心也是二叉树的前序遍历,但是要返回遍历的结果,Note中注释返回的结果需要自己开空间,即自己定义一个数组存放遍历的数,题中形参只给了根节点,和一个int * 类型(指针)的returnSize,这里的returnSize是返回的数组大小。

解题逻辑:首先判断树是否为空树,如果是空树return;如果不是空树则开始前序遍历,前序的顺序是根节点,左子树,右子树,所以可以使用递归的方法。首先动态开辟一个数组,  数组中存放遍历的结果,每遍历一个,数组的下标+1,即returnSize+1,如果遍历到空节点,返回上一层递归,再去递归它的左子树,右子树。

Traversal(struct TreeNode * root,int *returnNum,int *returnSize)
{
    //调用的人需要数组和数组的大小,1.可以返回结构体,2.返回指针
    //如果returnSize为int类型,当前的形参只是实参的临时拷贝,不改变实参
    //所以使用一个int * ,当解引用使用形参的时候,会改变实参。
    if(root ==NULL)
        return;
    returnNum[(*returnSize)++] = root->val;
    Traversal(root->left,returnNum,returnSize);
    Traversal(root->right,returnNum,returnSize);
}

int * preorderTraversal(struct TreeNode * root, int * returnSize)
{
    //先自己开辟要返回的数组  数组的类型为int 型,要返回int *类型,所以使用一个指针指向数组
    int * returnNum = (int *)malloc(sizeof(int *)*100);
    //returnSize 为返回的数组的大小 
    *returnSize = 0; // 初始化为0
    if(root ==NULL)
        return NULL;
    //前序遍历单独写一个函数
    Traversal(root,returnNum,returnSize);
    return returnNum;

看完别人的思路,感觉如下的方式写更优解,上面开辟的数组大小是固定的,可以写一个函数统计这棵树的节点个数,再去开辟。




int * preorderTraversal(struct TreeNode * root, int * returnSize)

4.二叉树的中序遍历

void Inorder(struct TreeNode * root)
{
    if(root == NULL)
        return;
    Inorder(root->left);
    printf("%d",root->data);
    Inorder(root->right);
}

5.二叉树的后序遍历

void PostOrder(struct TreeNode * root)
{
    if(root ==NULL)
        return;
    PostOrder(root->left);
    PostOrder(root->right);
    printf("%d",root->data);
}

二、二叉树的实现

1.获取树中节点的个数

使用分治思维,统计根节点的左右节点的个数。遇到左节点,再去统计它的左右节点个数,递归。

一种经典的错误写法如下

int TreeSize(struct TreeNode * root)
{
    if(root ==NULL)
        return;
    int size = 0;
        size++;
    TreeSize(root->left);
    TreeSize(root->right);
    return size;
}

这种写法错误的点在于,没有完全理解递归的栈帧。当定义size的时候,size是一个局部变量,返回的size并不是总共的size,统计出来的左右节点个数没有保存,也没有返回给上一层的根节点。

正确写法如下使用一个三目运算符,如果节点为空,返回0,如果不为空,统计左右节点的个数,再加上自己,即所求的所有的节点个数。

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

2.获取叶子节点的个数

获取叶子节点个数,先明白叶子节点的定义就是没有左右子树的节点,那么在函数中可以写如果根为空,返回0,如果节点不为空,统计左右子树节点的叶子节点,叶子节点即为(root->left==NULL && root->right==NULL)代码如下:

int TreeSizeLeaf(struct TreeNode * root)
{
    if(root ==NULL)
        return 0;
    if(root->left && root->right ==NULL)
        return 1; 
    return TreeSizeLeaf(root->left)+TreeSizeLeaf(root->right);
}

3.获取第K层节点的个数

如图所示:因为这个结构为一棵二叉树,认为节点1为第一层,第一层只会有一个节点。如果现在要求1的第三层的节点个数[3,5,6],无法直接访问到,但是对于2和4[3,5,6]分别是他们的左子树和右子树,对于2和4,[3,5,6]是他们的第二层。对于[3,5,6]本身,他们就是第一层,如果第一层不为空,就返回1,如果第一层为空,返回0。

1的第三层 == 2的第二层(1的左子树)+4的第二层(1的右子树)

                 == (3的第一层(2的左子树)+空 (2的右子树))+(5的第一层(4的左子树)+6的第一层(4的右子树))

由此类推 求当前的第K层节点个数 == 左子树的K-1层+右子树的K-1层,直到k ==1,当k==1的时候,判断这个节点是否为空,如果为空,返回0,如果不为空返回1

int TreeLeveSize(struct TreeNode * root,int k)
{
    if(root ==NULL)
        return 0;
    if(k==1)
    {
        //这里不用再写判断是否为空,因为在调用递归的时候,已经先判断了
       return 1;
    }
    return TreeLevelSize(root->left,k-1)+TreeLevelSize(root->right,k-1);
}

4.获取二叉树的高度[Leetcode104.]

思路:比较当前节点的左右子树的深度,哪个高,返回哪个。树的深度如果只有根节点,深度为0,如果有左子树或者有右子树,深度为1。比较两个数的深度的时候需要有两个统计变量,哪个大返回哪个值+1,+1的原因在于,如果为空,则有0层,如果有一个节点,本层就是1,返回到上一层就是2。

int TreeHeight(struct TreeNode * root)
{
    if(root ==NULL)
        return 0;
    int lh = TreeHeight(root->left);
    int rh = TreeHeighr(root->right);
    return lh > rh? lh+1:rh+1;
}

5.判断是否为单值二叉树[leetcode965.]

题目:如果二叉树的每个节点都具有相同的值,那么这个树就是单值二叉树。题目链接:力扣

思路:当前节点为根节点,判断它和左右子树的值是否相同,如果相同,继续判断,先走到左子树,判断它和它的左右子树是否相同,相同再判断原来根节点的右子树,依次递归。

思想是顺着这个顺序,写代码可以反着写,即我遇到了我的左子树或者右子树和我的值不相同,就return false,如果相同,就继续判断,直到遍历完这棵树。在这里要注意,左右子树可能为空,如果为空就无法访问到val,所以要加上这个条件。

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;
    //如果有1个为false,就返回false
    return isUnivalTree(root->left) && isUnivalTree(root->right);
}

6.检查两棵树是否相同[Leetcode100.]

题目:给两棵二叉树的根节点p,q,编写一个函数来判断两棵树是否相同(如果两棵树在结构上相同,并且节点具有相同的值,则认为他们相同) 题目链接:力扣

思路:先判断它们的根是否相同,如果根相同,比较左子树,左子树相同,再比较右子树。如果都相同,再比较子节点的左子树和右子树。思路顺着写,代码反着写。

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

7.判断是否为另一棵树的子树[Leetcode572.]

题目:给两棵二叉树root,subroot。检验root中是否包含和subroot具有相同结构和节点值的子树,如果存在,返回true,如果不存在,返回false。题目链接:力扣

思路:可以借用判断两个树是否相同的函数

//判断两棵树是否相同
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;
    if(isSameTree(root,subRoot))
        return true;
    //在左右子树中寻找 有一个为true就返回true;
    return isSubtree(root->left,subRoot) || isSubtree(root->right,subRoot);
}

8.查找二叉树中值为x的节点

思路:先判断根,根的值 == x ,返回,如果不等于,再找到左子树,如果左子树的值相等返回,不相等去找右子树,找到返回,找不到返回null(找完了)

但是这里需要注意,如果在左子树中找到返回,返回之后给上一层,并没有返回给第一个函数的return,所以需要保存一个值,找到之后保存这个结果。

BTNode * Find(BTNode * root,int x)
 {
    if(root->data == x)
        return root;
   BTNode * ret1 =  Find(root->left,x)
      if(ret1)
         return ret1;
   BTNode * ret2 = Find(root->right,x)
      if(ret2)
         return ret2;
    return NULL;
}

9.翻转二叉树[Leetcode226.]

题目:给一棵二叉树的根节点root,翻转这棵树,并返回其根节点,题目链接:力扣

思路:给一个tmp交换,然后递归

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
struct TreeNode* invertTree(struct TreeNode* root){
    if(root ==NULL)
        return NULL;
    struct TreeNode * tmp = (struct TreeNode *)malloc(sizeof(struct TreeNode *));
    tmp = root->left;
    root->left = root->right;
    root->right = tmp;

    invertTree(root->left);
    invertTree(root->right);
    
    return root;

}

 

10.对称二叉树[Leetcode101.]

题目:给一个二叉树的根节点root,检查它是否轴对称。题目链接:力扣如图:

 思路:之前判断两个树是否相同,都是用左子树和左子树比较。这道题目可以使用递归的方法,比较左子树和右子树

bool _isSymmetric(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)
        retrun false;
    return _isSymmetric(p->left,q->right) && _isSymmetric(p->right,q->left);
}


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

 

11.二叉树的遍历[Nowcoder KY11.]

题目:编写一个程序,读入用户输入的一串先序遍历的字符串,根据此字符串建立一个二值二叉树(以指针方式存储),例如:ABC##DE#G##F###,其中#表示的是空格,空格字符代表空树,建立起二叉树之后,再对二叉树进行中序遍历,输出遍历结果。

题目链接:二叉树遍历_牛客题霸_牛客网

题目思路:首先要重建这棵树,现在是以前序的遍历存储,根据前序再递归,分别存入到它的左右子树中。然后中序遍历输出

struct TreeNode 
{
    char val;
    struct TreeNode * left;
    struct TreeNode * right;
 }
struct TreeNode * rebulidTree(char * str,int *pi)
{
    if(str[*pi] == "#")
     {   (*pi)++;
         retrun NULL;
     }
    //构建节点,如果不为空,存入值,继续遍历它的左右子树 
    struct TreeNode * root = (struct TreeNode *)malloc(sizeof(struct TreeNode *));
    
    root->val = str[(*pi)++];
    root->left = rebuildTree(str,pi);
    root->right = rebuildTree(str,pi);
    return root;
}
//中序遍历
  void Inorder(struct TreeNode * root)
    {
        if(root ==NULL)
            return;
        Inorder(root->left);
        printf("%c",root->val)
        Inorder(root->right);
    }
int main()
{
    char str[100];
    scanf("%s",&str);
    int i = 0;
    //先重建这棵树
    struct TreeNode * root = rebuildTree(str,&i);
    Inorder(root);
    return 0;
}

12.判断一棵二叉树是否为平衡二叉树[Leetcode110.]

题目:给定一颗二叉树,判断它是否是高度平衡的二叉树,高度平衡:一个二叉树的每个节点的两个左右子树的高度绝对值不超过1,题目链接:力扣

思路:使用递归,统计它的左右子树,判断左右子树的高度,如果大于1,返回false,如果不大于1,继续遍历它的左右子树,直到root为空。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

int TreeHeight(struct TreeNode * root)
{
    if(root ==NULL)
        return 0;
        int leftHeight = TreeHeight(root->left);
        int rightHeight = TreeHeight(root->right);
        return leftHeight > rightHeight ? leftHeight+1 :rightHeight+1;
}

bool isBalanced(struct TreeNode* root){

    if(root ==NULL )
        return true;
    //统计左右子树的高度,左右子树高度的绝对值超过1返回false,否则返回true;
    int left_height = TreeHeight(root->left);
    int right_height = TreeHeight(root->right);
       
    if(abs(left_height -right_height) >1)
        return false;

        return isBalanced(root->left) && isBalanced(root->right);
    return true;

}

 

13.二叉树的层序遍历

        思路:二叉树的层序遍历就是先遍历根节点,然后遍历它的左右子树,遍历完后,再遍历左子树的左右子树,遍历右子树的左右子树。可以使用一个队列,当队列为空的时候,进入根节点,然后出根节点,进入它的左右子树,出根节点,进入左右子树。直到出到队列为空,结束遍历。

        队列之中,如果存二叉树的节点的值,会找不到左右孩子,如果存二叉树的节点,占用空间较多,可以存二叉树的节点指针。代码如下:

void LevelOrder(BTNode * root)
{
    Queue q;
    QueueInit(&q);
    if(root ==NULl)
        return;
    if(root)
        QueuePush(&q,root);
    while(!QueueEmpty(&q))
    {
        BTNode * front = QueueFront(&q);
        print(front->data);
        QueuePop(&q);
        if(front->left)
            QueuePush(&q,front->left);
        if(fornt->right)
            QueuePush(&q,front->right);
     }
}

14.判断一棵树是否为完全二叉树

同样,也是用层序遍历的方法,创建一个队列,如果出的节点是空,后面出的节点不是空,那就不是完全二叉树,如果出空之后后面仍是空,那就是完全二叉树

bool TreeComplete(BTNode * root)
{
    //创建队列
    Queue q;
    QueueInit(&q);
    if(root)
       QueuePush(&q,root);
    while(!QueueEmpty(&q))
    {
        BTNode * front = QueueFront(&q);
        QueuePop(&q)
        if(front ==NULL)
        {
            break;
        }
        
        else
        {
            QueuePush(&q,front->left);
            QueuePush(&q,front->right);
        }
    }
        //break出来判断后面的队列是否为空,如果为空,return true,如果不为空,return false;
        while(!QueueEmpty(&q)
        {
            front = QueuePop(&q);
            if(front != NULL)
                return false;
            Destroy(&q);
            return true;
        }
}

总结

二叉树的相关函数大多用的递归的方法,这个时候需要多画图,理解函数的栈帧,调试看bug。技术有限,如有错误请指正。

举报

相关推荐

0 条评论