什么是链式结构?
二叉树我之前阐述过了,有两种储存方式,具体的可以在我这篇博客了解:二叉树入门详解 链式结构中一个节点内有数值和左孩子以及右孩子的指针,我目前创建二叉树还是直接手动创建的,这篇博客主要讲的是我对二叉树的的具体操作! 我将构造这个二叉树,下面除oj题外所有操作都是基于我构造的这个二叉树的
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType val;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTN;
BTN* BuyBinaryTreeNode(BTDataType x)
{
BTN* tam = (BTN*)malloc(sizeof(BTN));
if (tam == NULL)
{
printf("malloc fail\n");
exit(-1);
}
tam->val = x;
tam->left = NULL;
tam->right = NULL;
}
BTN* CreatBinaryTree()
{
BTN* node1 = BuyBinaryTreeNode(1);
BTN* node2 = BuyBinaryTreeNode(2);
BTN* node3 = BuyBinaryTreeNode(3);
BTN* node4 = BuyBinaryTreeNode(4);
BTN* node5 = BuyBinaryTreeNode(5);
BTN* node6 = BuyBinaryTreeNode(6);
BTN* node7 = BuyBinaryTreeNode(7);
node1->left = node2;
node1->right = node3;
node2->left = node4;
node2->right = node5;
node3->left = node6;
node3->right = node7;
return node1;
}
int main()
{
BTN* tree = CreatBinaryTree();
return 0;
}
二叉树操作的思路
二叉树的结构我们思考一下,大致是一个根节点然后带两个子树,子树又可以分为一个根节点加两个子树(子树有可能是空节点) 根据结构我们可以发现解决有关二叉树的问题时,我们可以用递归来解决,我们都可以从:根节点+左子树+右子树出发,一直递归,并且递归的终止条件就是遇到空节点
二叉树的前/后/中序遍历
相同点:都是把整个二叉树遍历一遍 不同点:遍历的顺序不同
前序:根节点+左子树+右子树 中序:左子树+根节点+右子树 后序:左子树+右子树+根节点 注意前面三个遍历指的是每个结点都是这样的 层序:设根节点为第一层,然后从上到下访问每一层的所有结点。 注:我们下面的操作都是以上面直接构造的二叉树为例的
我在这边画一下谦虚遍历的情况,中序以及后序遍历思想都是一样的 下面是前序遍历的数字顺序:
要点:我们将每个结点都看作是一个根节点,然后根据前序遍历的规则:跟+左+右遍历整个二叉树
void PrevOrder(BTN* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->val);
PrevOrder(root->left);
PrevOrder(root->right);
}
void InOrder(BTN* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->val);
InOrder(root->right);
}
void PostOrder(BTN* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->val);
}
前面画了一张前序数字的遍历图,下面画一张中序遍历的函数栈帧! **注:**由于是递归,我们先判断递归的结束条件,防止栈溢出无限递归的出现。
二叉树的层序遍历
层序:设根节点为第一层,然后从上到下访问每一层的所有结点。 思路: 层序遍历不能用递归的思想,而是用得借用队列先进先出的性质。 1,头结点入队 2.上一个结点入队的时候,下一结点出队 3,注意:入队的时候必须是非空 注意:因为用的是C语言,所以我们还要自己构造队列 其实跟我们之前写的队列差不多,只需要改这个tyepedef struct BinaryTreeNode*QDataType表示此时队列的元素是指针
Queue.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef struct BinaryTreeNode* QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* front;
QueueNode* rear;
}Queue;
void QueueInit(Queue* ps);
void QueueDestory(Queue* ps);
void QueuePush(Queue* ps, QDataType x);
void QueuePop(Queue* ps);
bool QueueEmpty(Queue* ps);
size_t QueueSize(Queue* ps);
QDataType QueueFront(Queue* ps);
QDataType QueueRear(Queue* ps);
Queue.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"QUeue.h"
void QueueInit(Queue* ps)
{
assert(ps);
ps->front = ps->rear = NULL;
}
void QueueDestory(Queue* ps)
{
assert(ps);
QueueNode* cur = ps->front;
while (cur)
{
QueueNode* ret = cur->next;
free(cur);
cur = ret;
}
ps->front = ps->rear = NULL;
}
void QueuePush(Queue* ps, QDataType x)
{
assert(ps);
QueueNode* cur = (QueueNode*)malloc(sizeof(QueueNode));
if (cur == NULL)
{
printf("malloc fail\n");
exit(-1);
}
cur->data = x;
cur->next = NULL;
if (ps->rear == NULL)
{
assert(ps->front == NULL);
ps->front = ps->rear = cur;
}
else
{
ps->rear->next = cur;
ps->rear = cur;
}
}
void QueuePop(Queue* ps)
{
assert(ps);
assert(ps->front != NULL);
if (ps->front == ps->rear)
ps->front = ps->rear = NULL;
else
{
QueueNode* ret = ps->front->next;
free(ps->front);
ps->front = ret;
}
}
bool QueueEmpty(Queue* ps)
{
assert(ps);
return ps->front == NULL;
}
size_t QueueSize(Queue* ps)
{
assert(ps);
int sum = 0;
QueueNode* cur = ps->front;
while (cur)
{
sum++;
cur = cur->next;
}
return sum;
}
QDataType QueueFront(Queue* ps)
{
assert(ps);
assert(ps->front != NULL);
return ps->front->data;
}
QDataType QueueRear(Queue* ps)
{
assert(ps);
assert(ps->rear != NULL);
return ps->rear->data;
}
层序遍历:
void LevelOrder(BTN* root)
{
Queue st;
QueueInit(&st);
QueuePush(&st, root);
while (!QueueEmpty(&st))
{
BTN* cur = QueueFront(&st);
QueuePop(&st);
if (cur == NULL)
break;
printf("%d ", cur->val);
if (cur->left != NULL)
QueuePush(&st, cur->left);
if (cur->right != NULL)
QueuePush(&st, cur->right);
}
}
二叉树的节点个数
思路: 节点个数 = 根节点+左子树节点个数+右子树节点个数
int BinaryTreeSize(BTN* root)
{
if (root == NULL)
return 0;
return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
二叉树叶子节点的个数
思路:返回左子树叶子节点以及右子树叶子节点
int BinaryTreeLeafSize(BTN* root)
{
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
二叉树第k层节点的个数
思路: 查找第k层的节点个数 假设k = 1层,直接返回第一层的节点个数 k = 2时,返回的是以第二层的节点为根节点的第一层的节点个数 (这个思路可能没解释清楚) 结合下面的代码思考一下,当k = 2的时候,返回的不就是第一个节点的左右子树的结点数嘛? 注:我们的参数就是一个指针,只能返回1,但是最终的返回值是左右两边的和
意思是两个参数,当k的那个参数为1的时候,返回该层的个数即可
int BinaryTreeLevelKSize(BTN* root, int k)
{
if (root == NULL)
return 0;
if(k==1)
return 1;
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
查找二叉树中值为X的节点
思路: 查询根节点,若根节点不符合,查询左子树,查询右子树 递归即可!
BTN* BinaryTreeFind(BTN* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->val == x)
return root;
BTN* tal = BinaryTreeFind(root->left, x);
if (tal)
return tal;
return BinaryTreeFind(root->right, x);
}
判断二叉树是否是完全二叉树
思路:其实和层序遍历类似,也是要先写一个队列结构,然后上一个元素出队列的时候,其子节点入队,然后当出队的元素是NULL时,跳出循环,然后遍历剩余队列,如果队列后面还有数字,那么就不是完全二叉树 **注意:此时不上一个节点只要出队了,其子节点就要入队,不用管其子节点是否为空节点
bool BinaryTreeComplete(BTN* root)
{
if (root == NULL)
return false;
Queue st;
QueueInit(&st);
QueuePush(&st, root);
while (!QueueEmpty(&st))
{
BTN* cur = QueueFront(&st);
QueuePop(&st);
if (cur == NULL)
break;
QueuePush(&st, cur->left);
QueuePush(&st, cur->right);
}
while (!QueueEmpty(&st))
{
BTN* cur = QueueFront(&st);
QueuePop(&st);
if (cur != NULL)
return false;
}
return true;
}
二叉树的高度
思路: 二叉树的高度 = max(左子树的高度,右子树的高度)+1(根节点)
int BrnaryTreeHigh(BTN* root)
{
if (root == NULL)
return 0;
return max(1 + BrnaryTreeHigh(root->left), 1 + BrnaryTreeHigh(root->right));
}
二叉树的销毁
思路:我们直到,二叉树的构建是从上到下的,但是二叉树的销毁不能也是这样,因为如果我们先销毁根节点的话,那么我们的子节点就找不到了,所以我们应该先销毁左子树,再销毁右子树,最后销毁根节点的顺序来实施
oj
单值二叉树
思路:只要父节点的值不等于孩子的值就返回false
bool isUnivalTree(struct TreeNode* root){
if(root==NULL)
return true;
if(root->left&&root->val!=root->left->val)
return false;
if(root->right&&root->val!=root->right->val)
return false;
return isUnivalTree(root->left)&&isUnivalTree(root->right);
}
相同的树 思路直接写在代码里
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);
}
另一棵树的子树 思路: 依次遍历树的每一个节点,然后以该节点为根节点,跟另一棵树比较是否相等,就是套用一下上一题的代码,easy
bool checksame(struct TreeNode*p1,struct TreeNode*p2)
{
if(p1==NULL&&p2==NULL)
return true;
if(p1==NULL||p2==NULL)
return false;
if(p1->val!=p2->val)
return false;
return checksame(p1->left,p2->left)
&&checksame(p1->right,p2->right);
}
bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){
if(root==NULL)
return false;
if(checksame(root,subRoot))
return true;
return isSubtree(root->left,subRoot)||isSubtree(root->right,subRoot);
}
二叉树遍历 思路: 还是递归: 先判断该节点是否为空,如果不为空,赋值,然后判断左右子树
#include<stdio.h>
#include<stdlib.h>
typedef char BTDataType;
typedef struct TreeNode {
BTDataType val;
struct TreeNode* left;
struct TreeNode* right;
}BTN;
BTN* CreatBinaryTree(BTDataType *a,int*pi)
{
if(a[*pi]=='#')
{
(*pi)++;
return NULL;
}
BTN*tam = (BTN*)malloc(sizeof(BTN));
if(tam==NULL)
{
printf("malloc fail\n");
exit(-1);
}
tam->val = a[(*pi)++];
tam->left = CreatBinaryTree(a,pi);
tam->right = CreatBinaryTree(a,pi);
return tam;
}
void InOrder(BTN*root)
{
if(root==NULL)
return ;
InOrder(root->left);
printf("%c ",root->val);
InOrder(root->right);
}
int main()
{
char arr[100] = {0};
scanf("%s",arr);
int i = 0;
BTN*tree = CreatBinaryTree(arr,&i);
InOrder(tree);
return 0;
}
欢迎三连!!!多谢铁子支持!!!
|