【C习题】二叉树经典习题

前言

  • 二叉树具有递归的性质
  • 因此我们做题时要往递归的大方向进行思考。

一.遍历

1.前序遍历

力扣链接

  • 先遍历根,再遍历左子树,最后遍历右子树
  • 此题关键:传入i的地址,而不是i的临时拷贝
  • 原因:每一层的 i 都要建立栈帧,因此递归之后的i值,每一层都是独立的i,不会互相影响
//求二叉树的size相当于根节点+左子树的节点总数+右子树的节点总数
int BTSize(struct TreeNode* root)
{
    
    
    if (root == NULL)
    {
    
    
        return 0;
    }
    return BTSize(root->right) + BTSize(root->left) + 1;
}
void BTPreOrder(struct TreeNode*root,int*arr,int*pi)
{
    
    
    if (root == NULL)
    {
    
    

        return;
    }
    arr[*pi]=root->val;
    (*pi)++;//*与++的优先级相同,但是运算顺序是从右向左的!
    BTPreOrder(root->left,arr,pi);
    BTPreOrder(root->right,arr,pi);

}
int* preorderTraversal(struct TreeNode* root, int* returnSize)
{
    
    
    *returnSize =BTSize(root);

    int *arr=(int*)malloc(sizeof(int)*(*returnSize));


    if(arr==NULL)
    {
    
    
        perror("malloc fail");
        exit(-1);
    }
    int i = 0;
    BTPreOrder(root,arr,&i);
    return arr;
}

2.中序遍历

力扣链接

  • 先遍历左子树,再遍历跟,最后遍历右子树
  • 关键:同上

int BTSize(struct TreeNode* root)
{
    
    
    if (root == NULL)
    {
    
    
        return 0;
    }
    return BTSize(root->right) + BTSize(root->left) + 1;
}
void BTInOrder(struct TreeNode* root,int* i,int *arr)
{
    
    
    if (root == NULL)
    {
    
    
        return;
    }
    BTInOrder(root->left,i,arr);
    arr[*i] = root->val;
    (*i)++;
    BTInOrder(root->right,i,arr);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize)
{
    
    
    *returnSize = BTSize(root);
    int *arr=(int *)malloc(sizeof(int)*(*returnSize));
    int i = 0;
    BTInOrder(root,&i,arr);
    return arr;
}

3.后序遍历

力扣链接

  • 先遍历左子树,再遍历右子树,最后再遍历根
  • 关键:同上
int BTSize(struct TreeNode* root)
{
    
    
    if (root == NULL)
    {
    
    
        return 0;
    }
    return BTSize(root->right) + BTSize(root->left) + 1;
}
void BTPostOrder(struct TreeNode* root,int*i,int* arr)
{
    
    
    if (root == NULL)
    {
    
    
        return;
    }

    BTPostOrder(root->left,i,arr);
    BTPostOrder(root->right,i,arr);
    arr[*i]=root->val;
    (*i)++;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize)
{
    
    
    *returnSize=BTSize(root);
    int *arr=(int*)malloc((*returnSize)*sizeof(int));
    if(arr==NULL)
    {
    
    
        perror("malloc fail");
        exit(-1);
    }
    int i = 0;
    BTPostOrder(root,&i,arr);
    return arr;
}

4.层序遍历

  • 借助对列先进先出的结构
  • 出根节点将其左孩子和右孩子入栈
  • 直到对列为空结束
typedef  struct BTNode* QLNDateType;//typedef不能对已封装的类型再次封装
//节点
typedef struct QListNode
{
    
    
	QLNDateType val;
	struct QListNode* next;
}QListNode;

