数据结构中常用的二叉树算法汇总

本文详细介绍了二叉树的各种遍历方式,包括递归和非递归的前序、中序、后序遍历,以及层序遍历。此外,还探讨了二叉树的深度、直径、节点个数的计算方法,以及复制、比较、转换和对称性判断等操作。对于二叉搜索树,特别讲解了如何将其转化为排序的双向链表。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.二叉树的递归遍历

1.1前序递归遍历

void preorder(NODE *root)
{
    if(root != 0)
    {
        printf("%c ", root->data);
        preorder(root->lchild);
        preorder(root->rchild);
    }
}

1.2中序递归遍历

void midorder(NODE *root)
{
    if(root != 0)
    {
        midorder(root->lchild);
        printf("%c ", root->data);
        midorder(root->rchild);
    }
}

1.3后序递归遍历

void posorder(NODE *root)
{
    if(root != 0)
    {
        posorder(root->lchild);
        posorder(root->rchild);
        printf("%c ", root->data);
    }
}

2.二叉树的非递归遍历

2.1前序非递归遍历

void preorder(NODE *root)
{
    NODE *stack[MAXN];
    NODE *p = root;
    int top = 0;
    while(p != 0 || top > 0)
    {
        while(p != 0)
        {
            printf("%c ", p->data);
            stack[top++] = p;
            p = p->lchild;
        }
        if(top > 0)//栈非空
        {
            p = stack[--top];
            p = p->rchild;
        }
    }
}

2.2中序非递归遍历

void midorder(NODE *root)
{
    NODE *stack[MAXN];
    NODE *p = root;
    int top = 0;
    while(p != 0 || top > 0)
    {
        while(p != 0)
        {
            stack[top++] = p;
            p = p->lchild;
        }
        if(top > 0)//栈非空
        {
            p = stack[--top];
            printf("%c ", p->data);
            p = p->rchild;
        }
    }
}
//对比前序和中序非递归遍历代码,不难发现只是输出节点值得代码位置变了,其他代码都一样。

2.3后序非递归遍历

void posorder(NODE *root)//后序遍历非递归算法
{
    NODE *stack[MAXN];
    NODE *cur, *pre = 0;//cur为当前节点 pre为前一个访问的节点
    int top = 0;
    stack[top++] = root;//先将根节点入栈
    while(top > 0)//栈非空
    {
        cur = stack[top-1];//使cur指向栈顶节点
        if((cur->lchild == 0 && cur->rchild == 0) || (pre != 0 && (pre == cur->lchild || pre == cur->rchild)))
        {
            //如果当前结点没有孩子结点或者孩子节点都已被访问过
            printf("%c ", cur->data);
            top--;
            pre = cur;
        }
        else
        {
            if(cur->rchild != 0)
                stack[top++] = cur->rchild;
            if(cur->lchild != 0)
                stack[top++] = cur->lchild;
        }
    }
}

3.二叉树的层序遍历(即从上到下逐层遍历)

void levelorder(NODE *root)
{
    NODE* que[MAXN];
    NODE *p;
    int tail = 0, top = 0;
    que[tail++] = root;//根节点入队
    while(tail != top)
    {
        p = que[top++];//出队
        printf("%c ", p->data);
        if(p->lchild != 0)
            que[tail++] = p->lchild;
        if(p->rchild != 0)
            que[tail++] = p->rchild;
    }
}

4.二叉树深度

树的深度:从根节点到叶子结点一次经过的结点形成树的一条路径,最长路径的长度为树的深度,根节点的深度为1。

int tree_depth(NODE *root)
{
    if(root == 0)
        return 0;
    else
    {
        int depth, depth1=1, depth2=1;
        depth1 += tree_depth(root->lchild);
        depth2 += tree_depth(root->rchild);
        depth = depth1 > depth2 ? depth1:depth2;
        return depth;
    }
}

区分一下树的高度:从叶子节点到根结点一次经过的结点形成树的一条路径,最长路径的长度为树的高度,叶子节点的深度为1。虽然同一棵树的深度和高度相同,但是具体到树的某个节点,其深度和高度是不一样的 。


5.二叉树的直径

树的直径(有时称为宽度)是两个叶子节点之间最长路径上的节点数。


int diameterOpt(NODE *root, int* height)
{//第二个参数是存储树的高度 

  int lh = 0, rh = 0;//左右子树的高度
  int ldiameter = 0, rdiameter = 0;//左右子树的直径
  
  if(root == NULL)
  {
    *height = 0;
     return 0; //直径也为0
  }
  
  ldiameter = diameterOpt(root->left, &lh);
  rdiameter = diameterOpt(root->right, &rh);
  
  *height = max(lh, rh) + 1;
  
  return max(lh + rh + 1, max(ldiameter, rdiameter));
}

6.二叉树的节点个数

int count_node(NODE *root)
{
    if(root == 0)
        return 0;
    else
        return count_node(root->lchild) + count_node(root->rchild) + 1;
}

7.二叉树的复制

NODE* copy_B_tree(NODE *root)
{
    if(root == 0)
        return 0;
    NODE *p = 0;
    p = (NODE*)malloc(sizeof(NODE));
    p->data = root->data;
    p->lchild = copy_B_tree(root->lchild);
    p->rchild = copy_B_tree(root->rchild);
    return p;
}

8.判断两棵二叉树是否相等

int equal_B_tree(NODE *root1, NODE *root2)
{
    if(root1 == 0 && root2 == 0)
        return 1;
    if(root1 != 0 && root2 != 0)
        if(root1->data == root2->data)
            if(equal_B_tree(root1->lchild, root2->lchild) && equal_B_tree(root1->rchild, root2->rchild))
                return 1;
    return 0;
}

