刷题 ------ 二叉树

1.相同的树

在这里插入图片描述

  • 同时递归遍历其所给的两个树的每个节点。
  • 对其进行挨个比较就好。

在这里插入图片描述

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


bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
    //都是空节点
    if(p == NULL && q == NULL)
    {
        return true;
    }

    //其中有一个节点是空
    if((p == NULL && q != NULL) || (p != NULL && q == NULL))
    {
        return false;
    }

    //走到这里,就证明两棵树的当前两个节点都有值,那么就去比较他俩的值
    if(p -> val != q -> val)
    {
        return false;
    }


    return isSameTree(p -> left,q -> left) && isSameTree(p -> right,q -> right);
}

2.对称的二叉树

在这里插入图片描述
这道题和上面的想同的树两道题可以说是一模一样的,如果真正的懂了上面那道题,这道题只需要改一句话就好了。
咱们可以尝试将例题中的树拆分成两课树。
在这里插入图片描述
在这里插入图片描述
将p的左孩子和q的右孩子去对应,p的右孩子和q的左孩子去作比较,这样就好了
在这里插入图片描述
代码如下,直接俄调用上一题的函数,只是改了一行而已。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
bool isSameTree(struct TreeNode* p, struct TreeNode* q)
{
    //都是空节点
    if(p == NULL && q == NULL)
    {
        return true;
    }

    //其中有一个节点是空
    if((p == NULL && q != NULL) || (p != NULL && q == NULL))
    {
        return false;
    }

    //走到这里,就证明两棵树的当前两个节点都有值,那么就去比较他俩的值
    if(p -> val != q -> val)
    {
        return false;
    }


    return isSameTree(p -> left,q -> right) && isSameTree(p -> right,q -> left);
}


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

3.二叉树的最大深度

在这里插入图片描述
我们同样还是运用递归的方式去实现,做了这几道题,也能感觉出来点啥,这几种方式,其实跟在学习数据结构图的遍历的时候,那里有一个深度优先算法,就是一条路走到黑的那种,其实还是很类似的。

  • 先去判断它的左孩子,再去递归判断它的右孩子。
  • 而当前这棵小树的深度又是Max(left,right)+1;
    在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
int maxDepth(struct TreeNode* root)
{
    if(root == NULL)
    {
        return 0;
    }

    int i,j;

    
    if(root -> left != NULL)
    {
        //左孩子不为空,进去递归求值
        i = maxDepth(root -> left);
    }
    else
    {
        //左孩子为空,为 0 即可,
        i = 0;
    }

    if(root -> right != NULL)
    {
        j = maxDepth(root -> right);
    }
    else
    {
        j = 0;
    }

    return i > j ? i+1:j+1;
}

上面的代码长,但是方便理解,下面的代码和上面的原理是一样的

int Max(int x, int y)
{
    return x > y ? x : y;
}

int maxDepth(struct TreeNode* root)
{
    if(root == NULL)
    {
        return 0;
    }

    return Max(maxDepth(root -> left), maxDepth(root -> right)) + 1;
}

4.将有序数组转化为二叉搜索树

在这里插入图片描述
这道题在刷数组的时候遇到过,当时也没有仔细写清楚题解。
BST树的中序遍历就是升序序列
因为题目中的数组已经是升序的排放好了,所以数组中间的那个数,一定是BST树的根节点。
然后左孩子的话,就去(left,mid-1)这个区间去递归实现
而右孩子的就去(mid+1,right)这个区间去。

  • 如果left > right ,那么返回NULL即可,,代表没有值了
  • mid 一定是当前子树的根节点。
  • 然后分别去递归构造其左右孩子即可。
struct TreeNode* CreateTree(int* nums,int left,int right)
{
    if(left > right)
    {
        return NULL;
    }
    else
    {
        struct TreeNode* root = (struct TreeNode*)malloc(sizeof(struct TreeNode));
        int mid = (left + right)/2;
        root -> val = nums[mid];
        root -> left = CreateTree(nums,left,mid-1);
        root -> right = CreateTree(nums,mid+1,right);
        return root;
    }


}


struct TreeNode* sortedArrayToBST(int* nums, int numsSize) {
    return CreateTree(nums,0,numsSize-1);
}

5.平衡二叉树

在这里插入图片描述
平衡二叉树的条件是左右子树的高度差不能超过1.
我们上边已经可以求出二叉树的深度了,所以去求出每个二叉树之间的深度差不也很简单么。

方法一:从上往下

  • 从上到下,对于每课树的求其深度。
  • 求出来后,判断是否满足绝对值是1。