typedef struct Queue
{
    
    
	QListNode* Top;
	QListNode* Tail;
}Queue;
void QueuePushBack(Queue* q,QLNDateType x)
{
    
    
	QListNode* NewNode = (QListNode*)malloc(sizeof(QListNode));
	if (NewNode == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	NewNode->next = NULL;
	NewNode->val = x;
	if (q->Top == NULL)
	{
    
    
		q->Tail = NewNode;
		q->Top = NewNode;
		return;
	}
	else
	{
    
    
		q->Tail->next = NewNode;
		q->Tail = NewNode;
	}
}
int QueueEmpty(Queue q)
{
    
    
	return q.Top == NULL;
}
void QueueDestory(Queue* q)
{
    
    
	QListNode* cur = q->Top;
	q->Top = NULL;
	q->Tail = NULL;
	while (cur)
	{
    
    
		QListNode* next = cur->next;
		free(cur);
		cur = next;
	}
}
//到这才是关键
void BTLevelOrder(BTNode* root)
{
    
    
    Queue qlist;
    QueueInit(&qlist);
    if (root != NULL)
    {
    
    
        QueuePushBack(&qlist, root);
    }
    while (!QueueEmpty(qlist))
    {
    
    
        BTNode* front = QueueTop(qlist);
        QueuePopFront(&qlist);
        if (front != NULL)
        {
    
    
            printf("%d ", front->val);
            QueuePushBack(&qlist, front->left);
            QueuePushBack(&qlist, front->right);
        }
    }
    QueueDestory(&qlist);
}

二.提高

1.单值二叉树

力扣链接

  • 比较根节点与左右节点
  • 注意:我们要的是结果!
  • 因此:当右节点的值不等于根节点的值时,就返回false!左节点同理!
  • 如果节点为空说明:左右子树与根节点相同,或者只有一个节点(题目条件)。
bool isUnivalTree(struct TreeNode* root)
{
    
    
    if(root==NULL)
    {
    
    
        return true;
    }
    if(root->left!=NULL&&root->val!=root->left->val)
    {
    
    
        return false;
    }
    if(root->right!=NULL&&root->val!=root->right->val)
    {
    
    
        return false;
    }
    return isUnivalTree(root->left)&&isUnivalTree(root->right);
}

2.二叉树翻转

力扣链接

  • 递归思路:翻转一棵树相当于翻转其左右子树
struct TreeNode* invertTree(struct TreeNode* root)
{
    
    
    if(root==NULL)
    {
    
    
        return NULL;
    }
    invertTree(root->left);
    invertTree(root->right);
    struct TreeNode*tmp = root->right;
    root->right = root->left;
    root->left = tmp;
    return root;
}

3.判断二叉树树相同

力扣链接

  • 判断二叉树是否相同需看左子树和右子树是否相同
  • 注意:我们要的是结果!
  • 因此:当左右节点的值不相同时,我们返回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);
}

4.判断二叉树对称

力扣链接

  • 思路:二叉树对称只需判断一棵树的左子树与另一棵的右子树是否相同与一棵树的右子树与左子树是否相同
  • 注意:我们要的是结果!
  • 因此:当左右节点的值不相同时,我们返回false即可
 bool is_same_tree(struct TreeNode* root1,struct TreeNode* root2)
{
    
    
	if (root1 == NULL && root2 == NULL)
	{
    
    
		return true;
	}
	if (root1 == NULL || root2 == NULL)
	{
    
    
		return false;
	}
	if (root1->val != root2->val)
	{
    
    
		return false;
	}
	return is_same_tree(root1->left, root2->right) &&
		   is_same_tree(root1->right, root2->left);
}
bool isSymmetric(struct TreeNode* root)
{
    
    
    return is_same_tree(root->left,root->right);
}

5.另一棵树的子树

力扣链接

  • 思路:先比较整棵树是否相同,再比较其左子树是否相同,最后比较右子树是否相同,如果相同返回true,如果***不相同返回false!*
bool is_same_tree(struct TreeNode* root1,struct TreeNode* root2)
{
    
    
	if (root1 == NULL && root2 == NULL)
	{
    
    
		return true;
	}
	if (root1 == NULL || root2 == NULL)
	{
    
    
		return false;
	}
	if (root1->val != root2->val)
	{
    
    
		return false;
	}
	return is_same_tree(root1->right, root2->right) && 
	       is_same_tree(root1->left, root2->left);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot)
{
    
    
    if(root==NULL)
    {
    
    
        return false;
    }
    bool ret = is_same_tree(root,subRoot);
    if(ret)
      return true;

    bool left = isSubtree(root->left,subRoot);
    if(left)
      return true;
    bool right = isSubtree(root->right,subRoot);
    if (right)
       return true;

       return false;
}

5.判断完全二叉树

  • 借助层序遍历的思路,当出到空时,如果后面有不为空的,则其不是完全二叉树,反之是完全二叉树。
typedef  struct BTNode* QLNDateType;//typedef不能对已封装的类型再次封装
//节点
typedef struct QListNode
{
    
    
	QLNDateType val;
	struct QListNode* next;
}QListNode;

