【初阶】链式二叉树

        普通的二叉树增删改查没有什么意义,如果是存储数据则不如使用顺序表、链表。学习二叉树的初级操作,是为了学习后面更复杂的二叉树打基础(搜索二叉树、AVL树、红黑树、B树...)。

        这里大多使用的是递归法,分而治之,还有非递归的方法,但是难度偏大。


一、二叉树的遍历

PS:这里使用'#'号表示空。

前序遍历: 根  左子树  右子树

void BinaryTreePrevOrder(BTNode* root)
{
    if (root == NULL) {
        printf("# ");
        return;
    }
       
    printf("%c ",root->_data);
    BinaryTreePrevOrder(root->_left);
    BinaryTreePrevOrder(root->_right);
    return;
}

中序遍历: 左子树  根  右子树

扫描二维码关注公众号,回复: 14710728 查看本文章
void BinaryTreeInOrder(BTNode* root)
{
    if (root == NULL) {
        printf("# ");
        return;
    }
    BinaryTreeInOrder(root->_left);
    printf("%c ", root->_data);
    BinaryTreeInOrder(root->_right);

    return;
}

后序遍历: 左子树  右子树  根


void BinaryTreePostOrder(BTNode* root)
{
    if (root == NULL) {
        printf("# ");
        return;
    }
    BinaryTreePostOrder(root->_left);
    BinaryTreePostOrder(root->_right);
    printf("%c ",root->_data);

    return;
}

二、链式二叉树的创建

typedef char BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType _data;
	struct BinaryTreeNode* _left;
	struct BinaryTreeNode* _right;
}BTNode;

Q : 这里已知二叉树的前序遍历结果"ABD##E#H##CF##G##" ,请构建出二叉树。
 

BTNode* BinaryTreeCreate(BTDataType* a,int* pi)
{
     //不能在 if 里面++,不然每判断一次都要加一次。
    if (a[*pi]=='#') {
        (*pi)++;
        return NULL;
    }

    BinaryTreeNode* root = (BinaryTreeNode*)malloc(sizeof(BinaryTreeNode));

    root->_data = a[(*pi)++];

    root->_left = BinaryTreeCreate(a,pi);

    root->_right = BinaryTreeCreate(a,pi);

    return root;
}

测试: 

        使用前序遍历查看一下。

PS: 这里变量 i  传参数要传入地址进去,如果只传入变量,其只是相当于其变量的拷贝。从父节点传入左右节点时,i 的值相等。会导致在同一个下标下赋值两个数据,导致错误。所以要传入地址,让 i 的值执行一次变一次

三、二叉树初级操作

1.求二叉树节点个数

int BinaryTreeSize(BTNode* root)
{
    return root == NULL ? 0 : BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

2.求二叉树叶子节点个数

int BinaryTreeLeafSize(BTNode* root)
{
    if (root==NULL) {
        return 0;
    }

    if (root->_left == NULL && root->_right == NULL) {
        return 1;
    }

    return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}

3.求二叉树的深度

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

    int left = Depth(root->left);
    int right = Depth(root->right);

    return left > right ?left+1 :right+1;
}

4.求二叉树第k层节点个数

int BinaryTreeLevelKSize(BTNode* root, int k)
{
    assert(k >= 1);
    if (root == NULL)
        return 0;
    
    if (k == 1)
        return 1;

    return BinaryTreeLevelKSize(root->_left,k-1)+ BinaryTreeLevelKSize(root->_right,k-1);
}

        求K层的节点数,进入到左右子树后,就是求K-1层的节点数,K==1就是其到了指定的层数。

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

BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
    if (root == NULL)
        return NULL;
    //使用前序最好,避免第一个节点就是要找的节点
    if (root->_data == x)
        return root;

    BTNode* ret1 = BinaryTreeFind(root->_left, x);
    if (ret1)
        return ret1;

    BTNode* ret2 = BinaryTreeFind(root->_right, x);
    if (ret2)
        return ret2;

    return NULL;
}

        这要返回节点的时候,用变量来接受了一下,因为这里还要判断一次,如果不为空,要返回给上个节点。如果使用下面这种写法,返回的时候会再递归一次,浪费效率。

 //浪费效率
 if (BinaryTreeFind(root->_left, x))
     return BinaryTreeFind(root->_left, x);

6.二叉树的销毁

