GO语言实现二叉树基本功能

用GO语言和用C语言写的思路没有区别,都是利用递归来解决遍历难的问题。这里值得注意的是,在GO中用switch-case是完全可以代替if-else的,个人觉得在做多项判断时用switch-case来写不仅效率更高,思路也更清晰,也使得代码简洁。

前置各种参数说明:

package main
import "fmt"
const MAXSIZE=1000
const OK=1
const ERROR=0

type TElemType rune
type Status int

 下面是二叉树结点的存储:

type BiNode struct{
    data TElemType;
    Lchild *BiNode;
    Rchild *BiNode;
}
type BiTree = *BiNode;

        1.初始化二叉树

        即建立一个空二叉树,空间为一个结点大小

func InitBiTree(T *BiTree) Status {
    *T = new(BiNode)
    fmt.Println("分配地址成功")
    (*T).Lchild=nil
    (*T).Rchild=nil
    (*T).data=0
    return OK
}//初始化二叉树(给第一个节点分配空间)

     2.创建二叉树.

        对于创建二叉树来说,例如上图所示的二叉树,首先要自己对其进行前序遍历,值得注意的是:例如c结点的左孩子为空,我们用'#'来表示空结点。d,e,f结点的孩子也是这么表示。如此看来,这棵树的表示方法为 'abc#d##e##f##'。基于这个思路,创建二叉树的算法可以表示为:

func CreateBiTree(T *BiTree) {
    var ch TElemType
    fmt.Scanf("%c",&ch)
    switch{
        case ch=='#':
            (*T)=nil    //当输入#时判定为空结点
        default:        //其他情况分配空间,并且将ch赋给结点的data域
            *T = new(BiNode)
            (*T).data=ch
            CreateBiTree(&((*T).Lchild))
            CreateBiTree(&((*T).Rchild))
    }
}

 3.前序、中序、后续遍历二叉树.

        在一对一的线性表中,只需要找到每个元素的后继结点就可以成功遍历出全部元素,环环相扣纵享丝滑。而二叉树的存储特点为数据是一对多(一对二)存储方式,用这种思路去遍历显然是行不通的,如果说有那么一种方法可以既有条理地去完成遍历,又能使代码简洁。那递归方法再合适不过了。

        前序遍历方法思路就是从树根开始,先打印根结点的值,然后去找左子树。注意,这时左子树可以看作单独的一棵树,左子树的树根就是整颗树的左孩子。例如刚才的二叉树,树根为a,当打印出a的值后,以同样的方式访问a的左子树b,这时在访问以b为根结点的树,以此类推,直到当访问的树为空树时(比如访问到c的左子树),开始如此遍历右子树。

中序遍历即为先访问左子树,再访问根结点,最后访问右子树。同理后序遍历最后访问根结点 。

func PreorderTree(T *BiTree){
    switch{
        case *T==nil:
            return;
        default:
            fmt.Printf("%c,",(*T).data)
            PreorderTree(&(*T).Lchild)
            PreorderTree(&(*T).Rchild)
    }
}

func InorderTree(T *BiTree){
    switch{
        case *T==nil:
            return;
        default:
            InorderTree(&(*T).Lchild)
            fmt.Printf("%c,",(*T).data)
            InorderTree(&(*T).Rchild)
    }
}

4.求结点个数.

与遍历思路大同小异:前序遍历二叉树,当访问到空结点时:函数返回0,其余情况每访问一个结点,返回数+1。当二叉树完全遍历完成时,返回值就是二叉树结点的个数。

func CountNodes(T *BiTree) int{
    switch{
        case *T==nil:
            return 0;
        default:
            return 1+CountNodes(&(*T).Lchild)+CountNodes(&(*T).Rchild) ;
    }
}

5.求二叉树的深度.这个算法稍微麻烦一些,我们以函数本身的视角来审视这个算法:当函数接受(被输入)一个结点时,先判断是否为空,若结点为空结点,则返回0。若该结点不为空,则递归计算该节点的左子树深度,返回值赋给m。递归计算右子树深度,返回值赋给n。当m>n时,返回m+1。否则返回n+1。整棵树遍历完成后,返回值就是树的深度。

func Deep(T *BiTree) int{
    var m int;
    var n int;
    switch{
        case (*T)==nil:
            return 0;
        default:
            m=Deep(&(*T).Lchild);
            n=Deep(&(*T).Rchild);
            switch{
                case m>n:
                    return m+1;
                default:
                    return n+1;
            }
    }
}

6.复制树.

方法很简单,以前序遍历为基础,遇到空结点则将新树的该结点置空,否则照抄旧树。遍历完成时,复制新树完成。

func copy(T *BiTree,Q *BiTree){
    switch{
        case *T==nil:
            *Q=nil;
        default:
            *Q=new(BiNode);
            (*Q).data=(*T).data;
            (*Q).Lchild=(*T).Lchild;
            (*Q).Rchild=(*T).Rchild;
            copy(&(*T).Lchild,&(*Q).Lchild);
            copy(&(*T).Rchild,&(*Q).Rchild);
    }
}

7.测试.

        下面给出main函数以及测试结果,由于本人设备不在身边,现在一台平板,只能用模拟环境测试,结果与pc端是一样的,后续会把pc端的结果更新。

func main(){
    var T BiTree;
    var Q BiTree;
    InitBiTree(&T);
    CreateBiTree(&T);
    fmt.Printf("前序遍历:");
    PreorderTree(&T);
    fmt.Println("");
    fmt.Printf("中序遍历:");
    InorderTree(&T);
    fmt.Println("");
    fmt.Printf("节点的个数为:%d\n",CountNodes(&T));
    n:=Deep(&T);
    fmt.Printf("二叉树的深度为:%d\n",n);
    copy(&T,&Q);
    fmt.Printf("前序遍历复制后的新树:\n");
    PreorderTree(&Q);
}

写在最后:目前本人还在学习阶段,这是第一次发文档,难免有不妥当不正确的地方,希望各位不吝赐教。如果这篇文章有一天能帮助到可能正在学习数据结构或者学习golang的你,我一定会很开心的。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值