typedef struct Queue
{
    
    
	QListNode* Top;
	QListNode* Tail;
}Queue;
void QueuePushBack(Queue* q,QLNDateType x)
{
    
    
	QListNode* NewNode = (QListNode*)malloc(sizeof(QListNode));
	if (NewNode == NULL)
	{
    
    
		perror("malloc fail");
		exit(-1);
	}
	NewNode->next = NULL;
	NewNode->val = x;
	if (q->Top == NULL)
	{
    
    
		q->Tail = NewNode;
		q->Top = NewNode;
		return;
	}
	else
	{
    
    
		q->Tail->next = NewNode;
		q->Tail = NewNode;
	}
}
int QueueEmpty(Queue q)
{
    
    
	return q.Top == NULL;
}
void QueueDestory(Queue* q)
{
    
    
	QListNode* cur = q->Top;
	q->Top = NULL;
	q->Tail = NULL;
	while (cur)
	{
    
    
		QListNode* next = cur->next;
		free(cur);
		cur = next;
	}
}
//关键代码
bool BinaryTreeComplete(BTNode* root)
{
    
    

    Queue qlist;
    QueueInit(&qlist);
    if (root != NULL)
    {
    
    
        QueuePushBack(&qlist, root);
    }
    while (!QueueEmpty(qlist))
    {
    
    
        BTNode* front = QueueTop(qlist);
        QueuePopFront(&qlist);
        if (front != NULL)
        {
    
    
            QueuePushBack(&qlist, front->left);
            QueuePushBack(&qlist, front->right);
        }
        else
        {
    
    
            break;
        }
    }
    while (!QueueEmpty(qlist))
    {
    
    
        BTNode* front = QueueTop(qlist);
        QueuePopFront(&qlist);
        if (front != NULL)
        {
    
    
            QueueDestory(&qlist);
            return false;
        }
    }
    QueueDestory(&qlist);
    return true;
}

6.二叉树的构建与遍历

牛客链接

  • 利用数组创建二叉树
  1. 在外面传入下标的指针
  2. 当识别到#时,二叉树的结点设置为空,但是数组还要往后识别,这时的下标要加1
  3. 不必考虑数组是否读完,因为数组的元素是足够刚好建立一棵二叉树的
  4. 根的左节点指针等于其子树创建的节点之后的返回值,右节点同理。
  5. 返回创建的根节点(也是4的返回值)
  • 中序遍历二叉树
    思路:见上文
#include <stdio.h>
#include<stdlib.h>
typedef char BTNodeDataType;
typedef struct BTNode
{
    
    
    BTNodeDataType val;
    struct BTNode* left;
    struct BTNode* right;
}BTNode;
BTNode* BTCreat(BTNodeDataType* a, int* pi)
{
    
    
    if (a[*pi] == '#')
    {
    
    
        (*pi)++;
        return NULL;
    }
    BTNode* NewNode = (BTNode*)malloc(sizeof(BTNode));
    if (NewNode == NULL)
    {
    
    
        perror("malloc fail");
        exit(-1);
    }
    NewNode->val = a[(*pi)++];
    NewNode->left = BTCreat(a, pi);
    NewNode->right = BTCreat(a,  pi);
    return NewNode;
}
void BTInOrder(BTNode* root)
{
    
    
    if (root == NULL)
    {
    
    
        return;
    }
    BTInOrder(root->left);
    printf("%c ", root->val);
    BTInOrder(root->right);
}
int main() 
{
    
    
    char arr[150]={
    
    0};
    scanf("%s",arr);
    int i = 0;
    BTNode*root = BTCreat(arr,&i);
    BTInOrder(root);
    return 0;
}

7.二叉树的最大深度

力扣链接

  • 最大深度:左子树的深度与右子树深度的较大值,再加上根节点的高度1.
  • 细节:左子树的深度与右子树深度的值应当保留下来,这样可以少递归很多次!
int maxDepth(struct TreeNode* root)
{
    
    
    if(root==NULL)
    {
    
    
        return 0;
    }
    int left = maxDepth(root->left);
    int right = maxDepth(root->right);
    return left > right? left+1:right+1;
}

猜你喜欢

转载自blog.csdn.net/Shun_Hua/article/details/129954071