void BinaryTreeDestory(BTNode* root)
{
    if (root==NULL)
    {
        return;
    }

    BinaryTreeDestory(root->_left);
    BinaryTreeDestory(root->_right);
    free(root);
}

         释放节点要使用后序遍历的方式,因为前面还需要要释放的节点来找后面的节点,释放要节点要从最后面的节点开始释放。

7.层序遍历

        层序遍历的意思是,一层一层遍历,一层遍历完后,再遍历二层。

        这里要使用前面的队列的数据结构来实现。

        其思路是,把二叉树根节点放入队列,然后访问队头,让其子节点带入队列。

        这时A出队列,B变为队头,再把B的子节点带入队列。        

        这时B出队列,C变为队头,再把C的子节点带入队列。

 

        这时C出队列,D变为队头,再把D的子节点带入队列。(这里D的子节点为空)依次按照这个方式到最后。

        就会发现出队列的顺序就是二叉树的层序遍历。

代码:

PS:因为其要把节点的子节点带进去,所以要把二叉树的节点存入队列中去。

//存二叉树的节点数据
typedef struct BinaryTreeNode* Qdatatype;

struct QueNode
{
	Qdatatype data;
	struct QueNode* next;
};
void BinaryTreeLevelOrder(BTNode* root)
{

    Queue q;
    QueueInit(&q);
    if (QueueIsEmpty(&q)) {
        QueuePush(&q,root);
    }

    while (!QueueIsEmpty(&q)) {
        BTNode* front = QueueTop(&q);
        QueuePop(&q);

        printf("%c ", front->_data);

        if (front->_left) {
            QueuePush(&q, front->_left);
        }

        if(front->_right){
            QueuePush(&q, front->_right);
        }
        
    }
    QueueDestroy(&q);
}

测试:

8.判断二叉树是否是完全二叉树

        这里要使用前面层序遍历的思路来判断,这里要把空节点一起入队,然后判断空节点是否连续,如果全部都是空节点,那么就是完全二叉树,如果中间有还有节点,那么就不是完全二叉树。

        

bool BinaryTreeComplete(BTNode* root)
{
    Queue q;
    QueueInit(&q);
    if (QueueIsEmpty(&q)) {
        QueuePush(&q, root);
    }

    while (!QueueIsEmpty(&q)) {
        BTNode* front = QueueTop(&q);
        QueuePop(&q);
        //这里遇到空指针,那么说明到最后一层了,节点都被带到队列中去了
        if (front) {
            QueuePush(&q, front->_left);
            QueuePush(&q, front->_right);
        }
        else {
            break;
        }
    }

    while (!QueueIsEmpty(&q))
    {
        BTNode* front = QueueTop(&q);
        QueuePop(&q);

        if (front) {
            QueueDestroy(&q); //注意这里退出的时候要释放内存。
            return false;
        }
        
    }

    QueueDestroy(&q);
    return true;
}

测试:

四、初阶OJ题

1.判断是否是单值二叉树

        如果二叉树每个节点都具有相同的值,那么该二叉树就是单值二叉树。只有给定的树是单值二叉树时,才返回 true;否则返回 false

 

        拿一个基准值来判断是否是否相等,如果每个节点都相等,那么就是单值二叉树。否则不是。

代码: 

bool flag=true;

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

    if(root->val!=val)
        flag=false;

    isSameNode(root->left,val);
    isSameNode(root->right,val);
    return true;
}

bool isUnivalTree(struct TreeNode* root){
    if(root==NULL)
        return true;

    flag=true;
    isSameNode(root,root->val);
    return flag;
}

2.检查两颗树是否相同

给你两棵二叉树的根节点 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);
}

3.判断另一个树,是自己的子树

        给你两棵二叉树 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 false;

    if(isSameTree(root,subRoot))
        return true;

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

4.判断是否是对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。 

        分解判断子树是否相等就可以,但是要注意,判断的方向,并不是单一的方向进行判断。

代码:

bool isSameTree(struct TreeNode* q,struct TreeNode* p)
{
     if(p==NULL && q==NULL)
        return true;

    if(p==NULL || q==NULL)
        return false;
    
    if(p->val!=q->val)
        return false;

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

}

bool isSymmetric(struct TreeNode* root){
    if(root==NULL)
        return true;

    return isSameSubTree(root->left,root->right);
}


总结:

        会发现使用递归法,代码其实都比较简单,只不过思路要明确,其主要思想还是分而治之。

猜你喜欢

转载自blog.csdn.net/weixin_45423515/article/details/125029875