9.判断两棵二叉树是否是子树关系

bool HasSubtree(Node* root1, Node* root2)
{//判断root2是不是root1的一颗子树
    bool res = false;
    if(root1 != NULL && root2 != NULL) {
        if(root1->data == root2->data)
            res = IsSubtree(root1, root2);
        if(!res)
            res = HasSubtree(root1->lchild, root2);//判断是否是左子树的子树
        if(!res)
            res = HasSubtree(root1->rchild, root2);//判断是否是右子树的子树
    }
    return res;
}

bool IsSubtree(Node* root1, Node* root2) {
    if(root2 == NULL)
        return true;
    if(root1 == NULL)
        return false;
    if(root1->data != root2->data)
        return false;
    return IsSubtree(root1->lchild, root2->left) && IsSubtree(root1->rchild, root2->right);
}

10.交换二叉树的所有左右节点

void exchange_node(NODE *root)
{
    if(root != 0)
    {
        NODE *pl = 0, *pr = 0;
        pl = root->lchild;
        pr = root->rchild;
        root->lchild = pr;
        root->rchild = pl;
        exchange_node(root->lchild);
        exchange_node(root->rchild);
    }
}

11.已知二叉树的前序和中序遍历结果建树

NODE* build_Btree(char *pre, char *mid, int len)
//已知前序遍历pre和中序遍历mid
{
    NODE *root;
    root = (NODE*)malloc(sizeof(NODE));
    if(len == 0)
        return root = 0;//len==0 表示无子节点
    root->data = pre[0];
    int i=0;
    while(mid[i] != pre[0] && i < len)
        i++;//找出左节点个数
    root->lchild = build_Btree(pre+1, mid, i);//pre+1 跳过根节点
    root->rchild = build_tree(pre+i+1, mid+i+1, len-i-1);
    //pre+i+1 代表前序遍历右子树部分 mid+i+1代表中序遍历右子树部分
    return root;
}

12.已知二叉树的后序和中序遍历结果建树

NODE* build_Btree(char *mid, char *pos, int len)
//已知中序遍历mid和后序遍历pos
{
    NODE *root;
    root = (NODE*)malloc(sizeof(NODE));
    if(len == 0)
        return root = 0;//len==0 表示无子节点
    root->data = pos[len-1];
    int i = 0;
    while(mid[i] != pos[len-1] && i < len)
        i++;//找出左节点个数

    root->lchild = build_tree(mid, pos, i);
    root->rchild = build_tree(mid+i+1, pos+i, len-i-1);
    return root;
}

13. 将二叉搜索树转换为一个排序的双向链表

NODE *head = NULL;
NODE *tail = NULL;
NODE* Convert(NODE* root)
{//中序递归遍历
    if(root == NULL)
        return NULL;
    //处理左子树
    Convert(root->lchild);
    //将根节点插到链表尾部
    if(tail == NULL)
        head = tail = root;//中序遍历的第一个节点
    else
    {
        tail->rchild = root;
        root->lchild = tail;
        tail = root;
    }
    //处理右子树
    Convert(root->rchild);
    return head;
}

14.判断二叉树是否是平衡二叉树

//借助于求二叉树深度的函数(见4)
bool isbalance_tree(Node* root)
{
    if(root == NULL)
        return true;
    return abs(tree_depth(root->lchild) - tree_depth(root->rchild)) <= 1 && isbalance_tree(root->lchild) && isbalance_tree(root->rchild);     
}

15.判断一棵树是否是对称二叉树

//对称二叉树指镜像二叉树
bool isSymmetrical(Node* root)
{
    if(root == NULL)
        return true;
    return Symmetrical(root->lchild, root->rchild);    
}
bool Symmetrical(Node* root1, Node* root2)
{
    if(root1 == NULL && root2 == NULL)
        return true;
    if(root1 != NULL && root2 != NULL)
        return root1->data == root2->data && Symmetrical(root1->lchild, root2->rchild) && Symmetrical(root1->rchild, root2->lchild);
    return false;
}

16.序列化和反序列化二叉树

//将二叉树虚拟化为字符串,每个节点值用'!'分割,'#'代表空节点
//         1
//       /   \
//      2     3
//    /   \   
//   4     5
//序列化为:1!2!4!##5!##3!##
char* Serialize(TreeNode *root)//序列化二叉树(使用前序遍历)
{
    string s;
    pre_order(root, s);
    char *res = new char[s.size() + 1];
    memset(res, 0, s.size()+1);
    strcpy(res, s.c_str());
    return res;
}
TreeNode* Deserialize(char *str) {
    if(*str == '#')
        return NULL;
    int pos = 0;
    return build_tree(str, pos);
}
void pre_order(TreeNode *root, string& s)
{
    if(root == NULL)
    {
        s += "#";
        return ;
    }
    else
        s = s + to_string(root->val) + "!";
     pre_order(root->left,  s);
     pre_order(root->right, s);
}
TreeNode* build_tree(char *str, int &pos)
{
    if(str[pos] == '#')
    {
        pos++;
        return NULL;
    }
    int x = 0;
    for(; str[pos] != '\0' && str[pos] != '!'; pos++)
        x = x * 10 + str[pos] - '0';

    TreeNode *p = new TreeNode(x);
    if(str[pos] == '\0')
        return p;
    else
        pos++;
    p->left = build_tree(str, pos);
    p->right = build_tree(str, pos);

    return p;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值