bool isBalanced(struct TreeNode* root)
{
    if(root == NULL)
    {
        return true;
    }

    int i,j;
    i = Depth(root -> left);
    j = Depth(root -> right);

    if(abs(i - j) > 1)
    {
        return false;
    }

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

能发现,从上往下求深度,其实对于没课子树都有在重复的进行递归,求深度,时间上并不优秀。

放法二:从下往上
如果可以从下往上求的话,就不会一直重复的求子树的深度。

  • 从一开始然后就一直往下左孩子下面走,求出左孩子的深度之后再去求右孩子的深度。
  • 如果正常则返回深度,不平衡的情况下,将其当前的深度设置成-1这里的-1没有任何的特殊含义
  • 只是一个标志,告诉咱这颗子树已经不平衡了,就ok了。
int Max(int x, int y)
{
    return x > y ? x : y;
}

//求深度
int Depth(struct TreeNode* root)
{
    if(root == NULL)
    {
        return 0;
    }
    int lefthight = Depth(root -> left);
    int righthight = Depth(root -> right);
	
	//如果人任意一颗子树的高度是 -1 则代表不平衡,所以到根节点也返回-1即可
    if(abs(lefthight - righthight) > 1 || lefthight == -1 || righthight == -1)
    {
        return -1;
    }
    else
    {
        return Max(lefthight,righthight)+1;
    }

   
}


bool isBalanced(struct TreeNode* root)
{
	//如果深度是 -1 则代表不平衡
    return Depth(root) >= 0;
}

这个办法对于遍历二叉树的深度,只进行了一次,比上一个时间上提升了不少。

6.二叉树的最小深度

在这里插入图片描述
同样是求二叉树的深度,但是不同的是,我们每次递归的时候需要得到的是各个子树的最小的那个深度。

  • 如果当前的节点即没有左孩子也没有右孩子,那么就是叶子节点,所以返回1
  • 重点是每次取出那个最小的值。
  • 还有就是最开始有一个 root == NULL的条件后面还需判断,因为最开始的那个仅仅是判断第一次进去这个函数的时候,如果以后不判断就会出问题比如下面这张图

在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
int Min(int x, int y)
{
    return x > y ? y : x;
}

int minDepth(struct TreeNode* root)
{
    if(root == NULL)
    {
        return 0;
    }

    if(root -> left == NULL && root -> right == NULL)
    {
        //叶子节点
        return 1;
    }
    int ans = INT_MAX;
    if(root -> left != NULL)
    {
        ans = Min(minDepth(root -> left),ans);
    
    if(root -> right != NULL)
    {
        ans = Min(minDepth(root -> right),ans);
    }

    return ans + 1;
}

7.路径总合

在这里插入图片描述
这道题我刚开始想的是如何记录走过的节点相加起来的数 等于 targetSum 但是想不同,发现题解是走一个减去一个,我666啊,有这个思路就可以做了

  • 首先必须得是叶子节点,然后去判断是否和当前的targetSum相等。
  • 如果相等的话,返回true就好了。
  • 没传一次targetSum - 去一下当前节点的值。
  • 注意最后的是 ||
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

bool hasPathSum(struct TreeNode* root, int targetSum)
{
    if(root == NULL)
    {
        return false;
    }

    //该节点是叶子节点
    if(root -> left == NULL && root -> right == NULL)
    {
        //如果相等 返回 true 就好了
        return targetSum == root -> val;
    }

    return hasPathSum(root -> left,targetSum - (root -> val)) ||  
            hasPathSum(root -> right,targetSum - (root -> val));
}

8. 完全二叉树的节点个数

在这里插入图片描述
一共有多少节点,也就是遍历一遍二叉树就好了,我们学会的三种遍历方式,进行遍历,拿上一个变量记录一下就行。

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

void Inorder(struct TreeNode* T,int* num)
{
    if(T == NULL)
    {
        return;
    }

    Inorder(T -> left,num);
    (*num)++;
    Inorder(T -> right,num);
}

int countNodes(struct TreeNode* root)
{
    int ans = 0;
    Inorder(root,&ans);

    return ans;    
}

这样的时间很慢,题解中提到了可以用位运算,位运算不是很清楚就先算了。

9.翻转二叉树

在这里插入图片描述
同样是采取左右根的后序遍历方式,对其每个节点的左右孩子进行翻转。
在这里插入图片描述

/**
 * 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 root;
    }

    struct TreeNode* leftchild = invertTree(root->left);
    struct TreeNode* rightchild = invertTree(root->right);

    root -> left = rightchild;
    root -> right = leftchild; 


    return root;
}

10.二叉树的所有路径

在这里插入图片描述

方法一:DFS

运用(DFS)深度优先算法,需要一个栈来辅助。

  • 首先去找第一个叶子节点,如果当前的不是叶子节点,那么就去将该节点入栈。
  • 当发现当前节点是叶子节点的时候,开始去构造当前的路径
  • 运用spritnf函数,同时记录自己当前的长度,下面代码中 tmp + len 不理解可以看下图:
    在这里插入图片描述
  • 构造好每一个的路径,就可以导入paths数组中去了.

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
void ConstructionPaths(char** paths,struct TreeNode* root,int* returnSize,int* stack, int top)
{
    if(root == NULL)
    {
        return;
    }


    if(root -> left == NULL && root -> right == NULL)
    {
        //找到叶子节点了开始录入路劲
        char* tmp = (char*)malloc(sizeof(char) * 101);
        int inde = 0;
        int len = 0;
        //添加栈中的路径
        for (int i = 1; i <= top; i++)
        {
            len += sprintf(tmp + len,"%d->",stack[i]); 
        }
        
        //添加当前叶子节点
        sprintf(tmp + len,"%d",root -> val);
        paths[(*returnSize)++] = tmp;
    }
    else
    {
        //不是叶子节点,入栈
        stack[++top] = root -> val;
        ConstructionPaths(paths,root -> left,returnSize,stack,top);
        ConstructionPaths(paths,root -> right,returnSize,stack,top);
    }



}


char** binaryTreePaths(struct TreeNode* root, int* returnSize)
{
    char** paths = (char**)malloc(sizeof(char*) * 101);
    int stack[102] = {0};   //栈
    int top = 0;
    *returnSize = 0;
    ConstructionPaths(paths,root,returnSize,&stack,top);
    return paths;
}

栈也会随着递归而变化的,所以没必要进行出栈的这一算法。

方法二:BFS

还有一种是(BFS)广度优先搜索算法,这一算法需要运用两个队列来实现。(节点队列)(路径队列)

  • 首先将其根节点入队列,
  • 然后进行出队列的这一循环过程中,如果当前队首元素是叶子节点,将其队列中的路径直接导出即可
  • 如果不是叶子节点,那么就去将该节点入节点队列,从而将其路径入路径队列。

队列中的front 和 rear 是同时指向两个队列的

#define MAX_SIZE 101


char** binaryTreePaths(struct TreeNode* root, int* returnSize)
{
    //答案 -- 最后的全部路径
    char** paths = (char**)malloc(sizeof(char*) * MAX_SIZE);
    *returnSize = 0;

    //节点队列
    struct TreeNode** nodeQueue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * MAX_SIZE);
    //每个节点对应的路径队列
    char** pathQueue = (char**)malloc(sizeof(char*) * MAX_SIZE);
    //两个队列的前后指针,同时运行的
    int rear = 0, front = 0;

    //将根节点去入队列
    char* tmp = (char*)malloc(sizeof(char) * MAX_SIZE);
    sprintf(tmp,"%d",root -> val);
    //入队列
    nodeQueue[rear] = root;
    pathQueue[rear++] = tmp;

    while(front < rear)
    {
        //出队列
        struct TreeNode* curNode = nodeQueue[front];
        char* curPath = pathQueue[front++];

        if(curNode -> left == NULL && curNode -> right == NULL)
        {
            //当前队首节点是叶子
            paths[(*returnSize)++] = curPath;
        }
        else
        {
            //不是叶子
            int len = strlen(curPath);
            
            //左孩子入队列
           if(curNode -> left != NULL)
            {
                char* tmp = (char*)malloc(sizeof(char) * MAX_SIZE);

                //构造新的路径
                for (int i = 0; i < len; i++)
                {
                    tmp[i] = curPath[i];
                }
                sprintf(tmp + len,"->%d",curNode -> left -> val);

                //将新的节点以及路径入队列
                nodeQueue[rear] = curNode -> left;
                pathQueue[rear++] = tmp;
            }

           //右孩子入队列
            if(curNode -> right != NULL)
            {
                char* tmp = (char*)malloc(sizeof(char) * MAX_SIZE);
                //构造新的路径
                for (int i = 0; i < len; i++)
                {
                    tmp[i] = curPath[i];
                }
                sprintf(tmp + len,"->%d",curNode -> right->val);

                //将新的节点以及路径入队列
                nodeQueue[rear] = curNode -> right;
                pathQueue[rear++] = tmp;
            }
        }

    }

    return paths;
}


11.左叶子之和

在这里插入图片描述

方法一:DFS

用深度优先算法:当前节点的有左孩子,并且左孩子是叶子节点,那么就记录下来。

bool IsLeaf(struct TreeNode* node)
{
    if(node -> left == NULL && node -> right == NULL)
    {
        return true;
    }
    else
    {
        return false;
    }
}


void DFS(struct TreeNode* root, int* sum)
{
    if(root == NULL)
    {
        return;
    }

    DFS(root -> left,sum);
    if((root -> left != NULL) && (IsLeaf(root -> left)))
    {
        //左孩子是叶子节点。
        *sum += (root -> left -> val); 
    }

    DFS(root -> right,sum);
}

int sumOfLeftLeaves(struct TreeNode* root)
{
    if(root == NULL)
    {
        return 0;
    }
    int sum = 0;
    DFS(root,&sum);

    return sum;
}

方法二:BFS

广度优先搜索算法

  • 和上一题一样,先将根节点入队列。
  • 然后去判断当前节点的左孩子是否是叶子,出队列的捅死再将其左右孩子入队列即可。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

//判断当前节点是否是叶子节点
bool IsLeaf(struct TreeNode* node)
{
    if(node -> left == NULL && node -> right == NULL)
    {
        return true;
    }
    else
    {
        return false;
    }
}


int sumOfLeftLeaves(struct TreeNode* root)
{
    if(root == NULL)
    {
        return 0;
    }
    int sum = 0;
    struct TreeNode** nodeQueue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 10001);
    int front = 0, rear = 0;

    //根节点入队列
    nodeQueue[rear++] = root;
    while(front < rear)
    {
        //出队列
        struct TreeNode* curNode = nodeQueue[front++];
        //判断
        if( curNode -> left != NULL && IsLeaf(curNode -> left))
        {
            //是叶子节点
            sum += curNode -> left -> val;
        }

         //将其左右孩子入队列
        if(curNode -> left != NULL)
        {
            nodeQueue[rear++] = curNode -> left;
        }
        if(curNode -> right != NULL)
        {
            nodeQueue[rear++] = curNode -> right;
        }

    }

    return sum;
}

12. 二叉搜索树中的众数

在这里插入图片描述
这道题暴力即可求解,任意一种遍历方式遍历二叉树,然后去记录其各个数出现的次数,遍历出出现最多次数的数即可,这样做也可以,而这种方式说到底也是运用数组和哈希表的方式去做,可是时间上就不尽人意了

void InorderTraversal(struct TreeNode* root)
{
    if(root == NULL)
    {
        return;
    }

    InorderTraversal(root -> left);
    insert(root -> val);	//插入哈希表
    InorderTraversal(root -> right);
}


int* findMode(struct TreeNode* root, int* returnSize)
{
    int* ans = (int*)malloc(sizeof(int) * 1000);
    *returnSize = 0;
    InorderTraversal(root);
    int i,maxnum = INT_MIN;
    for (i = 0; i < 100; i++)
    {
        Node* cur = hashtable[i];
        while(cur != NULL)
        {
            if(cur -> value > maxnum)
            {
                maxnum = cur -> value;
            }
            cur = cur -> next;
        }
    }
    for (i = 0; i < 100; i++)
    {
        Node* cur = hashtable[i];
        while(cur != NULL)
        {
            if(cur -> value == maxnum)
            {
                ans[(*returnSize)++] = cur -> key;
            }

            cur = cur -> next;
        }
    }
	
	//清空哈希
    Clear();
    return ans;
}

方法二:leetcode题解
因为是二叉搜索树(BST)所以他的中序遍历一定是一个和数组一样的有序序列。

  • 把他想象成数组众的元素,从第一个开始遍历

  • 因为它是有序的,所以用base 来记录当前这一段连续的数字是谁,x从上往下也就是数组众的从左往右
    在这里插入图片描述

  • 当base == x 那么就去记录出现的次数 count 自增即可

  • 当base != x时候,证明此阶段的数遍历完了,去更新新的base以及出现次数 base = x count = 1

  • 接着就是去判断 count 和maxCount是否一样,

  • 如果一样,直接插入ans数组中就好哦了

  • 如果count > maxCount 了就说明出现新的众数了,将ans数组清空,重新记录下当前的base。

int* ans;
int ansSize;
int base,count,maxCount;

void UpData(int x)
{
    if(x == base)
    {
        count++;//自增即可。
    }
    else
    {
        //新的数出现
        base = x;   //
        count = 1;
    }

    if(count == maxCount)
    {
        //当前base 出现的次数 等于众数次数
        ans[ansSize++] = base;
    }

    if(count > maxCount)
    {
        //新的众数出现
        maxCount = count;
        ansSize = 0;
        ans[ansSize++] = base;
    }


}


void DFS(struct TreeNode* root)
{
    if(root == NULL)
    {
        return;
    }

    DFS(root -> left);
    UpData(root -> val);
    DFS(root -> right);
}


int* findMode(struct TreeNode* root, int* returnSize)
{
    base = count = maxCount = 0;
    ansSize = 0;
    ans = (int*)malloc(sizeof(int) * 4001);

    DFS(root);
    *returnSize = ansSize;
    return ans;
}


13.二叉搜索树的最小绝对差

在这里插入图片描述
还是二叉搜索树,所以对其进行中序遍历的结果就是一个升序的,然后将其存放到数组中去,最后在一个升序的数组中去找最小绝对差值谁也会把。

void InorderTraversal(struct TreeNode* root,int* result,int* size)
{
    if(root == NULL)
    {
        return;
    }
    InorderTraversal(root -> left,result,size);
    result[(*size)++] = root -> val;
    InorderTraversal(root -> right,result,size);

}


int getMinimumDifference(struct TreeNode* root)
{
    int* result = (int*)malloc(sizeof(int) * 40001);
    int size = 0;
    InorderTraversal(root,result,&size);
    int i,minGap = INT_MAX;
    for (i = 1; i < size; i++)
    {
        int gap = result[i] - result[i-1];
        if(minGap > gap)
        {
            minGap = gap;
        }
    }

    return minGap;
}

这种办法谁也会,但是咱可以再中序遍历结束时候就可以算出差值,没有必要再多浪费一个数组空间,以及再遍历一遍它。

  • 我们拿一个prev的指针记录着上一个数据是什么
  • 然后比较每次求出的差值大小就好了

要注意的是,得使用二级指针,因为你要修改指针的值嘛,当然你也可以拿一个变量去记录值,这都是无所谓的。

void InorderTraversal(struct TreeNode* root,struct TreeNode** prev, int* minGap)
{
    if(root == NULL)
    {
        return;
    }
    InorderTraversal(root->left,prev,minGap);
    if(*prev != NULL)
    {
        int gap = (root -> val) - ((*prev) -> val);
        if(gap < *minGap)
        {
            *minGap = gap;
        }
    }
    *prev = root;

    InorderTraversal(root->right,prev,minGap);
}


int getMinimumDifference(struct TreeNode* root)
{
    struct TreeNode* prev = NULL;
    int minGap = INT_MAX;
    InorderTraversal(root,&prev,&minGap);

    return minGap;
}

14.二叉树的直径

在这里插入图片描述
这道题主要是考察,直径在这里的概念是啥,先观察上图,这一看就能看出来,最左边的孩子到最右边的孩子一定是那个最长的,想都不用想,没有什么比从一左边拐到右边长了,最左边的孩子的路径是2,最右边的是1.所以直径是 2+1 = 3.
你看下图:下图最左边的孩子是 4 还是 6. 最右边的孩子是3还是7,现在就会发现,说成左右孩子不合理,应该是 左子树的深度+右子树的深度 = 直径
在这里插入图片描述
而如何去就深度大家都是会的,上面也有求深度的题,下面就是求深度的代码。
在这里插入图片描述

  • 所以每一个根节点,都有其所对应的左右子树。
  • 只需要去判断每一棵子树之间的最大长度,也就是直径,来回去比较即可。
  • 只需要哦在求深度的代码上加上一句话就好了
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */

int Max(int x, int y)
{
    return x > y ? x : y;
}

int Depth(struct TreeNode* root,int* max)
{
    if(root == NULL)
    {
        return 0;
    }
    int leftHight = Depth(root->left,max);
    int rightHight = Depth(root -> right,max);

    if(leftHight + rightHight > *max)
    {   //左子树+右子树的深度就是最长路径
        *max = leftHight + rightHight;
    }


    return Max(leftHight,rightHight)+1;
}


int diameterOfBinaryTree(struct TreeNode* root)
{   
    int max = INT_MIN;
    int depth = Depth(root,&max);
    //printf("depth = %d \n",depth);
    return max;
}

15.二叉树的深度

在这里插入图片描述
这道题的思想说到底还是运用深度优先搜索算法(DFS)

  • 用一个变量(slope)随时更新坡度。
  • 而DFS函数本身则是负责求出当前节点左孩子和右孩子的val之和
  • 如果是空指针则返回0,否则的话就直接返回 左孩子的值 + 右孩子的值 + 自身的值。

在这里插入图片描述

int DFS(struct TreeNode* root,int* slope)
{
    if (root == NULL)
    {
        return 0;
    }
    int leftSum = 0, rightSum = 0;
    //分别计算左右孩子的和
    leftSum += DFS(root->left, slope);
    rightSum += DFS(root->right, slope);

    *slope += abs(leftSum - rightSum);
    return leftSum + rightSum + root -> val;
}


int findTilt(struct TreeNode* root)
{
    int slope = 0;
    DFS(root,&slope);

    return slope;
}

16.另一棵子树

在这里插入图片描述
在刷二叉树当中,第一题中就出现了判断两个树是否相同,这无非就是去遍历二叉树中的每个节点,然后判断是否是于subRoot相同。
下面的方式是最简单粗暴的一种。

  • 我们在递归遍历root时候是对其进行挨个节点的访问,然后让它与subRoot判断两树是否相同。
  • 如果但凡有一个相同,直接返回true即可。

//传过来两颗树,判断这两课树是否想同
bool isSameTree(struct TreeNode* p,struct TreeNode* q )
{
    //都是空指针
    if(p == NULL && q == NULL)
    {
        return true;
    }

    //一个有值,另一个没有,那么就肯能相同
    if((p == NULL && q != NULL) || (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;
    }

    return   isSubtree(root -> left,subRoot) || 
            isSubtree(root -> right,subRoot) || 
            isSameTree(root,subRoot);
}

17.根据二叉树创建字符串

在这里插入图片描述

这道题主要是有四种情况,搞清楚这四种情况,就能写出来了。


 1. 左右孩子都没有:
对于这种来说,就啥也不用动,直接把自己的值写入就好了。
 2. 左右孩子都有:
需要分别对它的左右孩子都加入括号。
 3. 只有左孩子:
对他的右孩子加入括号
 4. 只有右孩子:
相对于这种情况,你需要在左孩子上加入一个空括号,从而代表左孩子是空的,不然等结果出来又该如何判断他是左孩子还右孩子。
char* ans;
int size;

void DFS(struct TreeNode* root)
{
    if(root == NULL)
    {
        return;
    }

    size += sprintf(ans + size,"%d",root -> val);
    //如果当前节点   没有孩子,直接写入即可,不需要加任何括号
    if(root -> left == NULL && root -> right == NULL)
    {
        return;
    }

    //如果当前节点   有两个孩子,需要在两个孩子的结果外都加上括号
    if(root -> left != NULL && root -> right != NULL)
    {
        //左孩子
        ans[size++] = '(';
        DFS(root -> left);
        ans[size++] = ')';

        //右孩子
        ans[size++] = '(';
        DFS(root -> right);
        ans[size++] = ')';
    }

    //只有左孩子,给左孩子加括号,不给右孩子加
    if(root -> left != NULL && root -> right == NULL)
    {
        //左孩子
        ans[size++] = '(';
        DFS(root -> left);
        ans[size++] = ')';
    }

    //只有右孩子,只给左孩子加上空括号,右孩子也加括号
    if(root -> left == NULL && root -> right != NULL)
    {
        //左孩子
        ans[size++] = '(';
        ans[size++] = ')';
        //右孩子
        ans[size++] = '(';
        DFS(root -> right);
        ans[size++] = ')';
    }


}


char* tree2str(struct TreeNode* root)
{
    ans = (char*)malloc(sizeof(char) * 100000);
    size = 0;

    DFS(root);
    ans[size] = '\0';
    return ans;
}

18.合并二叉树

在这里插入图片描述
在数据结构中学过会构造二叉树的话,这就不是问题了,我先是死板一点的,将所有可能一一列举出来的。


 1. 两个都为空的话,生成树自然也是空的
 2. 两个都有值的话,生成树的节点就是他俩的和
 3. 一个有一个没有的话,就去选择那一个有的值赋予即可.
//将 p q 合并
void Merge(struct TreeNode** T,struct TreeNode* p,struct TreeNode*q)
{
    if(p == NULL && q == NULL)
    {
        (*T) = NULL;
    }
    else
    {
        (*T) = (struct TreeNode*)malloc(sizeof(struct TreeNode));
        //两个都不为空 加起来的值
        if(p != NULL && q != NULL)
        {
            (*T) -> val = p -> val + q -> val;
            Merge((&(*T)->left),p -> left, q -> left);
            Merge((&(*T)->right),p -> right, q -> right);
            return;
        }

        if(p != NULL)
        {
            //左孩子不为空,只传左边的,右边的不动
            (*T) -> val = p -> val;
            Merge((&(*T)->left),p -> left, q);
            Merge((&(*T)->right),p -> right, q);
        }
        else
        {
            //右孩子不为空,只传右边的,左边的不动
            (*T) -> val = q -> val;
            Merge((&(*T)->left),p, q -> left);
            Merge((&(*T)->right),p, q -> right);
        }

    }

}


struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2)
{
    struct TreeNode* newT = NULL;
    Merge(&newT,root1,root2); 
    return newT;
}

其实这也就DFS算法,官方题解更为简单,在代码上优化了不少,思想其实还是一样的。

19.二叉树的层平均值

在这里插入图片描述
如果会二叉树的层序遍历,这道题并不是难的,而层序遍历则需要构造出一个队列来。

  • 首先将根节点入队列,保证队列中有元素。
  • 然后每次循环时候,那一个变量number记录当前层元素个数,也就是目前队列中的元素个数。
  • 然后挨个出队列的同时,加上自身的值,再将其孩子入队列就好了。
#define MAX_SIZE 10000


double* averageOfLevels(struct TreeNode* root, int* returnSize)
{
    //队列
    struct TreeNode** queue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * MAX_SIZE);
    int front = 0, rear = 0;

    //结果 答案
    double* ans = (double*)malloc(sizeof(double) * MAX_SIZE);
    *returnSize = 0;

    queue[rear++] = root;

    //队列不为空
    while(front != rear)
    {
        int number = rear - front;      //记录当前层有多少个元素
        int size = number;
        double sum = 0.0;

        while(size > 0)
        {
            //出队列
            root = queue[front++];
            sum += root -> val;

            if(root -> left != NULL)
            {
                queue[rear++] = root -> left;
            }

            if(root -> right != NULL)
            {
                queue[rear++] = root -> right;
            }
            size--;
        }

        ans[(*returnSize)++] = sum / number;
    }

    return ans;
}

20. 两数之和IV ---- 输入二叉搜索树

在这里插入图片描述
这个题目,勾起了各位的回忆没,从刚开始刷题,leetcode第一道题,两数之和。
有多少人倒在了第一题啊?是因为不熟悉这种刷题方式吧估计都是,反正我第一次刷题时候,C都没学完,就好奇的不行,想看看,我清楚的记得我当时是把,那个答案用printf打印出来了,然后以为它给了我一个main函数,我返回值是return 0;到了后来才慢慢的懂了。

  • 二叉搜索树中序遍历即升序数组。
  • 利用双指针一前,一后,去计算值,然后与所给的K进行比较。
  • 从而选择是left++,还是right–
#define MAX_SIZE 10000

void Inorder(struct TreeNode* root,int* nums,int* size)
{
    if(root == NULL)
    {
        return;
    }

    Inorder(root -> left,nums,size);
    nums[(*size)++] = root -> val;
    Inorder(root -> right,nums,size);
}


bool findTarget(struct TreeNode* root, int k)
{
    int* nums = (int*)malloc(sizeof(int) * MAX_SIZE);
    int size = 0;
    //二叉搜索树中序遍历得到升序数组。
    Inorder(root,nums,&size);

    //双指针
    int left = 0,right = size - 1;
    while(left != right)
    {
        if(nums[left] + nums[right] < k)
        {
            left++;
        }
        else if(nums[left] + nums[right] > k)
        {
            right--;
        }
        else
        {
            return true;
        }
    }

    return false;
}

还有一种办法就是利用哈希表来做了,用哈希表这种方式做的话,就没有必要一定用中序遍历的方式了。

  • 去遍历二叉树的每一个节点,如果发现 k - x(当前的节点)在哈希表中没有,那么就将 x 插入哈希表
  • 如果k - x 在哈希表中已经出现,直接返回true就好了。

代码中出现的哈希函数就不往出放了,太乱了,这整个哈希函数在上面的12题那个众数中也源码实现。
下面代码中之所以又套用了一个函数,是因为我的哈希表是全局变量,在leetcode上它调试别的测试用例,以前测试用例中的值还在哈希表中,所以我得提前清空一下。

#define TABLE_SIZE 10000
Node* hashtable[TABLE_SIZE];
bool helper(struct TreeNode* root,int k)
{
    if(root == NULL)
    {
        return false;
    }

    int key = k - (root -> val);
    if(Find(key) == NULL)
    {   
        //如果哈希表中没有 k - x 的值 那么将 x 记录到哈希表中
        insert(root -> val);
    }
    else
    {
        return true;
    }
    return helper(root -> left,k) || helper(root -> right,k);
}

bool findTarget(struct TreeNode* root, int k)
{
    Clear();
   return helper(root,k);
}

21.二叉树中的第二小的节点

在这里插入图片描述
我这道题最直观的,最先想到的就是数组+排序

  • 遍历一遍二叉树,将其所有节点拷贝到数组中去
  • 然后对数组进行排序。
  • 最前面的就是最小的数么,
void Inorder(struct TreeNode* root, int* nums,int* size)
{
    if(root == NULL)
    {
        return;
    }
    Inorder(root -> left,nums,size);
    nums[(*size)++] = root -> val;
    Inorder(root -> right,nums,size);
}


int cmp_int(void* x,void* y)
{
    return *(int*)x - *(int*)y;
}

int findSecondMinimumValue(struct TreeNode* root)
{
    int* nums = (int*)malloc(sizeof(int) * 10000);
    int size = 0;
    Inorder(root,nums,&size);
    qsort(nums,size,sizeof(nums[0]),cmp_int);

    int i;
    for (i = 1; i < size; i++)
    {
        if(nums[i] != nums[i-1])
        {
            break;
        }
    }
    //提前出来
    if(i < size)
    {
        return nums[i];
    }
    else
    {
        return -1;
    }
}

还有一种方法,

  • 从题目中可以知道,相对于没棵子树,根节点永远是最小的。
  • 所以说,当你发现根节点,比当前的这个ans里面的数据都大,那么久没必要去遍历其下边的了。
void DFS(struct TreeNode* root,int* ans,int fristVal)
{
    if(root == NULL)
    {
        return;
    }

    //不等于 -1 就意味着找到了第二个了
    // 如果根节点都大于了ans 那就没有必要去遍历它一下的了
    if(*ans != -1 && root -> val >= *ans)
    {
        return;
    }

    //走到这里,就证明其根节点比当前的ans 里面的小,然后去和第一个最小节点的值去比较
    if(root -> val > fristVal)
    {
        *ans = root -> val;
    }

    DFS(root -> left,ans,fristVal);
    DFS(root -> right,ans,fristVal);


}


int findSecondMinimumValue(struct TreeNode* root)
{
    int ans = -1;
    int fristVal = root -> val; //最小值 第一个

    DFS(root,&ans,fristVal);

    return ans;
}

22. 二叉树中的搜索

在这里插入图片描述
他是一棵二叉搜索树,左子树的节点永远比根节点小,右子树的节点永远比根节点大
有这句话,就够了。

struct TreeNode* searchBST(struct TreeNode* root, int val)
{
    if(root == NULL)
    {
        return NULL;
    }

    if(val < root -> val)
    {
        return searchBST(root -> left, val);
    }
    else if(val > root -> val)
    {
        return searchBST(root -> right, val);
    }
    else
    {
        return root;
    }
}

23.数据流中的第K大元素(Tag)

在这里插入图片描述
我看完这道题有点像在我学习B树的时候,去构造B树的过程中,挨个有序的插入。
所以我先按照数组的方式去写了写,但是时间耗时非常大,因为在数组中去要想有序的插入数据,就需要大量的挪动数据,所以不是很好。

其实下面的FindsuitIndex 和 Insert 可以合并成一个函数的。。。。

#define MAX_SIZE 100000


typedef struct 
{
    int* keys;
    int size;
    int k;
} KthLargest;

//找到和合适的索引
int FindSuitIndex(KthLargest* obj,int val)
{
    int i;
    for (i = 0; i < obj -> size; i++)
    {
        if(val < obj -> keys[i])
        {
            break;
        }
    }

    return i;
}

//在该索引处插入
void Insert(KthLargest* obj, int val,int index)
{
    int i;
    //挪动数据
    for (i = obj -> size; i > index; i--)
    {
        obj -> keys[i] = obj -> keys[i-1];
    }

    obj -> keys[index] = val;
    obj -> size++;
}

KthLargest* kthLargestCreate(int k, int* nums, int numsSize)
{
    KthLargest* tmp = (KthLargest*)malloc(sizeof(KthLargest));
    tmp -> keys = (int*)malloc(sizeof(int) * MAX_SIZE);
    tmp -> size = 0;
    tmp -> k = k;
    int i;
    for (i = 0; i < numsSize; i++)
    {
        int index = FindSuitIndex(tmp,nums[i]);
        Insert(tmp,nums[i],index);
    }

    return tmp;
}

int kthLargestAdd(KthLargest* obj, int val)
{
    int index = FindSuitIndex(obj,val);
    Insert(obj,val,index);


    int k = obj -> k;
    int i = obj -> size - 1;
    while(i > 0 && k > 1)
    {
        k--;
        i--;
    }

    return obj -> keys[i];

}

void kthLargestFree(KthLargest* obj)
{
    free(obj -> keys);
    free(obj);    
}

完了我有个疑问:就下图中的第三大数,不应该是4吗? 为什么是5啊.
在这里插入图片描述
在这里插入图片描述

24. 叶子相似的树

在这里插入图片描述
这道题,第一时间想到的,最简单的方式还是将二叉树进行遍历,对其的叶子节点单独拿出来构造成一个数组,然后去比较两个数组就好了啊。

#define MAX_SIZE 10000


void CreateLeaf(struct TreeNode* root,int* nums,int* size)
{
    if(root == NULL)
    {
        return;
    }

    //叶子节点
    if(root -> left == NULL && root -> right == NULL)
    {
        nums[(*size)++] = root -> val;
    }

    CreateLeaf(root -> left,nums,size);
    CreateLeaf(root -> right,nums,size);
}


bool leafSimilar(struct TreeNode* root1, struct TreeNode* root2)
{
    int* leaf1 = (int*)malloc(sizeof(int) * MAX_SIZE);
    int size1 = 0;
    int* leaf2 = (int*)malloc(sizeof(int) * MAX_SIZE);
    int size2 = 0;

    CreateLeaf(root1,leaf1,&size1);
    CreateLeaf(root2,leaf2,&size2);

    //叶子数量不同不行
    if(size1 != size2)
    {
        return false;
    }

    //比较两个数组是否相同
    for (int i = 0; i < size1; i++)
    {
        if(leaf1[i] != leaf2[i])
        {
            return false;
        }
    }

    return true;
}


25.递增顺序搜索树

在这里插入图片描述
他是一个二叉搜索数,所以,我们还是可以中中序遍历后,得到数组,然后构造二叉树,不过这次构造的二叉树是左子树全为空的。

#define MAX_SIZE 10000

//获取升序数组
void GetNums(struct TreeNode* root, int* nums, int* size)
{
    if(root == NULL)
    {
        return;
    }
    GetNums(root -> left,nums,size);
    nums[(*size)++] = root -> val;
    GetNums(root -> right,nums,size);
}
//利用所给的数组构造二叉树
void CreateBiTree(struct TreeNode** root,int* nums, int* index,int size)
{
    //构造完毕时候,赋值为NULL
    if(*index >= size)
    {
        *root = NULL;
        return;
    }

    (*root) = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    (*root) -> val = nums[(*index)++];
    (*root) -> left = NULL;
    CreateBiTree(&((*root) -> right),nums,index,size);
}
 
struct TreeNode* increasingBST(struct TreeNode* root)
{
    int* nums = (int*)malloc(sizeof(int) * MAX_SIZE);
    int size = 0;
    int index = 0;

    GetNums(root,nums,&size);

    struct TreeNode* T = NULL;
    CreateBiTree(&T,nums,&index,size);

    return T;
}

上面这种方法中去遍历了一次二叉树,而在构造的时候又遍历了一遍,还额外的用了数组和树的空间。
还有一种办法可以中序遍历一遍二叉树就能得出答案,并且还不用额外的数组,直接在原树上进行修改。

  • 需要一个头节点

  • 在这里插入图片描述

  • 我们还是对二叉树进行中序遍历,当root走到1节点时候

  • 将curnode->right = root 并且 将root的左孩子改为NULL,尽管它本来就是空的。

  • 然后就像遍历链表一样,将 curnode = root;
    在这里插入图片描述
    而走到2节点的时候也是这样的,就会把之前的关系切断。
    在这里插入图片描述

void DFS(struct TreeNode* root,struct TreeNode** curNode)
{
    if(root == NULL)
    {
        return;
    }

    DFS(root->left,curNode);

    (*curNode) -> right = root;
    root -> left = NULL;

    //
    *curNode = root;

    DFS(root->right,curNode);
}
 


struct TreeNode* increasingBST(struct TreeNode* root)
{
    //头节点
    struct TreeNode* head = (struct TreeNode*)malloc(sizeof(struct TreeNode));
    head -> right = head -> left = NULL;

    //利用cur去在递归函数中遍历
    struct TreeNode* curNode = head;
    DFS(root,&curNode);

    //使最后还能直接返回
    return head -> right;
}

26.二叉搜索树的范围和

在这里插入图片描述

  • 对该二叉搜索树进行一个中序遍历得到一个升序数组
  • 在升序数组中选出符合[low,high]区间内的数加在一起。
void Inorder(struct TreeNode* root,int* nums, int* size)
{
    if(root == NULL)
    {
        return;
    }

    DFS(root -> left, nums, size);
    nums[(*size)++] = root -> val;
    DFS(root -> right, nums, size);
}

int rangeSumBST(struct TreeNode* root, int low, int high)
{
    int* nums = (int*)malloc(sizeof(int) * 100000);
    int size = 0;
    Inorder(root,nums,&size);

    int i;
    int sum = 0;
    for (i = 0; i < size && nums[i] <= high; i++)
    {
        if(nums[i] >= low)
        {
            sum += nums[i];
        }
    }

    return sum;
}


也可以不用数组,直接在遍历的时候对其进行计算

void Inorder(struct TreeNode* root,int low, int high, int* sum)
{   
    if(root == NULL)
    {
        return;
    }

    Inorder(root -> left, low, high, sum);

    //在区间内
    if(root -> val >= low && root -> val <= high)
    {
        *sum += root -> val;
    }
    //该节点如果都比high 大 就没有必要了,因为以后的都比该节点大
    if(root -> val < high)
    {
        Inorder(root -> right, low, high, sum);
    }
}

int rangeSumBST(struct TreeNode* root, int low, int high)
{
    int sum = 0;
    Inorder(root,low,high,&sum);

    return sum;
}


而下面这种方式就在时间上提升了不少,

int rangeSumBST(struct TreeNode* root, int low, int high)
{
    if(root == NULL)
    {
        return 0;
    }

    //当前节点上的值小于low 返回右子树上的值
    if(root -> val < low)
    {
        return rangeSumBST(root -> right,low,high);
    }

    //当前节点上的值大于high 返回左子树上的值
    if(root -> val > high)
    {
        return rangeSumBST(root -> left,low,high);
    }

    //当前节点上的值区间内,返回左右子树和当前节点的值
    return root -> val + rangeSumBST(root -> left,low,high) + rangeSumBST(root -> right,low,high);
}

27.单值二叉树

在这里插入图片描述
嗯。。。。。

bool Helper(struct TreeNode* root,int val)
{
    if(root == NULL)
    {
        return true;
    }

    return Helper(root -> left,val) && root -> val == val && Helper(root -> right,val);
}

bool isUnivalTree(struct TreeNode* root)
{
    int val = root -> val;
    
    return Helper(root,val);
}

28. 二叉树的堂兄弟

在这里插入图片描述

题目中所涉及到是否在一层,所以我这边就用广度优先搜索的方式了(BFS),这道题的关键点在于,如果在一层,还得去判断它的父亲节点是否是同一个的。

  • 对于层序遍历二叉树,还是老套路,创建一个队列,然后将根节点入队列。
  • 在出队列的时候,会将其孩子入队列,然后去判断x 和 y 是否都在同一个根节点下入队列。
  • 如果在同一个根节点下入队列,那么肯定是一个父亲,如果不是,那么直接返回true就好了
  • 可能有点绕,我就先这样表达吧。

看代码理解可能好点,和普通的层序遍历只是多了一个记录出现次数的变量,旁边都有注释的

bool isCousins(struct TreeNode* root, int x, int y)
{
    int map[103] = {0};
    map[x] = 1;
    map[y] = 1;

    //队列,层序遍历
    struct TreeNode** queue = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 101);
    int rear = 0,front = 0;

    queue[rear++] = root;

    while(front != rear)
    {
        int number = rear - front;  //记录当前层有多少个元素
        int count = 0;			//记录当前层是否出现 x 和 y
        //每一个根节点出队列
        while(number > 0)
        {
            int tmpCount = 0;	//记录当前节点是否同时出现了 x 和 y
            //出队列
            root = queue[front++];
            //入孩子队列
            if(root -> left != NULL)
            {
                queue[rear++] = root -> left;
                if(map[root -> left -> val] == 1)
                {
                    count++;
                    tmpCount++;
                }
            }
            if(root -> right != NULL)
            {
                queue[rear++] = root -> right;
                if(map[root -> right -> val] == 1)
                {
                    count++;
                    tmpCount++;
                }
            }
            //说明两个孩子的父亲节点都一致
            if(tmpCount == 2)
            {
                return false;
            }
            number--;
        }    

        //那两个孩子在一层,但是不是一个父亲
        if(count == 2)
        {
            return true;
        }
    }

    return false;
}

29.计算布尔二叉树的值

在这里插入图片描述
注意题目中是说完整二叉树,也就是完全二叉树,所以它不会出现只有右孩子没有左孩子的情况。
当我们发现此节点是叶子节点时候,返回当前前节点的值。

bool evaluateTree(struct TreeNode* root)
{
    //此节点是叶子节点,那么值就是它自身
    if(root -> left == NULL)
    {
        return root -> val;
    }

    if(root -> val == 2)
    {
        return evaluateTree(root -> left) || evaluateTree(root -> right);
    }
    else
    {
        return evaluateTree(root -> left) && evaluateTree(root -> right);
    }

}

30.开幕式焰火

在这里插入图片描述
这道题呢,遍历二叉树的时候,然后将出现的数字记录在哈希表中。
然后再去查表即可。

void DFS(struct TreeNode* root,int* map)
{
    if(root == NULL)
    {
        return;
    }

    DFS(root -> left, map);
    map[root -> val]++;
    DFS(root -> right,map);
}


int numColor(struct TreeNode* root)
{
    int map[1001] = {0};
    DFS(root,map);  

    int count = 0;
    for (int i = 0; i < 1001; i++)
    {
        if(map[i] != 0)
        {
            count++;
        }
    }

    return count;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值